Автор: | bi0w0rM [AHTeam] |
Уровень: | Для опытных |
Программа: | mIRC 6.12 |
Инструменты: | MASM32, SoftICE |
[iNTR0] До недавнего времени я не особо любил сидеть в IRC: инет дорого стоит, да и "фигня все эти чаты" думал я. так и думал, пока не подключился к нашей городской интрасети - стоит дешево(2р/час, ночь - free) и народ здесь местный, то есть и друзья реальные. у меня был mIRC 5.12(так, шоб был :)), но на местном ftp-серве я откопал свежую версию этого кульного клиента(6.12), тем более она еще и скрипты поддерживает(что сыграло значительную роль в зафлудивании в привате и нюканьи врагов:)). и защиту Khaled Mardam-Bey улучшил, хотя не намного...
[rIPPiNG] Давайте поползем в раздел, посвященный регистрации -- Help->Register... Кто знаком с Win32-программированием знает, что из EditBox'а текст достается при помощи вызова API-функции GetDlgItemTextA. В софтайсе я поставил на нее брейкпоинт. bpx getdlgitemtexta. Нажал в мирке на ОК и нифига не вышло :(( Хотя я вспомнил, что достать текст можно еще и через SendDlgItemMessage, вот функция и ее входные параметры:
LONG SendDlgItemMessage(
HWND hDlg, // handle of dialog box int nIDDlgItem, // identifier of control UINT Msg, // message to send WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); Так вот, если посмотреть SDK к винде(поставляется с C++Builder и Delphi), то чтобы достать текст(аналог GetDlgItemTextA), надо в параметр Msg подставить WM_GETTEXT, при этом lParam - адрес строки, куда будет помещен текст, а в wParam максимальное количество символов. Это я все к тому, что надо поставить брейкпоинт на SendDlgItemMessage, делаем bpx SendDlgItemMessage, ОК, F12. и впрямь! попали:
* Reference To: USER32.SendDlgItemMessageA, Ord:0236h | :004C2337 8B3D7C465700 mov edi, dword ptr [0057467C] :004C233D 68E8F45800 push 0058F4E8 :004C2342 68E7030000 push 000003E7 :004C2347 6A0D push 0000000D :004C2349 6883000000 push 00000083 :004C234E 56 push esi :004C234F FFD7 call edi :004C2351 68D0F85800 push 0058F8D0 :004C2356 68E7030000 push 000003E7 :004C235B 6A0D push 0000000D :004C235D 6884000000 push 00000084 :004C2362 56 push esi :004C2363 FFD7 call edi В edi записывается [0057467C](вызов USER32.SendDlgItemMessageA) и edi несколько раз вызывается(то есть вызывается [0057467C] фактически). Вотъ. А мы хоть правильно вообще DOOMаем, может это совсем другие вызовы? Проверим: после вызова call edi проверяем входные параметры, куда должны записаться данные, здесь это 0058F4E8 и 0058F8D0. делаем в софтайсе d 0058F4E8 и смотрим в окно данных: там наше введенное имя. ну раз два вызова стоят вместе, значит в 0058F8D0 лежит наш введенный регномер - так и оказалось. Теперь стираем(или деактивируем) брейкпоинт: bc 00 (или bd 00). и ставим новый, на адрес строки с регномером, то есть: bpm 0058F8D0. F5. Прерываемся на:
:004C1DE2 880C07 mov byte ptr [edi+eax], cl :004C1DE5 40 inc eax :004C1DE6 84C9 test cl, cl :004C1DE8 75F6 jne 004C1DE0 :004C1DEA 8D942414010000 lea edx, dword ptr [esp+00000114] :004C1DF1 8D4C2410 lea ecx, dword ptr [esp+10] :004C1DF5 E886FDFFFF call 004C1B80 :004C1DFA 85C0 test eax, eax :004C1DFC 7410 je 004C1E0E :004C1DFE 5F pop edi :004C1DFF 5E pop esi :004C1E00 5D pop ebp :004C1E01 B801000000 mov eax, 00000001 :004C1E06 5B pop ebx :004C1E07 81C408020000 add esp, 00000208 :004C1E0D C3 ret Сейчас мы в цикле. Давайте нажмем на F12. Мы попали сюда:
:004C236F E81CFAFFFF call 004C1D90 :004C2374 85C0 test eax, eax << стоим здесь :004C2376 0F84BB000000 je 004C2437 мы только что были в некой функции 004C1D90. После выполнения test eax, eax я попробовал модифицировать флаги(команда r -> окажемся вверху экрана софтайса и ищем флаг Z(флаг нуля). Стоя на нем нажимаем Insert, затем Enter). Кто не въехал, как менять, сделайте так: стоя на команде je, пишем eb eip и меняем в окне данных 84 на 85. :)) Только не забудьте потом сменить обратно. Теперь нам сказали спасибо за регистрацию. :)) Следовательно, 004C1D90 - что-то вроде процедуры регистрации, которая устанавливает EAX(ее результат). Кому непонятно, то вот вам пример на C:
int CheckRegistr() { ... некий код ... if(!registered)return 0; else return 1; } ... // а где-то вызов ее:
if(CheckRegistr()==1)MessageBox(NULL,"Thanks for registering!!",NULL,MB_OK); // "==1" можно и не писать :) Вот так и тут: 004C1D90 - эта самая CheckRegistr(). Только тут ассемблер, результат передается через EAX. Учитывая, как работают команды je и test(см доки по ассемблеру) решаем, что эта процедура должна в EAX возвращать 1! А кстати, вот отрывок, который это делает(снова взят, см выше):
:004C1DF5 E886FDFFFF call 004C1B80 :004C1DFA 85C0 test eax, eax :004C1DFC 7410 je 004C1E0E :004C1DFE 5F pop edi :004C1DFF 5E pop esi :004C1E00 5D pop ebp :004C1E01 B801000000 mov eax, 00000001 :004C1E06 5B pop ebx :004C1E07 81C408020000 add esp, 00000208 :004C1E0D C3 ret Ну короче 004C1B80 - еще какая-то процедура проверки, все мудрено. Но вот видим заветные команды: mov eax, 00000001 и ret. То есть процедура возвращает 1. Но этому не бывать, если благодаря процедуре 004C1B80 сработает условный переход je 004C1E0E, перепрыгнув mov eax, 00000001 (очевидно там где-то дальше по коду возвратится 0). Так значит, узнаем смещение команды je 004C1E0E и забиваем ее двумя нопами(потому что команда двубайтовая - 0x74 и 0x10) в HIEW. Теперь по идее везде, где вызвается эта процедура проверки, будет выдаваться, что мы зарегены. Но программа перестала запускаться :( Тогда я попробовал сделать loader на ассемблере, вот его исходный код(компилируется в MASM32):
=================================== .486 .model flat, stdcall option casemap :none
include \masm32\include\windows.inc include \masm32\include\masm32.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\shell32.inc includelib \masm32\lib\masm32.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\shell32.lib
.data App db "mIRC 6.12",0 Err1 db "Program not found :(",0 prog db "MIRC.EXE",0 vastart DWORD 4C1DFCh
.data? bufr dw ? pinfo PROCESS_INFORMATION <> sinfo STARTUPINFO <> n DWORD ? .code
start:
JMP STARTUHA
PATCHER PROC RVA:DWORD,WR:DWORD INVOKE ReadProcessMemory,pinfo.hProcess,RVA,addr bufr,1,n invoke WriteProcessMemory,pinfo.hProcess,RVA,ADDR WR,1,n RET PATCHER ENDP
STARTUHA: invoke CreateProcess,addr prog,NULL,NULL,NULL,FALSE, CREATE_NEW_CONSOLE OR NORMAL_PRIORITY_CLASS,NULL,NULL,addr sinfo,addr pinfo .IF EAX == 0 invoke MessageBox,NULL,addr Err1,addr App,MB_OK invoke ExitProcess,0 .ENDIF
INVOKE ReadProcessMemory,pinfo.hProcess,vastart,addr bufr,1,n .IF EAX != 0 .IF bufr == 74h invoke SuspendThread,addr pinfo.hThread
INVOKE PATCHER,4C1DFCh,90h ; ВОТ! ВОТ главное место программы! ; здесь производится запись в память процесса mIRC INVOKE PATCHER,4C1DFDh,90h
invoke ResumeThread,addr pinfo.hThread invoke CloseHandle,pinfo.hThread invoke ExitProcess,0 RET .ENDIF .ENDIF
end start Лоадер работает, все ОК(только не вздумайте запускать его на уже патченной проге). НО и это не самый лучший вариант. Например, надо передавать mIRC параметры командной строки, тут надо дописывать. И вообще в случае того, что прога не запакована, лучше покопаться, и я решил вскрыть защиту до конца.
Из того, что работал лоадер, следует, что прога проверят на измененность сам свой исполняемый файл, а не шарит по своей памяти. Значит, проге надо узнать, где она находится, имя .exe-шника, короче. Опять пригождается опыт программирования под win32: это можно сделать при помощи вызова апишки GetModuleFileNameA. Ставим на нее бряк. bpx GetModuleFileNameA. Она при старте вызывается несколько раз. И самое интересное происходит при третьем ее вызове - два других это обработка ком. строки и еще что-то. Значит после срабатывания надо нажать два раза на F5, потом F12. Итак, а как вообще стоит поступать в подобных случаях? У меня при себе имеются два экземпляра проги: непатченный(запускается) и патченный(не запускается). Так вот, надо протрейсить вручную код и посмотреть, как ведут себя разные условные переходы. То есть после третьего срабатывания GetModuleFileNameA идем по коду и записываем на листочек адреса всех встретившихся условных переходов и приписываем - сработал или нет. Такую работу следует провести с каждым экземпляром проги. Так вот, там дальше будет порядочных размеров код с огромным циклом с подциклами :) Новичкам не очень сойдет. Нажмем на F12 и увидим, что только что были в процедуре(видимо подпроцедуре проверки целостности):
:004D488F E8CCFDF7FF call 00454660 << отсюда вернулись :004D4894 83F802 cmp eax, 00000002 << сравнили результат с 2 :004D4897 741B je 004D48B4 :004D4899 83F803 cmp eax, 00000003 :004D489C 7416 je 004D48B4 :004D489E 33C9 xor ecx, ecx :004D48A0 E81B920000 call 004DDAC0 :004D48A5 33D2 xor edx, edx :004D48A7 85C0 test eax, eax :004D48A9 0F95C2 setne dl :004D48AC A35CA15900 mov dword ptr [0059A15C], eax :004D48B1 8BC2 mov eax, edx :004D48B3 C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004D4888(C), :004D4897(C), :004D489C(C) | :004D48B4 33C0 xor eax, eax :004D48B6 C3 ret Так вот, если не сработает ни один из je 004D48B4, то прога запустится, а иначе мы прыгнем на xor eax, eax и будет плохо :) Значит вставляем по два nop по адресам 004D4897 и 004D489C и прога заработает. :0)
bi0w0rM[AHTeam] (c) 9 Mar 2004 SiTE : bioworm.narod.ru AHTeam: www.ahteam.org
|
|