Страницы

Поиск по вопросам

Показаны сообщения с ярлыком pe. Показать все сообщения
Показаны сообщения с ярлыком pe. Показать все сообщения

понедельник, 30 марта 2020 г.

Виды отладочной информации в PE файлах

#отладка #pe


Существует несколько форматов отладочной информации. Меня интересует 4 наиболее известных:
CodeView, PDB, FPO, MISC. В сети ответов не нашёл и решил задать вопрос тут.

Верно ли, что PDB - это тот же CodeView только вынесенный в отдельный файл?
Что кроме отладочных символов может содержать файл PDB?

Что такое Frame Pointer Omission?

Поскольку любая отладочная информация в PE описывается структурой IMAGE_DEBUG_DIRECTORY,
что должно находиться в соответствующей директории отладки, если вся информация вынесена
в отдельный файл (в случае использования MISC это вроде DBG, PDB - файл PDB)? Наверное
есть какая то ссылка на этот файл?..

MISC - это какой то конкретный формат отладочных данных или просто обозначает то,
что отладочная информация вынесена в отдельный файл DBG?
    


Ответы

Ответ 1



Типы отладочной информации перечислены документации формата PE, а также в заголовочном файле winnt.h: #define IMAGE_DEBUG_TYPE_UNKNOWN 0 #define IMAGE_DEBUG_TYPE_COFF 1 #define IMAGE_DEBUG_TYPE_CODEVIEW 2 #define IMAGE_DEBUG_TYPE_FPO 3 #define IMAGE_DEBUG_TYPE_MISC 4 #define IMAGE_DEBUG_TYPE_EXCEPTION 5 #define IMAGE_DEBUG_TYPE_FIXUP 6 #define IMAGE_DEBUG_TYPE_OMAP_TO_SRC 7 #define IMAGE_DEBUG_TYPE_OMAP_FROM_SRC 8 #define IMAGE_DEBUG_TYPE_BORLAND 9 #define IMAGE_DEBUG_TYPE_RESERVED10 10 #define IMAGE_DEBUG_TYPE_CLSID 11 #define IMAGE_DEBUG_TYPE_VC_FEATURE 12 #define IMAGE_DEBUG_TYPE_POGO 13 #define IMAGE_DEBUG_TYPE_ILTCG 14 #define IMAGE_DEBUG_TYPE_MPX 15 #define IMAGE_DEBUG_TYPE_REPRO 16 Из всего этого зоопарка наибольшее значение имеют следующие: IMAGE_DEBUG_TYPE_COFF Наиболее старый формат, предполагает встраивание отладочной информации в исполняемый файл. Поддерживает только ограниченный набор отладочной информации (соответствие строк исходников адресам в бинарнике и FPO, если оно применяется для файла). Такой тип информации использовали версии Visual C++ примерно до 2005, современные версии его не используют. IMAGE_DEBUG_TYPE_CODEVIEW Более новый формат, он допускает как встраивание информации в EXE-файл, так и вынесение в отдельный файл (.PDB). Однако в современных версиях Visual C++ отладочная информация всегда вынесена в .PDB, и Debug Directory данного формата содержит только ссылку на внешний PDB-файл. (Отдельного типа записи "PDB" в Debug Directory не существует.) IMAGE_DEBUG_TYPE_FPO Если программа компилируется без оптимизации FPO, ее стек представляет из себя связный список, в котором каждый элемент содержит указатель на следующий, так что отладчик может легко его обходить без дополнительной информации. Если же включена оптимизация FPO, указателя на следующий элемент не будет, поэтому для успешного анализа стека нужен размер данных на стеке у конкретной функции. Эта информация и включается в Debug Directory типа FPO. IMAGE_DEBUG_TYPE_MISC Ссылка на внешний файл .DBG, содержащий отладочную информацию формата COFF и/или CodeView. Файл .DBG аналогичен по структуре PE файлам, его обычно получали с помощью утилиты rebase, входящей в состав старых версий Visual Studio. Сейчас редко используется. IMAGE_DEBUG_TYPE_VC_FEATURE Некоторая служебная информация компилятора (для Visual C++ начиная с версии 2010 при сборке с параметром /GL). Для иллюстрации возьмем программу, собранную Visual C++ 2012 в отладочной конфигурации, и изучим ее Debug Directory с помощью команды dumpbin /headers: Debug Directories Time Type Size RVA Pointer -------- ------- -------- -------- -------- 5BAC500B cv 3E 000167B0 5BB0 Format: RSDS, {A11BDEB7-1F5D-4BFC-8E80-CE407A215DE8}, 39, C:\PROJECTS\CppTest\Debug\CppTest.pdb 5BAC500B feat 10 000167F0 5BF0 Counts: Pre-VC++ 11.00=7, C/C++=27, /GS=27, /sdl=0, guardN=unreported Первая строка - запись формата IMAGE_DEBUG_TYPE_CODEVIEW. RSDS - это сигнатура формата, дальше идет GUID, номер версии файла (для отслеживания изменений) и путь к файлу. Вторая строка - запись формата IMAGE_DEBUG_TYPE_VC_FEATURE. Из надписи sdl=0, например, можно сделать вывод, что параметр /sdl (дополнительные проверки безопасности) был выключен. Источники PE Format Generating debug information with Visual C++ Параметр компоновки /DEBUG Параметры компиляции /Z7, /Zi, /ZI Walking the Stack Without Symbols and With FPO (Frame Pointer Omission) The .dbg Files

понедельник, 9 марта 2020 г.

Узнать относительный адрес внутри проекции PE-файла

#cpp #winapi #pe


Есть один известный крэкми. После его изучения оказывается, что для его взлома достаточно
пропатчить два байта по адресу 40138B (забить условный переход NOP'ами) и два байта
по адресу 401243 (прописать там безусловный переход). Я хочу написать программу, которая
делает это автоматически.

#include 
#include 
#include 
#include 

enum {OK, CANT_OPEN};

int patch (TCHAR* fname)
{
    char first_patch[2]  = {0x90, 0x90};    // NOP NOP
    char second_patch[2] = {0xEB, 0x07};    // JMP 40124C
    HANDLE hFile = CreateFile(fname,
                              FILE_ALL_ACCESS, NULL,
                              NULL, OPEN_EXISTING,
                              FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return CANT_OPEN;

    int SizeFile  = GetFileSize(hFile, NULL);
    HANDLE MhFile = CreateFileMapping(hFile, 0, PAGE_READWRITE, 0, SizeFile, 0);
    HANDLE View   = MapViewOfFile(MhFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);

    UnmapViewOfFile(View);
    CloseHandle(hFile);
    CloseHandle(MhFile);
    return OK;
}

int main (int argc, char *argv[])
{
    TCHAR* default_fname = "CRACKME.EXE";

    if (argc > 1)
    {
        default_fname = argv[1];
    }

    patch(default_fname);

    std::cout << default_fname << std::endl;
    return 0;
}


Я спроецировал исполняемый файл в адресное пространство процесса и получил адрес
этой проекции. Теперь надо пропатчить этот образ, после чего процедура UnmapViewOfFile
сохранит изменения на диск.

Как в этой проекции найти адреса типа 40138B и 401243? Где можно посмотреть теорию
и примеры по редактированию PE-файлов?

Далее пишу хрень, можно не читать.
Успешно напечатал сигнатуру MZ, это значит, что при помощи View можно заполнять структуры
заголовков, используя, возможно, для выравнивания макросы Криса Касперски.

char M = *((char *) View);
char Z = *((char *) View + 1);
std::cout << M << Z << std::endl;


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

unsigned myRVAtoRAW(HANDLE pe_image, unsigned rva)
{
    IMAGE_DOS_HEADER dos_header;
    std::memcpy(reinterpret_cast(&dos_header), pe_image, sizeof(dos_header));

    std::cout << (dos_header.e_magic == 'ZM') << std::endl;
    return 0;
}

    


Ответы

Ответ 1



Можно пойти двумя путями: Разобраться и написать самому. Тогда рекомендую почитать статью: Разработка функций RvaToRaw и RawToRva. Либо воспользоваться функций из известной dbghelp.dll. Я бы посоветовал разобраться, благо это не сложно. Также следует отметить знаменитую статью Об упаковщиках в последний раз: Часть первая - теоретическая

Ответ 2



Что касаемо патчинга: int main() { HWND hWnd = FindWindow(0, "Calculator"); if(hWnd == 0){ MessageBox(0, "Error cannot find window.", "Error", MB_OK|MB_ICONERROR); } else { DWORD proccess_ID; GetWindowThreadProcessId(hWnd, &proccess_ID); HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, proccess_ID); if(!hProcess){ MessageBox(0, "Could not open the process!", "Error!", MB_OK|MB_ICONERROR); } else { DWORD newdatasize = sizeof(newdata); if(WriteProcessMemory(hProcess, (LPVOID)0x40138B , "0x90", newdatasize, NULL)){ MessageBox(NULL, "WriteProcessMemory worked.", "Success", MB_OK + MB_ICONINFORMATION); } else { MessageBox(NULL, "Error cannot WriteProcessMemory!", "Error", MB_OK + MB_ICONERROR); } CloseHandle(hProcess); } } return 0; } Адреса искать по паттерну(уникальной сигнатуре), сделать ее можно в любом отладчике

воскресенье, 8 марта 2020 г.

Виртуальная память процесса в windows, что из неё видно и как меняются адреса?

#windows #память #pe


Вопрос по загрузке PE и распределению в адресов в режиме пользователя.
Насколько я знаю, PE-секции выгружаются в общую для всех пользовательских программ
область, в зависимости от доступности, и адреса задаются при загрузке в память, но
тут вопрос - как происходит адресация внутри программы, раз мы не меняя кода получаем
работоспособную программу и при этом можем через ту же память обращаться в адресное
пространство других процессов? К примеру, у меня в программе по адресу 0x1 лежит mov
ax,bx и когда происходит jmp 0x1 он перекидывает меня именно в мою программу, а не
в чужую, при этом я могу прочитать тот же 0x1 другой программы как?
И как бы мне выцепить user32 и kernel32 без таблицы импорта, но из своего pe-файла?
И чего там ещё интересного можно найти?
    


Ответы

Ответ 1



Насколько я знаю PE-секции выгружаются в общую для всех пользовательских программ область в зависимости от доступности и адреса задаются при загрузке в память Так было до Windows 3.1 включительно, когда виртуальной памяти попросту не существовало, и разделение производилось по отовсюду доступным сегментам. В современных же операционных системах каждый процесс находится в собственном, изолированном адресном пространстве («песочнице»). Согласен, если файл (в том числе исполняемый) отображается в несколько процессов в режиме только на чтение и без каких-либо изменений, то операционная система может (но не обязана) сэкономить немного ОЗУ и отобразить соответствующие страницы виртуальной памяти этих процессов в один и тот же регион памяти физической. Однако это всего лишь трюк на уровне отображения виртуальной памяти на физическую силами железа. С точки зрения самих программ никакого общего региона не существует. к примеру у меня в программе по адресу 0x1 лежит mov ax,bx и когда происходит jmp 0x1 он перекидывает меня именно в мою программу а не в чужую Перед тем, как переключить выполнение на какой-либо поток вашего процесса, операционная система производит определённую донастройку процессора. В частности, она извлекает из своих внутренних структур физический адрес карты отображения памяти и передаёт его специальному блоку процессора, мапперу. Маппер же использует указанную карту примерно следующим образом: (Иллюстрация взята из ответа на вопрос «Какую модель памяти сегментную или страничную использует windows, linux, macos?») при этом я могу прочитать тот же 0x1 другой программы как? Надо попросить операционную систему не выделять пустую страницу, как она это обычно делает, а создать привязку к уже существующей странице. Иными словами, как бы прорубить окно в чужое адресное пространство. Однако отображаемый блок не должен накладываться на уже занятый регион виртуальной памяти вашего процесса. С другой стороны, это окно можно создать в любом месте памяти вашей программы. Для этого необходимо вызвать системную функцию MapViewOfFileEx(), указав целевой процесс и адрес в его виртуальной памяти. И как бы мне выципить user32 и kernel32 без таблицы импорта но из своего pe-файла? LoadLibrary() + GetProcAddress(). Всё остальное — хаки и ненадёжно.

четверг, 11 июля 2019 г.

Можно ли пометить отдельные секции исполняемого образа как незагружаемые в ОЗУ

Например в случае большого SFX-архива, до нескольких гигабайт, логично загрузить только исполняемый код распаковщика, а сам архив оставить на диске. И потом спокойно его распаковывать.
Если да, то как это сделать средствами Visual Studio? Как это вообще делается в случае SFX?


Ответ

Часто делают так. Создается EXE без дополнительных данных, а потом к нему дописывается "архив", при этом параметры секций не меняются. Загрузчик в память загрузит только сам EXE, оверлей не будет загружен, поскольку он не в секции. Запущенный EXE делает, к примеру, GetModuleHandle(NULL), получает адрес своего заголовка в памяти, парсит таблицу секций и узнает файловое смещение конца последней секции. Ровно за ним - оверлей с "архивом", который он может читать как ему угодно. Вариант номер два: после сборки в неиспользуемые поля заголовка вносится размер EXE, и запущеный процесс читает оттуда смещение до оверлея.

понедельник, 27 мая 2019 г.

Виртуальная память процесса в windows, что из неё видно и как меняются адреса?

Вопрос по загрузке PE и распределению в адресов в режиме пользователя. Насколько я знаю, PE-секции выгружаются в общую для всех пользовательских программ область, в зависимости от доступности, и адреса задаются при загрузке в память, но тут вопрос - как происходит адресация внутри программы, раз мы не меняя кода получаем работоспособную программу и при этом можем через ту же память обращаться в адресное пространство других процессов? К примеру, у меня в программе по адресу 0x1 лежит mov ax,bx и когда происходит jmp 0x1 он перекидывает меня именно в мою программу, а не в чужую, при этом я могу прочитать тот же 0x1 другой программы как? И как бы мне выцепить user32 и kernel32 без таблицы импорта, но из своего pe-файла? И чего там ещё интересного можно найти?


Ответ

Насколько я знаю PE-секции выгружаются в общую для всех пользовательских программ область в зависимости от доступности и адреса задаются при загрузке в память
Так было до Windows 3.1 включительно, когда виртуальной памяти попросту не существовало, и разделение производилось по отовсюду доступным сегментам. В современных же операционных системах каждый процесс находится в собственном, изолированном адресном пространстве («песочнице»).
Согласен, если файл (в том числе исполняемый) отображается в несколько процессов в режиме только на чтение и без каких-либо изменений, то операционная система может (но не обязана) сэкономить немного ОЗУ и отобразить соответствующие страницы виртуальной памяти этих процессов в один и тот же регион памяти физической
Однако это всего лишь трюк на уровне отображения виртуальной памяти на физическую силами железа. С точки зрения самих программ никакого общего региона не существует.
к примеру у меня в программе по адресу 0x1 лежит mov ax,bx и когда происходит jmp 0x1 он перекидывает меня именно в мою программу а не в чужую
Перед тем, как переключить выполнение на какой-либо поток вашего процесса, операционная система производит определённую донастройку процессора. В частности, она извлекает из своих внутренних структур физический адрес карты отображения памяти и передаёт его специальному блоку процессора, мапперу
Маппер же использует указанную карту примерно следующим образом:

(Иллюстрация взята из ответа на вопрос «Какую модель памяти сегментную или страничную использует windows, linux, macos?»)
при этом я могу прочитать тот же 0x1 другой программы как?
Надо попросить операционную систему не выделять пустую страницу, как она это обычно делает, а создать привязку к уже существующей странице. Иными словами, как бы прорубить окно в чужое адресное пространство.
Однако отображаемый блок не должен накладываться на уже занятый регион виртуальной памяти вашего процесса. С другой стороны, это окно можно создать в любом месте памяти вашей программы.
Для этого необходимо вызвать системную функцию MapViewOfFileEx(), указав целевой процесс и адрес в его виртуальной памяти.
И как бы мне выципить user32 и kernel32 без таблицы импорта но из своего pe-файла?
LoadLibrary() + GetProcAddress(). Всё остальное — хаки и ненадёжно.