25 мая 2017 г.

Port Forwarding. Доступ к сайту через VPN.

Что такое Port Forwarding (Port mapping)?


Если у вас дома для раздачи интернета используется Router и вы сами делали его настройку, значит наверняка в настройках видели пункт меню Port Forwarding (Port Mapping). Port Forwarding - это технология позволяет минуя NAT настроить доступ из интернета к конкретному компьютеру, находящемуся у вас дома и подключенному к Router. Чаще всего в настройках указываются две вещи:
  1. Имя или IP домашнего компьютера и порт на который будет идти переадресация из вне.
  2. Порт, который будет отрыт на Router со стороны интернета.
В результате получается поведение как на рисунке:


20.1.1.1 - IP адрес вас в интернете выданный интернет провайдером. Если из интернета подключаются к 80 порту на Router, то происходит перенаправление трафика на стационарный компьютер, а при обращении к 8080 на ноутбук.

Как это поможет разработчику?


Представьте, что у вас есть задача интеграции с какой то системой в сети заказчика. Вам дают VPN и тестовый компьютер с которого эта система доступна, при этом у пользователей VPN напрямую доступа к системе нет. Через RDP вы подключаетесь к компьютеру и понимаете, что для того, что бы начать разработку вам требуется устанавливать Visual Studio.

Что же можно в этом случае сделать?

Мы можем в качестве Router использовать компьютер до которого есть RDP доступ. На нём запустить программу, которая будет перенаправлять входящий трафик в систему заказчика, что даст нам возможность локально работать над интеграцией.

Сложно? Нет - проще паренной репы! А как это сделать объясню дальше.

Настройка Port Forwarding.


Первый шаг. Узнаем IP или Hostname системы (что заказчик конечно же скажет) с которой нам надо интегрироваться. Далее нам необходимо узнать порт, через который планируется взаимодействовать с системой. Если у вас возникает проблема с определением порта,  попробую привести типовые ситуации и объяснить как я узнал в них порт.
  • Если у вас взаимодействие с каким то вэб сервисом и заказчик дал URL формата http://host/service это значит, что вэб сервис работает по HTTP протоколу и порт по умолчанию 80.
  • Если адрес https://host/service значит с системой придётся взаимодействовать по HTTPS порту и его значение по умолчанию 443.
  • Если в адресе URL после host стоит двоеточие число: http(s)://host:8001/service значит используется нестандартный порт для HTTP(S) протокола и нам потребуется просто в явном виде указать его, в моём случае это 8001.
  • Такое же перенаправление можно настроить для чего угодно, даже для SQL сервера, только в этом случае придётся поискать нужный порт в интернете.
Второй шаг. Скачиваем самую маленькую и удобную утилиту для создания переадресаций с возможность их сохранять. Ссылка блога автора http://www.quantumg.net/portforward.php если ссылка на исполняемый файл не доступна, то можно скачать отсюда: [Download-Port-Forward] использую уже лет 5 и не жалуюсь. Есть масса аналогов, платных, бесплатных работающих как сервисы или же есть варианты штатными средствами без стороннего софта поднять перенаправление. Мне же по душе самому выбирать когда и что я хочу начать перенаправлять, при этом, должна быть возможность легко выключить это. Данная утилита лёгкая (44 кб) и без лишних кнопок делает то, что сказано. Рекомендую.

Для демонстрации я опишу пример:
  • Имя моего локального компьютера sapcomputer и это будет тем компьютером с RDP.
  • В качестве внешней системы я хочу взять доступ к сайту https://worldofwarcraft.com, к которому нет доступа с других компьютеров.
  • Рабочим компьютером к сожалению будет выступать мой же компьютер, я просто подключусь к локальному порту и должен оказаться на сайте.
Приступим! Запускаем утилиту, жмём Redirection >  Add в поле Port указываем локальный порт который будет отрыт для подключения клиентов. Можно указать такой же как и для Destination, но у меня локально 443 занят, вы в праве использовать любой свободный. Далее в поле с адресом указываем worldofwarcraft.com а порт 443 потому, что HTTPS://.



Жмём OK и можно проверять! Открываем браузер вводим https://sapcomputer:9999/ и вуаля, мы видим сайт:


Важно! Если вы перенаправляете на https порт 443 то и при обращении к локальному сайту надо обязательно использовать префикс https, аналогично с http. Иначе вы не сможете увидеть содержимое страницы.
Теперь с рабочего компьютера можно нацеливать разрабатываемый модуль на sapcomputer:9999 и работать, как если бы вы работали на sapcomputer. Просто не так ли?

23 мая 2017 г.

Инструменты отладки: System.Diagnostics, Output window, DebuggerDisplay, Edit and continue, Immediate Window и т.д

В данной статье я опишу способы, как упростить себе процесс создания приложения. Перечислю инструменты, которыми активно пользуюсь и опишу ситуации в которых они мне пригодились. Приступим!

Пространство имен System.Diagnostics 


Если говорить о инструментарии, который уже имеется в составе .NET Framework, тогда речь пойдут о классах лежащих в пространстве имен System.Diagnostics.* . Из всего многообразия классов я выделю только те, которые помогут нам в разработке.

Класс взаимодействия с отладчиком (Debugger).


Самый часто используемый мной класс это Debugger, дающий возможность взаимодействовать с отладчиком студии и постараюсь описать ситуации использования отдельных методов этого класса.

Метод Debugger.Launch() 


Launch
работает так по правилам:
  • Если вы находитесь в режиме отладки, то при вызове этого метода ничего происходить не будет
  • Если запустить ваше приложение вне студии, то при вызове этого метода появится диалог с предложением использовать один из предложенных в списке отладчиков. При этом продолжить работу без использования отладчика невозможно.

Простенький пример консольного приложения:

using System.Diagnostics;
 
namespace DebugFeatures
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Debugger.Launch();
        }
    }
}
 
Запустив по F5 в студии вы ничего не заметите, но запусти в проводнике увидите диалог:

В котором можете выбрать либо уже запущенную студию (у меня это 1-я строка), либо использовать новый экземпляр студии по вашему выбору.
Внимание! Если "New instance Microsoft ....", тогда весь код проекта будет не доступен. Поэтому желательно выбирать студию с отрытым вашим проектом.
Теперь по поводу жизненных ситуаций, когда этот метод пригодится:
  • При отладке двух процессов, когда ваше приложение запускает второе приложение, которое быстро "что то делает" и закрывается. При этом вы не успеваете сделать Debug -> Attach to process.
  • Допустим вы являетесь разработчиком какого то расширения или add-on для какой то системы. При запуске система подгружаете ваш модуль в память и передает ему управление. Встает вопрос, как же теперь отлаживать ситуацию в момент запуска модуля? В этом случае добавляем в код метода Debugger.Launch() и в момент запуска получаем возможность отладить процесс запуска.
    P. S. Если вы пользуетесь Thread.Sleep(15000) + Attach to process, то мои вам соболезнования :)
Важно! Стоит помнить что даже в релизной сборке будет отображаться окно с отладчиками, поэтому не забывайте удалять Debugger.Launch(), когда надобность в нем отпадает.
 

Метод Debugger.Break()


Break работает по правилам:
  • Если отладчик подключен, то происходит остановка при вызове методе, сравнимая остановке на breakpoint.
  • Если же отладчик не подключен и вызывается метод Break(), тогда будет показано окно с выбором отладчика, но при этом закрыв его приложение продолжит работу.

Пример кода:

using System.Diagnostics;
 
namespace DebugFeatures
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            int a = 1;
            while (a < 10 * 1000 * 1000)
            {
                a ++;
                if (a == 9*999* 999)
                {
                    Debugger.Break();
                }
            }
        }
    }
} 
 
На практике использовал этот метод довольна редко, но было дело, что студия "глючила" и breakpoint слетали после перезапуска. Выходом был этот метод.

 Output window и класс Debug.


Мне кажется, что люди не дооценивают окно Output внутри Visual Studio убирая его с рабочего пространства, а ведь это просто идеальная консоль для вывода событий происходящих в приложении и просто спасительный круг, если вы отлаживаете сложное многопоточное приложение. Либо пытаясь распутать какие то ситуации, где требуется анализ параметров и локальных переменных, что бы понять причину неисправности. Сейчас я постараюсь продемонстрировать как использовать Output, но при этом не загромождать им место.
Наблюдение показывает, что у всех программистов, которых я видел одна из ситуаций ниже:
  • Окно Output не присутствует на видимом рабочем пространстве. А находится где то снизу на вкладках, при этом программист может иногда поглядывать туда.
  • Окно Output всегда видно, но имеет настолько малый размер, что в нем ничего не понятно. При сборке что то мелькает, а на вопрос "Зачем тебе оно если ничего не понятно?" получал ответ "Что бы знать, что студия не подвисла и было на что поглядеть". 
  • Последний вариант, программист активно пользуется им и читает, что пишется в окне. Но при этом в окне два scroll умещается 4-6 строчек, а при работе приложение внутри мелькает текст и бедняга программист пытается найти нужное место.
Если вы из пункта 3 тогда хотел бы предложить мой способ размещения окна, которым я пользуюсь и перенастраиваю везде уже так в течении 5 лет. На всё про всё уходит 5-10 минут, но бывают и такие которые годами будут пользоваться неудобным интерфейсом или способом, при этом зная, что правится проблема в считанные минуты, но человеку "Лениво разбираться" :)

17 мая 2017 г.

MiniDumpWriteDump. Создаём minidump на C#.

Что такое minidump?

Minidump - это, в зависимости от запроса, полный или частичный "слепок" оперативной памяти работающего процесса с сохранением полного или частичного состояния на момент получения снимка.
Как это можно поможет в работе? Дело в том, что minidump можно создавать самому, а начиная с Framework 4.0 - Visual Studio при открытии minidump файла может показать точку останова, стек вызова и даже локальные процессы. Если конечно их не убрал /Release оптимизатор. Иными словами мы увидим такую же картину, как если бы мы работающее приложение поставили на паузу и решили посмотреть стек.
Заинтересовало не так ли? Тогда приступим!

Способы создания.

Известные мне способы создания дампа:
  1. Создание дампа внутри вашего приложения вызывая метод MiniDumpWriteDump из dbghelp.dll.
  2. Используя какие то внешние (out of process) утилиты:
    Стандартный диспетчер задач, Process Explorer или Process Hacker и т.д

Создание Minidump внутри приложения


Для создания дампа внутри нашего приложения нам необходимо всего лишь вызвать функцию MiniDumpWriteDump из библиотеки dbghelp.dll, которая есть у всех, или почти всех, версий Windows. Из отличий версий могут быть только флаги параметра, которые добавлялись в ходе развития библиотеки.
Зачем создавать дамп из тела приложения? Создание таким образом дает возможность контролировать место останова, для дальнейшего анализа дампа. Дампы созданные из диспетчера задач не могу гарантировать, что при открытии в студию вы попадете в "полезное" место и в котором будет все, что требуется для решении вашей задачи или проблемы.

Я создал простенькое консольное приложение для демонстрации работы:
  1. Код  метода Main():
    namespace Dumper
    {
        using System;
        using System.IO;
        using System.Linq;
        using System.Runtime.CompilerServices;
        using System.Threading;
        using Dumps;
        
        internal static class Program
        {
            private static bool _stopThread;
     
            /// <summary>
            /// Entry point.
            /// </summary>
            static void Main(string[] args)
            {
                Directory.GetFiles(DumpUtils.DumpDirectory).ToList().ForEach(File.Delete);
                CreateThread();
                TestMethod();
                _stopThread = true;
            }
     
            private static void CreateThread()
            {
                new Thread(() =>
                {
                    while (!_stopThread)
                    {
                        Console.WriteLine("while(true)");
                    }
                })
                {
                    Name = "While(true) thread"
                }.Start();
            }
     
            [MethodImpl(MethodImplOptions.NoOptimization)]
            private static void TestMethod()
            {
                var dateTime = DateTime.Now;
                try
                {
                    Console.WriteLine(dateTime);
     
                    int someDouble = 0;
                    someDouble = 10 / someDouble;
                }
                catch (Exception)
                {
                    DumpUtils.WriteDump();
                }
            }
        }
    }
    
  2. Код класса создающий Minidump файл:
    namespace Dumper.Dumps
    {
        using System;
        using System.Diagnostics;
        using System.IO;
        using System.Reflection;
        using System.Runtime.CompilerServices;
        using System.Runtime.InteropServices;
     
        /// <summary>
        /// Minidump support tools.
        /// </summary>
        public static class DumpUtils
        {
            /// <summary>
            /// Folder for saved minidumps.
            /// </summary>
            public const string DumpDirectory = "Minidump";
     
            [DllImport("dbghelp.dll")]
            private static extern bool MiniDumpWriteDump(IntPtr hProcess, int processId, IntPtr hFile, int dumpType,
                IntPtr exceptionParam, IntPtr userStreamParam, IntPtr callStackParam);
     
            /// <summary>
            /// Write minidump to file.
            /// </summary>
            /// <param name="minidumpType">Minidump flag(s).</param>
            [MethodImpl(MethodImplOptions.NoOptimization)]
            public static bool WriteDump(
                MinidumpType minidumpType = MinidumpType.MiniDumpWithFullMemory |
                MinidumpType.MiniDumpWithHandleData |
                MinidumpType.MiniDumpWithUnloadedModules |
                MinidumpType.MiniDumpWithFullMemoryInfo |
                MinidumpType.MiniDumpWithThreadInfo)
            {
                try
                {
                    if (!Directory.Exists(DumpDirectory))
                    {
                        Directory.CreateDirectory(DumpDirectory);
                    }
     
                    var currentProcess = Process.GetCurrentProcess();
                    var fileName = GetNewDumpFileName(currentProcess.ProcessName);
                    var currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                    var filePath = Path.Combine(currentDir, DumpDirectory, fileName);
                    var handler = currentProcess.Handle;
                    var processId = currentProcess.Id;
     
                    using (var fileStream = new FileStream(filePath, FileMode.CreateNew))
                    {
                        return MiniDumpWriteDump(
                            handler,
                            processId,
                            fileStream.SafeFileHandle.DangerousGetHandle(),
                            (int) minidumpType,
                            IntPtr.Zero,
                            IntPtr.Zero,
                            IntPtr.Zero);
                    }
                }
                catch (Exception)
                {
                    return false;
                }
            }
     
            private static string GetNewDumpFileName(string processName)
            {
                return string.Format("{0}_{1}_{2}.dmp", processName,
                    DateTime.Now.ToString("yyyy-dd-mm_HH-mm-ss"),
                    Path.GetRandomFileName().Replace("."""));
            }
        }
    }

2 мая 2017 г.

Выполнение bytecode или shellcode через VirtualAlloc и CreateThread.

Введение

Сейчас хотел бы показать интересный пример запуска нативного байт кода в управляемой куче на языке C#. Для демонстрации будут использованы примеры примитивных shellcode взятых с сайта https://www.exploit-db.com/. На данном ресурсе публикуются боевые exploit для известного программного обеспечения, а сам сайт является аналогом закрытого milw0rm. Перейдя в раздел https://www.exploit-db.com/shellcode/ можно найти огромное количество shellcode для различных операционных систем, и при исполнении делающие от простого открытия диалогового окна до запуска backshell для удаленного доступа к машине. Для того, что бы последовательность байт произвела заложенные в нее действия, ее требуется поместить в оперативную память и передать ей управление, иными словами какой то поток должен сделать CALL по адресу памяти. На языках С\С++ вызов делается очень просто, указатель на блок в памяти преобразуется в указатель на метод, чаще всего void(), и делается его вызов ((void (*)())shellcode)(). Если мы те же действия хотим произвести в управляемой куче, то нам запрещено работать с указателями, но при этом никто не запрещает используя WinAPI и Marshaling заставить систему выполнить то, что мы хотим.

Как это будет работать?

Всё легко и просто, для этого нам понадобится всего две WinAPI функции.

  • VirtualAlloc - используя которую, мы разместим байт код в неуправляемой части памяти нашего приложения.
  • CreateThread - которая создаст поток исполнения, у которого в качестве "Точки входа" (стартового адреса исполнения) будет наш размещенный блок с байт кодом.

Практика.

В примере ниже будет продемонстрировано два метода, 
  • RunCmd запускает cmd.exe внутри текущего консольного приложения.
  • RunMessageBox - создает обычный MessageBox с текстом. 

Далее привожу пример моего приложения. ByteCodeExecutor - класс, который непосредственно располагает байты в памяти и создает поток

    internal static class Program
    {
        /// <summary>
        /// Entry point.
        /// </summary>
        /// <param name="args">Arguments.</param>
        private static void Main(string[] args)
        {
            if (IntPtr.Size != 4)
            {
                throw new NotSupportedException("Examples required x86 platform");
            }
 
            RunCmd();
 
            RunMessageBox();
 
            Console.ReadKey();
        }
 
        private static void RunCmd()
        {
            Console.WriteLine("Console application becomes command line");
 
            // https://www.exploit-db.com/exploits/14052/
            var bytes = new []
            {
                0xFC, 0x33, 0xD2, 0xB2, 0x30, 0x64, 0xFF, 0x32, 0x5A, 0x8B,
                0x52, 0x0C, 0x8B, 0x52, 0x14, 0x8B, 0x72, 0x28, 0x33, 0xC9,
                0xB1, 0x18, 0x33, 0xFF, 0x33, 0xC0, 0xAC, 0x3C, 0x61, 0x7C,
                0x02, 0x2C, 0x20, 0xC1, 0xCF, 0x0D, 0x03, 0xF8, 0xE2, 0xF0,
                0x81, 0xFF, 0x5B, 0xBC, 0x4A, 0x6A, 0x8B, 0x5A, 0x10, 0x8B,
                0x12, 0x75, 0xDA, 0x8B, 0x53, 0x3C, 0x03, 0xD3, 0xFF, 0x72,
                0x34, 0x8B, 0x52, 0x78, 0x03, 0xD3, 0x8B, 0x72, 0x20, 0x03,
                0xF3, 0x33, 0xC9, 0x41, 0xAD, 0x03, 0xC3, 0x81, 0x38, 0x47,
                0x65, 0x74, 0x50, 0x75, 0xF4, 0x81, 0x78, 0x04, 0x72, 0x6F,
                0x63, 0x41, 0x75, 0xEB, 0x81, 0x78, 0x08, 0x64, 0x64, 0x72,
                0x65, 0x75, 0xE2, 0x49, 0x8B, 0x72, 0x24, 0x03, 0xF3, 0x66,
                0x8B, 0x0C, 0x4E, 0x8B, 0x72, 0x1C, 0x03, 0xF3, 0x8B, 0x14,
                0x8E, 0x03, 0xD3, 0x52, 0x68, 0x78, 0x65, 0x63, 0x01, 0xFE,
                0x4C, 0x24, 0x03, 0x68, 0x57, 0x69, 0x6E, 0x45, 0x54, 0x53,
                0xFF, 0xD2, 0x68, 0x63, 0x6D, 0x64, 0x01, 0xFE, 0x4C, 0x24,
                0x03, 0x6A, 0x05, 0x33, 0xC9, 0x8D, 0x4C, 0x24, 0x04, 0x51,
                0xFF, 0xD0, 0x68, 0x65, 0x73, 0x73, 0x01, 0x8B, 0xDF, 0xFE,
                0x4C, 0x24, 0x03, 0x68, 0x50, 0x72, 0x6F, 0x63, 0x68, 0x45,
                0x78, 0x69, 0x74, 0x54, 0xFF, 0x74, 0x24, 0x20, 0xFF, 0x54,
                0x24, 0x20, 0x57, 0xFF, 0xD0
            };
 
            ByteCodeExecutor.Execute(bytes);
        }