На главную | Содержание | Назад | Вперёд
Наши друзья

 

 

Создание дополнительных модулей для IDA

IDA — это сокращенное название программы Intreactive Disassembler (доступной по адресу www.datarescue.com), которая является одним из самых популярных средств для восстановления исходного кода и структуры программ. IDA является самым мощным и самым развитым интерактивным дизассемблером, доступным на сегодняшний день. Эта программа позволяет подключать дополнительные модули, т.е. пользователи могут самостоятельно расширять ее функциональные возможно­сти и автоматизировать выполнение задач. Для примера в нашей книге мы создали простой дополнительный модуль IDA, который позволяет выполнить сканирование двух двоичных файлов и сравнить их. При этом будут выделены все области кода, которые подверглись изменениям. Этот модуль можно использовать для сравнения исполняемого файла до добавления заплаты с тем же файлом после добавления за­платы для того, чтобы узнать, какие строки кода были исправлены.

Во многих случаях поставщики программного обеспечения "тайно" исправляют ошибки, связанные с безопасностью своих программ. Программа IDA позволяет ха­керу обнаружить эти скрытые заплаты. Хотим предупредить, что наш дополнитель­ный модуль может выделить в коде множество мест, которые не подвергались ника­ким изменениям. Большое количество ложных результатов будет получено при из­менении параметров компилятора или изменении заполнения между функциями. Тем не менее, это неплохой пример того, как научиться писать дополнительные мо­дули для IDA.
Наш пример позволяет также выделить основную проблему с обеспечением безопасности по методу "взлом-создание заплаты". Заплаты порой можно расцени­вать как руководство по взлому, и опытные хакеры знают, как читать эти руково­дства. Для использования приведенного ниже кода потребуется набор инструмен­тальных средств разработки программного обеспечения (SDK), который поставля­ется совместно с IDA. В тексте кода добавлены комментарии. Ниже перечислены стандартные заголовочные файлы. В зависимости от того, какие вызовы API плани­руется использовать, можно добавить и другие заголовочные файлы. Обратите вни­мание, что мы деактивировали некоторые предупреждающие уведомления и доба­вили заголовочный файл Windows. Благодаря этому мы получили возможность ис­пользовать графический интерфейс Windows для вывода отрывающихся диалоговых окон и т.д. Предупреждение 4273 выдается при использовании стан­дартной библиотеки шаблонов, когда эта библиотека отключается по желанию поль­зователя.
#include <windows.h> #pragma warning(  disable:4273 ) tinclude <ida.hpp> #include <idp.hpp> tinclude <bytes.hpp> #include <loader.hpp> #include <kernwin.hpp> tinclude <name.hpp>
Поскольку наш дополнительный модуль основан на дополнительном модуле, ко­торый предоставляется совместно с SDK, то следующий программный код взят про­сто из примера. Все необходимые функции и комментарии тоже являются частью примера.
//  эта функция обратного вызова вызывается для событий //  выдачи уведомлений через интерфейс пользователя.
static int sample_callback(void * /*user data'/,  int event_id, va_list /*va*/)
{
if ( %wht_id !r= ui_msg ) //// Предотвращение рекурсии. if ( <ewent_id И ui_setstate && event_id ! - ui_showauto
&& event_id ! - vui_pefresfe!marked ) It ^игнорируем неинтересные события msg("ui_callback #dkW', evenft_id?;
return 0;   //  0 означает  "обработать событие";
// в противном случае,   событие будет проигнорировано.
}
//-
// Пример того,   как генерировать определенные пользователем
// строковые префиксы
static const int prefix_width = 8;
static void get_user_defined_prefix(ea_t ea,
int lnnum,

int indent, const char *line, char *buf, size_t bufsize)
{
buf[0]   =  '\0';   // По умолчанию пустой префикс
// Мы хотим отображать префикс только для тех строк, // которые содержат инструкции.
if ( indent   != -1  )   return;   // Директива
if ( line[0]  ==  '\0'   )   return;   // Пустая строка
if ( *line == COLOR_ON )   line += 2;
if ( *line == ash.cmnt[0]   )   return;  // Строка комментария.   . .
// Мы не хотим еще раз выводить префикс для других строк
// той же инструкции данных. Для этой цели мы запоминаем номер
// строки и сравниваем его до генерации нового префикса.
static ea_t old_ea = BADADDR; static int old_lnnum;
if  ( old_ea == ea && old_lnnum == lnnurn ) return;
// Отобразим размер текущего элемента как определенный пользователем префикс, ulong our_size = get_item__size (еа) ;
// Похоже на строку команды. Мы не проверяем ее размер, поскольку // она будет дополнена пробелами самим ядром.
snprintf(buf,  bufsize,   " %d",  our_size);
// Запоминаем адрес и номер строки,  для которой мы создали префикс.
old_ea = еа;
old lnnum = lnnum;
I/-----------------------------------------------------,---------------------
//
// Инициализация,
//
//  IDA вызывает эту функцию только один раз.
// Если она возвращает значение PLGUIN_SKIP,   IDA никогда не загрузит ее снова. // Если эта функция возвращает значение PLUGIN_OK,   IDA выгрузит // дополнительный модуль,   но запомнит,  что модуль может работать // с базой данных.
// Дополнительный модуль будет загружен снова,  если пользователь
// активизирует его нажав "горячую" клавишу или выбрав его из меню.
// После загрузки дополнительный модуль остается в памяти.
// Если эта функция возвращает значение PLUGIN_KEEP,   IDA сохранит
// дополнительный модуль в памяти.  В этом случае функция инициализации
// может подключиться в модуль процессора и к точкам выдачи уведомлений
// через пользовательский интерфейс.
// Смотри функцию hook_to_notification_point().
//
//В этом примере мы проверяем формат входного файла и принимаем решение.
// Вы можете проверять или не проверять другие условия // для принятия решения
относительно того,  что делать,
// если вы согласились работать с базой данных.
//
int. init (void) i

if   (  inf.filetype == f_ELF )   return PLUGIN_SKIP;
// Раскомментируйте следующую строку,   чтобы понять как работает уведомление: // hook_to_notification_point(HT_UI,  sample_callback, NULL);

II Раскомментируйте следушшуо строку,  чтоОы понять как работает // определенный пользователем префикс:
/ t set _user_defined_p; ef ix (pref ix_w laths o.et_use;_def ±ned_pref ix) ,-return PLUGIN KEEP;
//-------------------------------------------------------.....--------------
// Завершить.
// Ks;K правило зтог обратный вызов пустой.
// Дополнительный модуль полнен отключится от списка уведомлений,
// если Сыла испольэ^аяка функция hcok_to_notifitetion^point{1.
П
ti IDA вызовет эту функцию при запросе пользователя нэ выиод.
П Эта функция не будет вызвана в случае
// аварийного эааерыекия программы.
void term(vcid) I
u.i.loott- froJn _r.atificar.ior. point |HT__U1,   3ajnpie_calibac<) ; setuserdeEinedpreEix(0,  NULL| ;
I
Добавим еще несколько заголовочных файлов и несколько глобальных пере­менных.
♦ include -^process, h>
# ir.elude "resource .ii"
DWORD g_-empE3t_staCe = 0; LPVOID cj_mapped_f ile - NULL; DWORD efllesize = 0;
Это позволяет загрузить файл в память. Этот файл будет использоваться в каче­стве образца для сравнения с нашим загруженным двоичным файлом. Обычно вы загружаете файл без заплаты в ГОЛ и сравниваете его с файлом, в который были внесены исправления.
bval iijdd^rilti I  thai.  "lliefileuamt f [
HANDLE aFileH -
CreateFile! theFiiename,
GENER1C_REAC, 0,
NULL,
PAGE_READONL1\
0,
0,
NULL  ];
if (!амарш
I
msg ("failed  to open map of file\n"]; return FALSE;
)
LPVOID a?iiePointer -
MapVlewOfFileExI aMapH,
FILE_MAP_READ,
D,
0,
3.
HULL) ;
DWORD aFileSize - Get fi leS i ze (aFI ieH,   NL'LL);

g_file_size = aFileSize; g_mapped_file = aFilePointer;
return TRUE;
)
Эта функция принимает строку машинного кода и сканирует проверяемый файл на предмет наличия этих байтов. Если строка кода не обнаруживается в проверяе­мом файле, то область кода будет помечена в качестве той, которая подверглась из­менениям. Это довольно простой метод, и он срабатывает во многих случаях. Но из-за изложенных в начале этого раздела проблем, этот метод исследования может при­вести к большому числу ложных тревог.
bool check_target_for_string(ea_t theAddress,   DWORD theLen) [
bool ret = FALSE; if(theLen > 4096) {
msg("skipping large buffer\n"); return TRUE;
}
try
(
// Сканируем проверяемый двоичный файл на предмет наличия строки, static char g_c[4096];
// Я не знаю другого способа скопировать строку данных // из базы данных IDA?! for(DWORD i=0;i<theLen;i++) (
g_c[i]  = get_byte(theAddress + i);
1
// Теперь у нас есть строка машинного кода;  выполним поиск. LPVOID curr = g_mapped_file; DWORD sz = g_file_size;
while(curr && sz) (
LPVOID tp = memchrfcurr, if(tp) (
sz -=  ( (char *) tp -
)
if(tp && sz >= theLen) (
if(0 == memcmp(tp,  g_c, theLen)) {
// Мы нашли совпадение! ret = TRUE; break;

if (sz > 1) 1
curr =  ((char *)tp)+l;
)
else {
break;
)
)
else [
break;
g_c[0], s z); (char *) curr);

:■
)
catch (...) <
msg["[!] critical failure."); return TRUE;
}
return ret;
Этот поток выявляет все функции и сравнивает их с двоичным файлом.
cdecl    test(void *р)
void i
// Ожидаем стартового сигнала, while(g_tempest_state == 0) {
SleepUO) ;
Вызываем функцию get_f unc_qty (), чтобы определить количество функций в загруженном двоичном файле.
/////////////////////////////////////
// Выполнить подсчет во всех функциях.
////'/////7Т///////////////?//7г//7///
int total_functions ■ get func qty {) ; int total_diff_matches » 0;
Теперь запустим цикл для каждой функции. Для получения структуры функции вызываем функцию getn_func (). Из структуры функции получаем начальный и конечный адрес каждой функции. Структура функции имеет тип f unc_t. Тип ea_t также известен как эффективный адрес (effective address) и представляет собой длинное целое число без знака. Из .структуры функции мы получаем начальный и конечный адрес функции. Затем мы сравниваем последовательность байтов с прове­ряемым двоичным файлом, как показано ниже.
for(int n=0;n<total_functions;n++) {
// msg("получаем следующую функцию \n">; func t *f = getn func (n);
/////////////////////////////////////////////// II Начальный и конечный адреса функции // есть в структуре
1111111II11111111111111111Н111111111U11111111 ea_t myea " f->startEA; ea_t last_location ■ myea;
while!(myea <™ f->endEA)   SS   (myea != BADADDR)) {
// Если пользователь захочет остановиться, мы должны вернуться сюда, if(0 -- gtempeststate) return;
eat nextea - get_first_cref_from(myea);
ea_t amloc ■ get_first_cref to (nextea);
ea_t amloc2 = get_next_cref to(nextea, amloc);
//Мы также проверяем наличие нескольких ссылок
if((amloc == myea)   &&   (amloc2 — BADADDR))
(
// Я завяз в циклах, поэтому добавил этот фрагмент // для принудительного выхода из следующей функции.

if(nextea > uvea!
(
myea « nent.ee;
а----------------------------------------------
// Расхомментируйте два следующее строки, чтооы
// получить результаты сканирования в графической форме.
// Вылядит эдорозо,  но замедляет сканирование.
/.' ----------------------------------------------
II jjmpto(myea);
i I  tefresn_idaviewI];
I
else myea = BADADDR;

)
Ise
I
// Ссылка — Это не последняя инструкция _CR_ Щ На этот код есть множественные ссылки.
П Немного изменим предыдущий код и лоСавим комментарий II если эти места не совпадают
II msg ( "отличавшееся место...  \П") ;
Разместим комментарий в нашем листинге (с помощью функции add_iong_cmt),
если проверяемый двоичный файл не содержит искомой строки машинного кода.
bool pa'jse for effect - FALSE; inn siia = myea - last location;
if I FALSE =■ crieck_target_for string(la£t_location, size)) i
add_icng_cmt (last__loca t ion, TRUE,
"*= ** Это место кола отличается от
места в проверяемом файле       =\г." N
msg("ОСнаружено место 0х%0ЙХ которое не созпадает о местом в проверяемом файле !\п",  last location); total di f ;_гг:3 rciiea-f *;
1
if Inextea > myea)
(
myea - nextea; else myea - BADADDR;
/У перейти к следующему адресу, jumpto(myea); refresh _idaview |);
1
)
)
msg("Сделано 1 В коде найдено *id мест,  которые отличаются от мест в проверяемом файле.\п",  total_diff_matcries) ;
1
Эта функция отображает диалоговое окно, запрашивающее у пользователя имя файла.
char ■ GetF1lenameDialog(HWND theParentWnd)
static TCHAR s zTile "HAX PA7H]   - "SO";
st.rcpy( szFile, ""1

0PLNFI1ENAME CpenFileName;
OpenFiloName . IStructSize -  s izeoE   (OPENFILENAME); OpenFlieName . hwndQuner ■ chftP^rcn-Wnd;
Opcr.ViieKarr.c . hlnstance = GetModuieKanaie ( "di tt sr.anner .pi w"
Open.-ileName. IpstrFllter - "wOOt!  all flie*W~". *\0\0";
Opei-iFi ieName. IpstrCustomFiiter = NULL;
Openyi-eNarre .nMaxCustFilter = 0;
OpenFlleNaine . nci 11st i ndes - IJ
OpenFi leNaire . IpsL cTi ie - s/.?i If. i
OpcnFileName.nMaxFile ■ si геоЕ[szFilel;
Open^i . ips LrFileTi        - NULL;
OpenFileNair.e . nMaxFileTitle = 0;
OpenFlleName.IpsLrIniLidIDir = NULL;
ApenFi leName . IpsLrTi t ie - "Open" ,-
Open? i ] eNair.e . nFileOffse;  - 0,-
fjpenri leNair,e . nF i leExr ens i on w 0 ;
CjpenFi leName . IpstrEefExt  - "•.*";
GpenFileName. IC'JSLData =   0 ;
CpenFileName.IprnHooK = NULL ?
Open filename . IpTempiateName - NULL;
OpenFlleName-. Flags = CFN_EXFLOPi;H   i ©Ш^ЖЩЗДВЗШИН
i £[GeLOpenFileName(  bOpenFileNarae  )) I
return (siiile) ;
I
tttUrn NULL;
\
Как и для всех "самодельных" диалогов, необходима функция DialogProc для обработки сообщений Windows.
BOOL CALLBACK My □ialufiProi: 1HWND hDla,   LINT fisq,   UP ARAM яРагдгг,,   LPflRAM IParanO
t'
switch [irsrj)
(
case WM COMMAND:
i f "(LCWCBD luPa rami ( DC tmSM&RI
I
cbar  "p = GRtFiieriajneDialog IhClg} ; SetClglteinTeKtthDla,   I DC E С-17_ Fl LENAME, pi;
1
if   (LCWOKLMviParaml IPG 37ART)
I
char, filename,2 5o1 г
GtLblyUetiiTent (hDlg,   ICC_ED1T_FI LENAME,   fibaname,   2Ы) ;
iJlCl « зг rlen(f1 1 enamel |
I
HessagpBoK (hBLg,   "Эь: не выбрали фаил для исследования", "Попробуйте c^ic раз",  MB OK);
)
else   ifllued  f i 1 e (f i lenanie j ) (
gtempeststate ~ '*-f
iinableWindow I  Getnlgl tern (hDlg,   1ПС_£ТАКТ), FALSE);
1
else I
Messag^Eiox itrJlg,   "Проверяемый файл не открывается", KOUi;iC«aH, ИВ ОК1,-
;
)
if   (LOWORDIwParam)   — I DC STOP)
(
g tempest state = 0;
I

if   (LOWORD(wParam)  == IDOK  ||  LOWORD(wParam)  == IDCANCEL) {
if(LOWORD(wParam)  == IDOK) (
}
EndDialog(hDlg,  LOWORD(wParam)); return TRUE;
)
break; default: break;

return FALSE;
}
void _cdecl _test2(void *p)
(
DialogBox ( GetModuleHandle("diff_scanner.plw"), MAKEINTRESOURCE(IDD_DIALOG1), NOLL,  MyDialogProc); )
//--------------------------------------------------------------------------
//
// Метод дополнительного модуля. //
// Это main-функция дополнительного модуля. //
// Она будет вызываться при выборе пользователем дополнительного модуля. //
// Arg - входной аргумент.  Он может быть определен в
// файле plugins.cfg.  По умолчанию равен нулю.
//
//
Функция run вызывается при активизации дополнительного модуля пользова­телем. В этом случае мы запускаем несколько потоков и отправляем короткое сооб­щение, которое отобразится в окне журнала.
void run(int arg) (
// Тестирование.
msg("запускаем дополнительный модуль для поиска отличий\п") ; _beginthread(_test,  О,  NULL); _beginthread(_test2,   0, NULL);
)
Эти глобальные элементы данных используются программой IDA для вывода информации о дополнительном модуле.
//--------------------------------------------------------------------------
char comment[]  = "Diff Scanner Plugin,  written by Greg Hoglund (www.rootkit.com)"; char help[] =
"Дополнительный модуль находит отличия в двоичном коде\п" "\п"
"Этот модуль обозначает места в коде,  которые были изменены.\п" "\п";
//--------------------------------------------------------------------------
// Это предопределенное имя дополнительного модуля в системном меню. // Предопределенное имя может быть заменено в файле plugins.cfg.
char wanted name[]  = "Diff Scanner";

II Это предопределенная "горячая" клавиша для дополнительного модуля.
// Предопределенная "горячая" клавиша может быть заменена в файле plugins.cfg.
// Замечание:  IDA не предупредит о некорректности "горячей" клавиши.
// Это только отключит   горячую клавишу.
char wanted_hotkey[] = "Alt-0";
// БЛОК ОПИСАНИЯ ДОПОЛНИТЕЛЬНОГО МОДУЛЯ
extern "С" plugin_t PLUGIN = { IDP_INTERFACE_VERSION,
О, // Параметры дополнительного модуля,
init, // Инициализация.
term, // Завершить.  Этот указатель может иметь значение NULL.
run, // Запустить дополнительный модуль.
comment, // Долгий комментарий для дополнительного модуля
// Может появляться в строке состояния // или как подсказка.
help,  // Многострочная помощь для дополнительного модуля
wanted_name,  // Предопределенное сокращенное имя для модуля wanted_hotkey // Предопределенная "горячая" клавиша для запуска модуля

 

На главную | Содержание | Назад | Вперёд
 
Яндекс.Метрика