Только для образовательных целей! |
|
Автор: | bi0w0rM [AHTeam] |
Уровень: | Для опытных |
Инструменты: |
[ В этой статье :: ]
>> Рассмотрим пример создания загрузчика(loader) для ASProtect >> Universal Loader для Total Commander(оригинальный код by Nabu) >> Еще один простой пример
[iNT R0] Умение программирования есть одно из лучших качеств хорошего крякера, ибо все, что делается руками, можно автоматизировать. Нашел способ распаковать хитрый паковщик/протектор - можно написать распаковщик. Главное вооружиться MSDN или Win32 SDK(поставляется, например, с Delphi или с C++Builder) и уметь кодить на чем-нибудь... отличным от Басика и просто Поцкаля. Мы же будем использовать MASM32(для его скачивания нет лучше сайта, чем www.wasm.ru). Макроассемблер крут и удобен. W32ASM - pure and simple ;) Тем более при компиляции получаются проги ОЧЕНЬ маленького размера, что хорошо крякеру, который что-то релизит в инет. Итак, начнем...
[PART I] ASProtect Loader Очень много сказано о защите ASProtect, особенно о его распаковке, долгой и мудежной, в большинстве случаев - с подлянами и засадами. Всё новые приемы обнаружения отладчика... Новые его версии уже в win9x хрен снимешь. Да, есть конечно Stripper, но я часто встречал случаи, когда распакованный файл не запускался, хотя версия аспра стриппером поддерживалась. Но очень простой способ прокатывает почти на всех прогах запакованных аспром - это создание загрузчика. Нет, аспр проверяет при запуске целостность кода, но что если патчить память, когда проверка уже произошла и Protection Error 15 не покажется? Вот... И для этого давайте напишим лоадер, который при желании можно заделать под просто лоадер для непакованных прог. Для этого будем использовать функции отладки. Используемые функции и их аргументы:
BOOL CreateProcess(
LPCTSTR lpApplicationName, // имя проги LPTSTR lpCommandLine, // CommandLine LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, //Инфа о стартапе LPPROCESS_INFORMATION lpProcessInformation //Инфа о процессе ); В принципе, нужное я обозначил. Если хотите узнать, какие значения может принимать lpProcessAttributes, lpThreadAttributes, dwCreationFlags и другие, то сходите в msdn.com или пошарьте в Win32 SDK, да и вообще в любом справочнике по Win API.
BOOL WriteProcessMemory(
HANDLE hProcess, // Это процесс, созданный по CreateProcess LPVOID lpBaseAddress, // Виртуальный адрес, по которому надо произвести запись LPVOID lpBuffer, // Это байты, которые надо записать DWORD nSize, // Номер байтов для записи LPDWORD lpNumberOfBytesWritten // Кол-во байтов для записи :) ); Здесь не должно возникнуть вопросов.
BOOL ReadProcessMemory(
HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead ); Да это тоже самое, только здесь не запись в память, а чтение.
Ну вот, теперь запускаем QuickEditor(QEDITOR.EXE в папке MASM32). Пишем туда наш код
======= bEGIN oF mpatch.asm =======
.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
; include - необходимый минимум для нашей проги
.data App db "DzSoft PHP Editor",0 ; Ну эт я над прогой такой издевался :) Err1 db "Program not found :(",0 ; Обломов prog db "DzPhpEd.exe",0 ; Имя проги bufwr DWORD 90h ; Мне нужно байт 75 поменять на NOP(90) va DWORD 579C03h ; Это первый адрес, по которому надо произвести запись va2 DWORD 579C04h ; А это второй
.data? bufr dw ? ; Байт, в который производится чтение pinfo PROCESS_INFORMATION <> ; Это Инфа о процессе sinfo STARTUPINFO <> ; Это инфа о запуске n DWORD ? ; Кол-во байтов .code
start: ; Оффицильное начало invoke CreateProcess,addr prog,NULL,NULL,NULL,FALSE, CREATE_NEW_CONSOLE OR NORMAL_PRIORITY_CLASS,NULL,NULL,addr sinfo,addr pinfo ; Создаем процесс.. CREATE_NEW_CONSOLE OR NORMAL_PRIORITY_CLASS ; для того, чтобы запускаемая прога не сворачивалась. .IF eax == 0 ; Если EAX(результат) равен 0(прога не запустилась) invoke MessageBox,NULL,addr Err1,addr App,MB_OK ; Выдаем сообщение invoke ExitProcess,0 ; И выходим :) .ENDIF .WHILE TRUE ; Это цикл. Вот за такие high-level ; макросы я очень MASM люблю :) invoke ReadProcessMemory,pinfo.hProcess,va,addr bufr,1,n ; Читаем память .IF eax != 0 ; Если все ОК .IF bufr != 00h ; И байт под адресу va(579C03h) не ; равен 00h(прога распакована уже), то.... invoke Sleep,300 ; Слипаем глаза, пока аспр проверяет целостность кода invoke SuspendThread,addr pinfo.hThread ; Тормозим нашу прогу invoke WriteProcessMemory,pinfo.hProcess,va,addr bufwr,1,n ; Пишем куда надо NOP invoke WriteProcessMemory,pinfo.hProcess,va2,addr bufwr,1,n ; И сюда тоже NOP invoke ResumeThread,addr pinfo.hThread ; Отправляем прогу дальше invoke CloseHandle,pinfo.hThread ; Сворачиваем мокруху invoke ExitProcess,0 ; Уходим .ENDIF .ENDIF .ENDW
end start ; Оффициальный конец
======= eND oF mpatch.asm ======= ну че? и все! Теперь Build All и получаем лоадер в 2,5 Кб. Этот размер до того маленький, что даже UPX говорит: "Зачем сжимать? Тут и сжимать нечего!" :). Похожую прогу писал - = ALEX = - , но у него вся эта тема была на делфи с его лишним весом :) тем более я делфу не знаю(и не уютно в ней чувствую себя), знаю только C и ASM(эти языки -- рай для программиста). Чтоб делать просто лоадер(без всяких там Sleep и цикл м проверкой на распакованность), просто уберите эти строки :). То есть вам надо будет только CreateProcess, SuspendThread, WriteProcessMemory и т.д.
[PART II] Universal Total Commander Loader Универсальный кряк для крутого файлового менеджера Total Commander. В самой проге применен алгоритм шифровки кода. Если надо - копайтесь, а мы пойдем путем шаровика :) Разработчики хорошо постарались, закриптовав прогу, но допустили погрешнсть - надо сначала показать наг, а потом МейнСкрин(основное окно), а у них НАГ блокиует MainScreen, а поэтому его легко убрать. Берем маленькую прогу WindowMan с y0da.cjb.net и зажав CTRL направляем на наг-скрин Total Commander мышу. Класс зовется TNASTYNAGSCREEN, запомним. Теперь таким же образом смотрим на главное окно, оно зовется TTOTAL_CMD, учтем...
Все, это достаточно, чтоб написать лоадер, который убирает наг автоматически. Подобную прогу уже представлял Nabu. Но там был C, а мы напишем на ассемблере и немного доработаем:
======= bEGiN oF tcmdcrk.asm =======
.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
WND1 HWND ? WND2 HWND ? WNDNAME1 db "TNASTYNAGSCREEN",0 ; ага! вот где имя класса нага пригодилось! WNDNAME2 db "TTOTAL_CMD",0 ; имя класса главного окна CAPTION db "Total Commander 5.5 - bi0w0rM",0 ; ну ето просто :) EXENAME db "totalcmd.exe",0 ; Имя проги
.code
start: INVOKE WinExec, addr EXENAME, SW_SHOWNORMAL ; Запускаем .WHILE TRUE ; Пусть прога бьется в цикле, когда надо - выйдем по ExitProcess INVOKE FindWindow,addr WNDNAME2,NULL ; ищем окно TTOTALCMD MOV WND2,EAX ; Кладем результат в WND2 .IF EAX != 0 ; Если окно нашлос-таки INVOKE FindWindow,addr WNDNAME1,NULL ; То изем еще одно окно - НАГ MOV WND1,EAX ; Ложим результат INVOKE ShowWindow,WND1,SW_HIDE ; Убираем наг НАФИК INVOKE EnableWindow,WND2,TRUE ; Енейблим главное окно INVOKE SetForegroundWindow,WND2 ; И делаем его сразу активным INVOKE SetWindowText,WND2,addr CAPTION ; Ну и небольшой прикол:) который не проходит :(( INVOKE ExitProcess,0 ; Уходим... .ENDIF .ENDW
end start
======= eND oF tcmdcrk.asm ======= У Nabu был прикол со Sleep. У нас же лоадер быстрее, так как бьется в цикле, пока не не создастся окно. Если он его находит - убивает наг и енейблит окно. Спасибо тебе, Nabu! Так бы я не решился такого написать!
[ЕЩЕ!] Еще недавно попросили меня сделать кейген для крякмиса фантома(FaNt0m's Crackme #4). Я разобрался в алгоритме генерации, но что-то лениво очень было писать :) Я решил - опять пойдем другим путем :) в результате - тот же кейген! В опщем посмотрев в листинге w32dasm я увидел, что сам ключ лежит по адресу 403284. Можно было бы использовать функции отладки, ставить бряки, пробиваясь в цикле... Зачем? можно сделать все проще. Итак, привожу проверку ключа:
===== Начало Листинга ===== :00401205 6800010000 push 00000100 :0040120A 6884304000 push 00403084
* Possible Reference to Dialog: MAINDIALOG, CONTROL_ID:03E8, "" | :0040120F 68E8030000 push 000003E8 :00401214 FF7508 push [ebp+08]
* Reference To: USER32.GetDlgItemTextA, Ord:0102h | :00401217 E862010000 Call 0040137E :0040121C 6800010000 push 00000100 :00401221 6884314000 push 00403184
* Possible Reference to Dialog: MAINDIALOG, CONTROL_ID:03E9, "" | :00401226 68E9030000 push 000003E9 :0040122B FF7508 push [ebp+08]
* Reference To: USER32.GetDlgItemTextA, Ord:0102h | :0040122E E84B010000 Call 0040137E :00401233 FF7508 push [ebp+08] :00401236 E8BE000000 call 004012F9 :0040123B 83F800 cmp eax, 00000000 :0040123E 7415 je 00401255 :00401240 6A40 push 00000040
* Possible StringData Ref from Data Obj ->"Check Serial" | :00401242 6829304000 push 00403029
* Possible StringData Ref from Data Obj ->"You got it! Congrats! :)"
; ............... ; В процедуре 004012F9 явно происходит проверка, зайдем в нее:
:004012F9 55 push ebp :004012FA 8BEC mov ebp, esp :004012FC 56 push esi :004012FD 57 push edi :004012FE 8D3584304000 lea esi, dword ptr [00403084] :00401304 8D3D84324000 lea edi, dword ptr [00403284] :0040130A 33C0 xor eax, eax :0040130C 33C9 xor ecx, ecx :0040130E B31A mov bl, 1A
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401328(U) | :00401310 803E00 cmp byte ptr [esi], 00 :00401313 7415 je 0040132A :00401315 8A06 mov al, byte ptr [esi] :00401317 02C1 add al, cl :00401319 32C1 xor al, cl :0040131B F6F3 div bl :0040131D 66C1E808 shr ax, 08 :00401321 0441 add al, 41 :00401323 8807 mov byte ptr [edi], al :00401325 47 inc edi :00401326 46 inc esi :00401327 41 inc ecx :00401328 EBE6 jmp 00401310
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401313(C) | :0040132A C60700 mov byte ptr [edi], 00 :0040132D 33C0 xor eax, eax :0040132F 83F900 cmp ecx, 00000000 :00401332 741A je 0040134E :00401334 6884324000 push 00403284 :00401339 6884314000 push 00403184
* Reference To: KERNEL32.lstrcmpA, Ord:02D6h | :0040133E E8A1000000 Call 004013E4 ; Ну уж кда проще - вызов strcmp. Сравнивает строки на похожесть :00401343 83F800 cmp eax, 00000000 ; Если не одинаковы, то жопа :00401346 7404 je 0040134C ; и джамп... :00401348 33C0 xor eax, eax :0040134A EB02 jmp 0040134E
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401346(C) | :0040134C 8BC1 mov eax, ecx
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00401332(C), :0040134A(U) | :0040134E 5F pop edi :0040134F 5E pop esi :00401350 C9 leave :00401351 C20400 ret 0004
........................ Перед началом вызова strcmp были записаны два параметра: 00403184 и 00403284. По достоверным данным, которые мне дали SoftICE и OllyDbg - в 00403284 лежит правильный ключ. Что делать теперь? Посмотрим на вызов обламывающего окна:
:00401255 6A30 push 00000030
* Possible StringData Ref from Data Obj ->"Check Serial" | :00401257 6829304000 push 00403029
* Possible StringData Ref from Data Obj ->"Wrong Serial! Keep trying, you'll " ->"get it!" | :0040125C 6836304000 push 00403036 :00401261 6A00 push 00000000
* Reference To: USER32.MessageBoxA, Ord:01BBh | :00401263 E834010000 Call 0040139C ===== Конец Листинга ===== Короче, вместо push 00403036, мы ставим push 00403284 и тогда вместо появления надписи Wrong Serial нам покажецца наш ключ! :)) Какие байты менять? было 6836304000, а станет 6884324000. Но мы раз кейгенщики, то давайте сделаем, чтобы байты патчились на лету! Вот прога:
======= bEgIN oF loader.asm ======= .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
Exe db "CRACKME4.EXE",0 ; Прога App db "bi0w0rM'z loader",0 ; Понты :) Error1 db "Could not create process :((",0 ; Сообщение, если не удалось запустить Error2 db "Fucking Version :((",0 ; Если это вовсе не наш крякмис, ; а соффсем другая прога
.data?
Sinfo STARTUPINFO <> ; Надо для запуска Pinfo PROCESS_INFORMATION <> ; Надо для запуска WByte DWORD ? ; Байт для записи RByte DWORD ? ; Байт для чтения Adr DWORD ? ; Адрес для патча n DWORD ? ; Кол-во байтофф
.code
start:
invoke CreateProcess,addr Exe,NULL,NULL,NULL,FALSE, NORMAL_PRIORITY_CLASS,NULL,NULL,addr Sinfo,addr Pinfo CMP EAX,0 JNZ PATCH INVOKE MessageBox,NULL,addr Error1,addr App,MB_ICONERROR INVOKE ExitProcess,0
PATCH:
MOV Adr, 0040125Dh MOV WByte,84h CALL PATCHER MOV Adr, 0040125Eh MOV WByte,32h CALL PATCHER MOV Adr, 0040125Fh MOV WByte,40h CALL PATCHER
invoke CloseHandle,Pinfo.hThread INVOKE ExitProcess,0
FUCK: INVOKE MessageBox,NULL,addr Error2,addr App,MB_ICONERROR
PATCHER PROC INVOKE ReadProcessMemory,Pinfo.hProcess,Adr,addr RByte,1,n INVOKE WriteProcessMemory,Pinfo.hProcess,Adr,addr WByte,1,n RET PATCHER ENDP
end start
======= eND oF loader.asm ======= Теперь скомпилируем лоадер и запустим его в папке с крякмисом. Введем имя и кей от балды. Во! Появляется мессага с нашим ключом.
Возможно, что в скором времени я напишу подобную статью, только уже об непосредственно отладке, бряках, трейсинге и т.д. Может даже напишем простенький распакоффщик для UPX. Но это будет потом, а пока на этом я прощаюсь.
bi0w0rM[AHT] bioworm.narod.ru mailto:bioworm@mail.ru ... на закате 2003 HAPPY NEW YEAR, ALL CRACKERZ AROUND THE WORLD!
|