Дао универсальной распаковки Крис Касперски Хакер, номер #089, стр. 089-114-2 Ага, вот он! Рассмотрим его повнимательнее. Я уверяю: тут есть чему поучиться: [хитро оптимизированный цикл чтения строковых ресурсов] 01004825h mov ebp, ds:LoadStringW ; ebp - указатель на LoadStringW 0100482Bh mov edi, offst off_10080C0 ;указатель на таблицу ресурсов 01004830h 01004830h loc_1004830: 01004830h mov eax, [edi] ; грузим очередной указатель на uID в eax 01004832h push ebx ; nBufferMax (максимальная длина буфера) 01004833h push esi ; lpBuffer (указатель на буфер) 01004834h push dword ptr [eax] ; передаем извлеченный uID функции 01004836h push [esp+0Ch+hInstance] ; hInstance 0100483Ah call ebp ; LoadStringW ; считываем очередную строку из ресурса 0100483Ch mov ecx, [edi] ; грузим тот же самый uID в ecx 0100483Eh inc eax ; увеличиваем длину считанной строки на 1 0100483Fh cmp eax, ebx ; ?строка влезает в буфер? 01004841h mov [ecx], esi ; сохраняем указатель на буфер поверх 01004841h ; старого uID(он больше не понадобится) 01004841h 01004843h lea esi, [esi+eax*2] ; позиция для следующей строки в буфере 01004846h jg short loc_100488B ; если буфер кончился, то это облом 01004848h add edi, 4 ; переходим к следующему uID 0100484Bh sub ebx, eax ; уменьшаем свободное место в буфере 0100484Dh cmp edi, offst off_1008150 ; ?конец таблицы ресурсов? 01004853h jl short loc_1004830 ; мотаем цикл, пока ресурсы не закончились В переводе на русский, это звучит так: "Блокнот берет очередной идентификатор строки из таблицы ресурсов, загружает строку, размещая ее в локальном буфере, и сохраняет полученный указатель на строку поверх… самого идентификатора, который уже не нужен!" Классический трюк с повторным использованием освободившихся переменных, известных еще со времен первых PDP, если не раньше. А вы все Microsoft ругаете! Блокнот писал не глупый хакер, бережно относящийся к системным ресурсам и, в частности, к памяти. Для нас же это в первую очередь означает, что снятый с "живой" программы дамп будет неполноценным. Вместо реальных идентификаторов строк в секции ресурсов содержатся указатели на память, указывающие в "космос"! Ведь при повторном запуске Блокнота листинг 1 уже не срабатывает. Во многих программах встречается конструкции вида: ["защита" от дампинга живых программ] void *p=0; // глобальная переменная if (!p) p = malloc(BUFF_SIZE); Очевидно, если сдампить программу после завершения строки с "if", то глобальная переменная p будет содержать указатель, доставшийся ей в "наследство" от предыдущего запуска, однако соответствующий регион памяти выделен не будет, и программа либо рухнет, либо залезет в чужие данные, устроив там настоящий переполох! Сформулируем главное правило: дампить программу можно только в точке входа! Остается разобраться, как эту точку входа отловить. [универсальный пример поиска OEP, основанный на балансе стека] Вот мы и подобрались к самому интересному и универсальному способу определения OEP, который к тому же легко автоматизировать. Упаковщик (даже если он не совсем корректный) просто обязан после распаковки восстановить стек, в смысле вернуть регистр ESP на место, под которым будет первичный фильтр структурных исключений, установленный системой по умолчанию. Некоторые упаковщики еще восстанавливают и регистры, но делать это в общем-то и не обязательно. |