Пишем Ida Плагин дампа памяти — Ida Dump
ida dump Memory — Предыстория
Всем привет! Как-то одной прекрасной ночью я взялся за реверс crackme «телефон». Если мега кратко, то он генерит два массива в 256 байт каждый. В них, по определённым смещениям, расставлены конкретные байты. Я восстановил алгоритм работы данного crackme (по крайней мере я так думал). Идея в том, чтобы два массива были одинаковыми. Проверял это я таким образом: смотрю в студии дамп, смотрю в Ida на дамп, вроде похоже, значит всё ок. А оказалось, что массивы были разные. Трудно углядеть различия в двух массивах, тем более, если разница в конце. Для более правильного сравнения существуют hex компараторы, либо можно просто снять md5-ки двух массивов и сверить их. Но, для этого нужно сдампить память. Как это сделать из студии понятно, нужно просто заюзать WriteFile, а как это сделать из Ida я не знал. Оказалось, что есть три способа, которые я опишу в порядке возрастания юзабилитности.
Сдампить память — 1 способ (тупой)

Неудобный способ
Юзаем возможности Ida: жмём G в окне дампа памяти, выделяем нужное количество байт, далее, в контекстном меню тыкаем Save to File… и сохраняем. Данный способ не подходит в том случае, если нам нужно сохранить кучу байт, так как приходится скроллить память, а в иде это не удобно делать. Поэтому давайте вспомним, что ида поддерживает свой скриптовый язык — IDC и перейдём ко второму способу.
Сдампить память — 2 способ (не очень тупой)
Итак, жмём shift+F2, вылезет окно, куда надо накодить скрипт:

Idaс скрипт дампа памяти
Вбиваем скрипт такого содержания:
auto file, fname, i, address, size, x; address = 0x000033F384; size = 0x100; fname = "C:\\My Programs\\KAV_KEYGEN\\Debug\\b2.bin"; file = fopen(fname, "wb"); for (i=0; i<size; i++, address++) { x = DbgByte(address); fputc(x, file); } fclose(file);
Всё работает, всё дампится, но смущает одно — каждый раз приходится править код, вшивать адрес, имя файла и тд. Короче, это не удобно для многоразового использования и, поэтому, мы перейдём к последнему способу.
Сдампить память — 3 способ (удобный)
Помимо всего прочего, ида ещё имеет поддержку плагинов. Почему-то плагин дампинга памяти я не нашёл в инете (скорее всего плохо искал). Но, для меня (и, думаю, для вас тоже) интереснее будет написать свой плагин, чем полдня гуглить его в яндексе. Для написания плагинов ида имеет свой SDK. Ниже я попытаюсь вкратце объяснить как пишется плагин в иде. Вообще, лучше Ida Pro Book, никто не знает как лучше закодить эти штуки, так что в любом случае, придётся там чего-нибудь да выискивать. Итак, из документации следует, что плагин должен экспортировать объект класса plugin_t с именем PLUGIN. Объект инициализируется различными параметрами, такими, как имя плагина, комментарий, его хоткей, указатели на функции инициализации, запуска и завершения. Вот как он выглядит:
plugin_t PLUGIN = { IDP_INTERFACE_VERSION, // IDA version plug-in is written for PLUGIN_UNL, // Flags (see below) IDAP_init, // Initialisation function IDAP_term, // IDAP_term: Clean-up function IDAP_run, // Main plug-in body IDAP_comment, // Comment unused IDAP_help, // As above unused IDAP_name, // Plug-in name shown inbb IDAP_hotkey // Hot key to run the plug-in };
Второе поле — флаг, означает то, что плагин, после того как отработает, сразу выгружается. Если на это поле тыкнуть F12, то студия покажет вам остальные его поля, которые к тому же комментируются. Как вы видите, начиная с 3 поля в объект передаются указатели функций. Вместо функции завершения плагина допустимо выставить NULL. Функция инициализации IDAP_init должна вернуть значение PLUGIN_OK. Тут можно проверять различные штуки и, если что, вернуть ошибку, чтобы плагин не загрузился. Самая основная функция — IDAP_run. В ней мы реализуем основной функционал. Прежде чем это сделать, давайте решим, что мы хотим. А хотим мы следующее: формочку, куда забьём либо диапазон адресов (начальный — конечный), либо начальный адрес памяти и количество байт, которые мы хотим сдампить. В процессе работы мы будем использовать следующие функции из IDA SDK:
segment_t* segment_t* getseg(ea_t ea)(ea_t ea) | Получает указатель на сегмент по адресу. Можно вытянуть оттуда его начальный и конечные адреса загрузки сегмента |
int AskUsingForm_c (const char * form, … ) | Универсальная функция, позваляющая создать свой кастомный интерфейс |
isLoaded (ea_t ea) | Проверяет валидность адреса ea |
int askyn_c(int deflt, const char* format, … ) | Отобразить диалоговое окно с нужными кнопками (Yes, No, Cansle) |
idaman bool ida_export get_many_bytes(ea_t ea, void * buf, ssize_t size) | Копирует байты по адресу ea в buf, размером size |
Отдельное внимание заслуживает функция AskUsingForm_c, так как она немного не понятная, на мой взгляд. В качестве параметров она принимает строку, в которой описаны все элементы плагина на форме (check box, radio button, edit) и, к тому же, обработчики на кнопки. Вот тут подробнее описано всё как надо делать :-).
Компиляция плагина в VS
Чтобы скомпилировать плагин, создайте пустой проект обычной dll-ки. Сразу выпиливайте от туда всякие stdafx или targetver. Затем нужно выставить следующие значения:
Configuration Properties=>C/C++:
General=>Additional Include Directories: | Прописываем путь к папке с инклудами ida sdk. Пример: C:\idasdk69\include;%(AdditionalIncludeDirectories) |
Preprocessor=>Preprocessor Definitions: | Дописываем __NT__;__IDP__; Пример: WIN32; __NT__;__IDP__;<different options> |
Configuration Properties=>Linker:
General=>Code Generation=>Runtime Library: | Multi-threaded (/MT) |
General=>OutPutFile: | $(OutDir)$(TargetName).plw |
General=>Additional Library Directories: | Дописываем путь к либам ida Sdk. Пример: C:\idasdk69\lib\x86_win_vc_32;%(AdditionalLibraryDirectories) |
Input=>Additional Dependencies: | Дописываем путь к либам ida Sdk. Пример:kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib; advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib; odbccp32.lib;C:\idasdk69\lib\x86_win_vc_32\ida.lib;%(AdditionalDependencies) |
Чтобы сто раз такое не настраивать, рекомендую создать шаблон. Для этого, после настроек, тыкните на File->Export Template. Когда вы заходите написать плагин в следующий раз, то просто выберите в названиях проекта этот шаблон.
Вот, что у нас получилось:
#include<Windows.h> #include<ida.hpp> #include<idp.hpp> #include<loader.hpp> #include<diskio.hpp> charIDAP_name[]="Dumper"; charIDAP_comment[]="Dumper"; charIDAP_help[]="Dumper"; charIDAP_hotkey[]="Ctrl-Alt-D"; charfilepath[MAXSTR]; char*dialog= "STARTITEM0\n" "DumpData\n\n" "PleaseInputAddr\n" "<#StartAddress0x#StartAddr:M:9:16::>\n" "<#EndAddress0x#EndAddr/Len:M:9:16::>\n" "<##Option##EndAddr:R>\n" "<Len:R>>\n"; int __stdcall IDAP_init(void) { returnPLUGIN_OK; } void __stdcall IDAP_term(void) { return; } void __stdcall IDAP_run(int arg) { char*lpSavePath; charszFileName[256]={0}; segment_t*curseg; USHORTnRadio=0; uval_tnStartAddres=get_screen_ea(); uval_tnEndAddres=nStartAddres+0x100; FILE*handle; intsize=0; if(curseg=getseg(nStartAddres)) nEndAddres=curseg->endEA; while(!isLoaded(nEndAddres-1)) nEndAddres-=0x100; if(AskUsingForm_c(dialog,&nStartAddres,&nEndAddres,&nRadio)==1) { if(nRadio) nEndAddres+=nStartAddres; msg("==============Dump==============\n"); msg("StartAddres:0x%08XEndAddres:0x%08X\n",nStartAddres,nEndAddres); if(isLoaded(nStartAddres)&&isLoaded(nEndAddres-1)) { size=nEndAddres-nStartAddres; if(size>0) { qprintf(szFileName,"%08X-%08X.Dump",nStartAddres,nEndAddres); lpSavePath=askfile2_c(1,szFileName,"*.bin;*.dmp","SaveDumpAs"); if(lpSavePath) { if(qfileexist(lpSavePath)) { if(askyn_c(1,"%s alreadyexists!Overwriteit?",lpSavePath)<;=0) { warning("Dumphasbeencancelled!\n"); return; } } handle=fopenWB(lpSavePath); if(handle==NULL) { warning("ErrorofopenFile!\n"); return; } uchar*mem=(uchar*)malloc(size+1); get_many_bytes(nStartAddres,mem,size); ewrite(handle,mem,size); eclose(handle); free(mem); msg("SavePath:%s\n",lpSavePath); msg("==============Dumpsuccess!==============\n"); } } else { warning("Error!Size<;0!\n"); } } else { if(!isLoaded(nStartAddres)) warning("Error!StartAddresdonotexists!\n"); else warning("Error!EndAddresdonotexists!\n"); } } return; } plugin_tPLUGIN= { IDP_INTERFACE_VERSION,//IDAversionplug-iniswrittenfor PLUGIN_UNL,//Flags(seebelow) IDAP_init,//Initialisationfunction IDAP_term,//IDAP_term:Clean-upfunction IDAP_run,//Mainplug-inbody IDAP_comment,//Commentunused IDAP_help,//Asaboveunused IDAP_name,//Plug-innameshowninbb IDAP_hotkey//Hotkeytoruntheplug-in };

Дампим память в Ida
Исходники к статье Ida Dumper
P.S
Прошу прошения за орфографические ошибки, St.Moriz их не фиксит, хотя он и модер :-)))
If you found an error, highlight it and press Shift + Enter or click here to inform us.