Sie sind auf Seite 1von 11

Cracking Delphi Programs

By CodeRipper
Tools used: DeDe by DaFixer, Olly debugger, PE Explorer, De Decompiler Lite

I. Let you familiarize with Delphi:


A form is a class which contain a lot of buttons, radiobutons etc. So if we have a form called TfrmPurchase we can have a lot of properties/events: TfrmPurchase.btnOKClick called when we click OK TfrmPurchase.btnCancelClick - called when we click Cancel TfrmPurchase.FormCreate is called when the form is created; default when you double click on Form TfrmPurchase.FormShow - is called when the form is showed TfrmPurchase.TimerTimer is called when the timer is out (something like WM_TIMER from VISUAL C++) TfrmPurchase.Edit1Change - called when the contents of Edit1 is changed TfrmPurchase.Memo1Change - called when the contents of Memo1 is changed If we have on the body of TForm1.Button1Click (under Nag Form.exe): Form1.DestroyWindowHandle; // kill Form1 Form2.ShowModal; // show the Form2 Form3.ShowModal; // show the Form3 The code will look like this: 0045241F MOV EAX,DWORD PTR DS:[455B74] ; reference to Form1 (under Class Information the BSS value) 00452424 MOV EDX,DWORD PTR DS:[EAX] ; now edx contain VMT Ptr (Classes Info in DeDe) 00452426 CALL DWORD PTR DS:[EDX+B4] 0045242C MOV EAX,DWORD PTR DS:[454378] ; reference to TForm2 instance (DATA) 00452431 MOV EAX,DWORD PTR DS:[EAX] ; now eax contains the HEAP value 00452433 MOV EDX,DWORD PTR DS:[EAX] ; now edx contain VMT Ptr 00452435 CALL DWORD PTR DS:[EDX+F8] 0045243B MOV EAX,DWORD PTR DS:[454220] ; reference to TForm2 instance ; (DATA) 00452440 MOV EAX,DWORD PTR DS:[EAX] 00452442 MOV EDX,DWORD PTR DS:[EAX] 00452444 CALL DWORD PTR DS:[EDX+F8] For finding these values you must go at Classes Info (in DeDe), double click on the wanted form and look on the right part you will see DATA,BSS,HEAP. A class is an abstract structure witch contains methods and variables (properties). An object is an instance of one class, a materialization of a class. Objects are kept in a special memory called HEAP.

Delphi use for parsing parameters to functions EAX, EDX, ECX, if they are more then 3 parameters will be used the stack (push parameter_4, push parameter_5). Note that these parameters are passed in reverse order. Delphi deals with global and local variables reference it on the following way: - [ebp+value] means that is pointer to a global variable - [ebp-value] means that it is pointer to a local variable Example: 00484A6C 8B45FC mov eax, [ebp-$04] ; pointer to a local variable in eax 00484A6F E830F1F7FF call 00403BA4 ; call System.LStrOfChar()

II. How a Delphi program looks at entry point:


The code of program: begin Application.Initialize; Application.CreateForm(TForm1, Form1); the NAG form which is first created Application.CreateForm(TForm2, Form2); Application.CreateForm(TForm3, Form3); Application.Run; end. The compiled code will be: 00452668 PUSH EBP 00452669 MOV EBP,ESP 0045266B ADD ESP,-10 0045266E MOV EAX,004524C0 00452673 CALL .00405C94 ; this is the _InitExe routine that also initialize units 00452678 MOV EAX,DWORD PTR DS:[454260] ; TApplication instance (Classes Info) 0045267D MOV EAX,DWORD PTR DS:[EAX] 0045267F CALL .00450578 ; Forms.TApplication.Initialize() 00452684 MOV ECX,DWORD PTR DS:[454344] ; for killing the Form1 we must change ; this to jmp 0045269C ; You should notice that the first form which is created is considerate here as being the main form 0045268A MOV EAX,DWORD PTR DS:[454260] 0045268F MOV EAX,DWORD PTR DS:[EAX] 00452691 MOV EDX,DWORD PTR DS:[452274] 00452697 CALL .00450590 ; Application.CreateForm(TForm1, Form1); 0045269C MOV ECX,DWORD PTR DS:[454378] 004526A2 MOV EAX,DWORD PTR DS:[454260] 004526A7 MOV EAX,DWORD PTR DS:[EAX] 004526A9 MOV EDX,DWORD PTR DS:[451F04] 004526AF CALL .00450590 ; Application.CreateForm(TForm2, Form2); 004526B4 MOV ECX,DWORD PTR DS:[454220] 004526BA MOV EAX,DWORD PTR DS:[454260] 004526BF MOV EAX,DWORD PTR DS:[EAX] 004526C1 MOV EDX,DWORD PTR DS:[4520C8] 004526C7 CALL .00450590 ; Application.CreateForm(TForm3, Form3); 004526CC MOV EAX,DWORD PTR DS:[454260]

004526D1 MOV EAX,DWORD PTR DS:[EAX] 004526D3 CALL .00450610 ; Application.Run; 004526D8 CALL .00403DC8 ; close the application (call ExitProcess) _InitExe - Initialization of units means running its initialization code. So you must step into 00405C94 and trace until you reach the IniUnits() routine. If you enter under 00405C94 you will see a GetModuleHandleA, after that is a call in which you must enter, after you enter inside him you will see another one -> step over this call whit F8 and return (RETN), you will see another call -> this time step into this call and you will find: 00403C1C MOV DWORD PTR DS:[455014],JMP.&kernel32.RaiseException 00403C26 MOV DWORD PTR DS:[455018],JMP.&kernel32.RtlUnwind 00403C30 MOV DWORD PTR DS:[45563C],EAX 00403C35 XOR EAX,EAX 00403C37 MOV DWORD PTR DS:[455640],EAX 00403C3C MOV DWORD PTR DS:[455644],EDX 00403C42 MOV EAX,DWORD PTR DS:[EDX+4] 00403C45 MOV DWORD PTR DS:[45502C],EAX 00403C4A CALL .00403B08 00403C4F MOV BYTE PTR DS:[455034],0 00403C56 CALL .00403BB4 ; you must trace into inside this one 00403C5B RETN If you enter inside call 00403BB4 you will find: 00403BDC CMP EDI,EBX 00403BDE JLE SHORT .00403BF7 00403BE0 MOV EAX,DWORD PTR SS:[EBP-4] 00403BE3 MOV ESI,DWORD PTR DS:[EAX+EBX*8] ; prepare the offset of initialization ; routine of a unit 00403BE6 INC EBX ; increase processed units counter 00403BE7 MOV DWORD PTR DS:[455640],EBX 00403BED TEST ESI,ESI ; is there any initialization routine for this unit? 00403BEF JE SHORT .00403BF3 ; if no, then process next unit 00403BF1 CALL ESI ; else call it! 00403BF3 CMP EDI,EBX ; did we processed all units? 00403BF5 JG SHORT .00403BE0 ; if no go back You can get a list whit all units from the program: Open the exe file with PE Explorer and go to Resource Viewer/Editor; double click on RC Data and click to PACKAGEINFO and you will see a list with all units from the program.

How we can simply find the offset of a method:


Load CrackMe#3.exe in Resource Tuner or PE Explorer and go at RC Data. TFORM1 has two buttons Button1 and Button2, Button1 has the event OnClick Button1Click and Button2 has the event OnClick Button2Click. Also we have a menu called MainMenu1 a submenu called Help1 and the menu item is called About1 -> About1 has the event OnClick About1Click.

In the case of an Editbox we could also have the event OnChange Edit1Change (called when the text from Edit1 is changed). Next step: We load the file under a hexeditor and we search for the ASCII string "Button1Click": 000531C0 000531D0 000531E0 000531F0 07 42 75 74 74 6F 6E 31 61 62 65 6C 31 F8 02 00 6C 32 01 00 13 00 28 3E 6E 31 43 6C 69 63 6B 05 F4 02 00 00 01 00 06 4C 00 01 00 06 4C 61 62 65 45 00 0C 42 75 74 74 6F 54 47 6F 6F 64 02 00 60 .Button1......L abel1......Labe l2....(>E..Butto n1Click.TGood..

The red value before the finded string represents the address of methods called when you click "Button1": 00453E28 Similar in the case of About1 we have 00454814. Similar in the case of Label3Click we have 00454824.

How we can easily find the code executed when you press a button or a menu items
Purpose of this: You got the code called when you click a menu item and we want to find the address which is called when you click on any menu item! Same things works for a Visual C++ program which use MFC42.DLL. 1. In the case of labels/buttons: We set a breakpoint to 00454824, we click on Label3 and after we return from 00454824 we find: 0043248E TEST BYTE PTR DS:[EBX+1C],10 00432492 JNZ SHORT CrackMe#.004324A6 00432494 CMP DWORD PTR DS:[EBX+6C],0 00432498 JE SHORT CrackMe#.004324A6 0043249A MOV EDX,EBX 0043249C MOV EAX,DWORD PTR DS:[EBX+6C] 0043249F MOV ECX,DWORD PTR DS:[EAX] 004324A1 CALL DWORD PTR DS:[ECX+18] 004324A4 JMP SHORT CrackMe#.004324BE 004324A6 CMP WORD PTR DS:[EBX+122],0 004324AE JE SHORT CrackMe#.004324BE 004324B0 MOV EDX,EBX 004324B2 MOV EAX,DWORD PTR DS:[EBX+124] 004324B8 CALL DWORD PTR DS:[EBX+120] ; this one calls the method 00454824 004324BE POP EBX ; we are here 004324BF RETN The address 004324B8 is called in the case of all buttons and all of labels, just set a breakpoint on it.

2. In the case of menu items: We set a breakpoint to 00454814, we click on About and after we return from 00454814 we find: 00442791 MOV EAX,DWORD PTR DS:[EAX+40] 00442794 CMP EAX,DWORD PTR DS:[EBX+88] 0044279A JE SHORT CrackMe#.004427AC 0044279C MOV EDX,EBX 0044279E MOV EAX,DWORD PTR DS:[EBX+8C] 004427A4 CALL DWORD PTR DS:[EBX+88] 004427AA JMP SHORT CrackMe#.004427DC 004427AC TEST BYTE PTR DS:[EBX+1C],10 004427B0 JNZ SHORT CrackMe#.004427C4 004427B2 CMP DWORD PTR DS:[EBX+44],0 004427B6 JE SHORT CrackMe#.004427C4 004427B8 MOV EDX,EBX 004427BA MOV EAX,DWORD PTR DS:[EBX+44] 004427BD MOV ECX,DWORD PTR DS:[EAX] 004427BF CALL DWORD PTR DS:[ECX+18] 004427C2 JMP SHORT CrackMe#.004427DC 004427C4 CMP WORD PTR DS:[EBX+8A],0 004427CC JE SHORT CrackMe#.004427DC 004427CE MOV EDX,EBX 004427D0 MOV EAX,DWORD PTR DS:[EBX+8C] 004427D6 CALL DWORD PTR DS:[EBX+88] ; this one calls the method 00454814 004427DC POP ESI ; we are here 004427DD POP EBX 004427DE RETN The address 004427D6 is called in the case of all buttons and all of menu items, just set a breakpoint on it.

Disable a menu item:


EnableMenuItem api is sometimes used to disable a menu item: 0012F074 0012F078 0012F07C 0012F080 0044C24A CALL to EnableMenuItem from CrackMe#.0044C245 002E0598 hMenu = 002E0598 0000F020 ItemID = F020 (61472.) 00000001 Flags = MF_BYCOMMAND|MF_GRAYED|MF_STRING

Also can be used AppendMenuA or AppendMenuW: BOOL AppendMenu( HMENU hMenu, UINT uFlags, UINT_PTR uIDNewItem, LPCTSTR lpNewItem

); If the uFlags is 1 the menu item will be disabled, if uFlags is 0 the menu item will be enabled!

I. Killing NAG FORMS


Do he ReleaseCapture on Olly (ReleaseCapture is from user32.dll) under same way can be also used for MessageDlg/ShowMessage You should see something like this: 0045306D MOV DL,1 0045306F MOV EAX,DWORD PTR DS:[410458] 00453074 CALL HelpMan_.0040CB8C 00453079 CALL HelpMan_.004037B0 0045307E CALL JMP.&user32.GetCapture 00453083 TEST EAX,EAX 00453085 JE SHORT HelpMan_.00453098 00453087 PUSH 0 ; lParam = 0 00453089 PUSH 0 ; wParam = 0 0045308B PUSH 1F ; Message = WM_CANCELMODE 0045308D CALL JMP &user32.GetCapture 00453092 PUSH EAX ; hWnd 00453093 CALL JMP.&user32.SendMessageA 00453098 CALL JMP.&user32.ReleaseCapture 0045309D MOV EAX,DWORD PTR SS:[EBP-4] ; ReleaseCapture return to this 004530A0 OR BYTE PTR DS:[EAX+2CC],8 004530A7 CALL JMP.&user32.GetActiveWindow Delete the hardware breakpoint and set a hardware breakpoint to last ret and Run with F9. Press Continue on form. You should now break to last ret. Now we step over with F8 a lot of times until we see something like this: 006A0349 MOV EAX,DWORD PTR DS:[6AB3EC] 006A034E MOV EAX,DWORD PTR DS:[EAX] 006A0350 MOV EDX,DWORD PTR DS:[EAX] 006A0352 CALL DWORD PTR DS:[EDX+D8] ; this create the form which could be a nasty NAG - nop him and you will kill the NAG! 006A0358 MOV EAX,DWORD PTR DS:[6AB76C] ; we land here A strange way to kill the NAG: For some NAGS replace at 006A0352 with the method called when you click Try or Continue ( example: call TReminderForm.TryButtonClick) and now the beach always starts without NAG!

II. Killing hardest NAGS:

For some nags first way (replaing the NAG creation whit nop) wont work so here is the second way of doing it. Do he ReleaseCapture on Olly (ReleaseCapture is from user32.dll) You will now break to user32 code, return from it and you will see: (this is common to all Delphi functions): 00492477 MOV DL,1 00492479 MOV EAX,DWORD PTR DS:[419F78] 0049247E CALL scrwon.0040E2A0 00492483 CALL scrwon.0040427C 00492488 CALL scrwon.00407680 ; JMP to USER32.GetCapture 0049248D TEST EAX,EAX 0049248F JE SHORT scrwon.004924A2 00492491 PUSH 0 00492493 PUSH 0 00492495 PUSH 1F 00492497 CALL scrwon.00407680 ; JMP to USER32.GetCapture 0049249C PUSH EAX 0049249D CALL scrwon.004079A0 ; JMP to USER32.SendMessageA 004924A2 CALL scrwon.00407960 ; JMP to USER32.ReleaseCapture 004924A7 MOV EAX,DWORD PTR DS:[5CCBF0] ; we land here 004924AC CALL scrwon.0049535C 004924B1 XOR EAX,EAX 004924B3 PUSH EBP 004924B4 PUSH scrwon.004926D5 004924B9 PUSH DWORD PTR FS:[EAX] 004924BC MOV DWORD PTR FS:[EAX],ESP 004924BF MOV EAX,DWORD PTR SS:[EBP-4] 004924C2 OR BYTE PTR DS:[EAX+2F4],8 004924C9 CALL scrwon.00407670 ; JMP to USER32.GetActiveWindow Delete the hardware breakpoint and set a hardware breakpoint to the last ret from code and Run with F9. Press Continue on form. You should now break at last ret. Now step over with F8 until we see something like: 005BFAB9 MOV EDX,DWORD PTR DS:[5CBC24] 005BFABF MOV DWORD PTR DS:[EDX],EAX 005BFAC1 MOV EAX,DWORD PTR DS:[5CBC24] 005BFAC6 MOV EAX,DWORD PTR DS:[EAX] 005BFAC8 MOV EDX,DWORD PTR DS:[EAX] 005BFACA CALL DWORD PTR DS:[EDX+EC] ; this create the code 005BFAD0 MOV EAX,DWORD PTR DS:[5CBC24] ; we land here Now restart the application and set hardware breakpoint on execute to 005BFACA. You should now break to 005BFACA step into this call and you will see: .. 0049252B PUSH EBP 0049252C PUSH scrwon.004926B3 00492531 PUSH DWORD PTR FS:[EAX] 00492534 MOV DWORD PTR FS:[EAX],ESP

00492537 0049253A 0049253F 00492541 00492542 00492547 0049254A 0049254D 0049254F 00492551 00492556 00492559 0049255E 0049255F 00492564 00492567 00492569 0049256F 00492574 00492579 0049257E 00492585 00492587 0049258A 00492594 00492596 00492599 004925A0 004925A2 004925A5 004925AA 004925AD 004925B4

MOV EAX,DWORD PTR SS:[EBP-4] CALL scrwon.00492350 XOR EAX,EAX PUSH EBP PUSH scrwon.00492607 PUSH DWORD PTR FS:[EAX] MOV DWORD PTR FS:[EAX],ESP PUSH 0 PUSH 0 PUSH 0B000 MOV EAX,DWORD PTR SS:[EBP-4] CALL scrwon.00476A8C PUSH EAX CALL scrwon.004079A0 ; JMP to USER32.SendMessageA MOV EAX,DWORD PTR SS:[EBP-4] XOR EDX,EDX MOV DWORD PTR DS:[EAX+24C],EDX MOV EAX,DWORD PTR DS:[5CCBF0] CALL scrwon.004964D8 MOV EAX,DWORD PTR DS:[5CCBF0] CMP BYTE PTR DS:[EAX+9C],0 JE SHORT scrwon.00492596 (74 04) MOV EAX,DWORD PTR SS:[EBP-4] MOV DWORD PTR DS:[EAX+24C],2 JMP SHORT scrwon.004925AA MOV EAX,DWORD PTR SS:[EBP-4] CMP DWORD PTR DS:[EAX+24C],0 JE SHORT scrwon.004925AA MOV EAX,DWORD PTR SS:[EBP-4] CALL scrwon.004922A4 MOV EAX,DWORD PTR SS:[EBP-4] CMP DWORD PTR DS:[EAX+24C],0 JE SHORT scrwon.0049256F

This code is a loop which wait for user to press a button, if button Continue is pressed the je from 00492585 will jump and the application will actually run. But this code is used for all forms of application and if you change the je forever you will have a bug. So we must change code at 005BFACA to jump to our code cave. In our code cave we enter: MOV BYTE PTR [00492585],075 ; change je to jne CALL DWORD PTR DS:[EDX+0EC] ; call the changed code MOV BYTE PTR [00492585],074 ; we restore back to je JMP 005BFAD0 ; jump right after NAG creation The problem: The NAG is still showed for some seconds. I tried to find a way to set a form invisible butt I couldnt do. Form1.Hide; Form2.Show;

Form3.Show; get translated into: 0045103C MOV EAX,DWORD PTR DS:[454B74] 00451041 CALL Project1.0044B808 ; Form1.Hide 00451046 MOV EAX,DWORD PTR DS:[453358] 0045104B MOV EAX,DWORD PTR DS:[EAX] 0045104D CALL HideForm.0044B810 ; Form2.Show 00451052 MOV EAX,DWORD PTR DS:[453200] 00451057 MOV EAX,DWORD PTR DS:[EAX] 00451059 CALL Project1.0044B810 ; Form3.Show Under Form1.Hide you will see: 0044B808 XOR EDX,EDX 0044B80A CALL .00447BBC 0044B80F RETN Under Form2.Show/ Form3.Show you will see: 0044B810 PUSH EBX 0044B811 MOV EBX,EAX 0044B813 MOV DL,1 0044B815 MOV EAX,EBX 0044B817 CALL HideForm.00447BBC 0044B81C MOV EAX,EBX 0044B81E CALL Project1.0042DF74 0044B823 POP EBX 0044B824 RETN We set a breakpoint on SetWindowPos and you will fiind: 0044FDC8 PUSH EBP 0044FDC9 MOV EBP,ESP 0044FDCB PUSH EBX 0044FDCC MOV EBX,EAX 0044FDCE MOV EAX,DWORD PTR SS:[EBP+8] 0044FDD1 MOV EAX,DWORD PTR DS:[EAX-4] ; mov in eax contents of DWORD PTR DS:[453B40], now EAX = 00951294 0044FDD4 MOV EAX,DWORD PTR DS:[EAX+30h] ; hwnd pointed by DS:[009512C4] hWnd = 'Project1',class='TApplication' 0044FDD7 PUSH EAX ; hWnd 0044FDD8 CALL JMP.&user32.IsWindowVisible 0044FDDD CMP EAX,1 0044FDE0 SBB EAX,EAX 0044FDE2 INC EAX 0044FDE3 CMP AL,BYTE PTR DS:[451FCC] 0044FDE9 JNZ SHORT .0044FE1E 0044FDEB CMP BL,BYTE PTR DS:[451FCC] 0044FDF1 JE SHORT .0044FE1E 0044FDF3 MOVZX EAX,BL 0044FDF6 MOVZX EAX,WORD PTR DS:[EAX*2+451FD0] ; we patch this to put in eax 97h so the form will be hidden!

0044FDFE PUSH EAX ; Flags = SWP_NOSIZE|SWP_NOMOVE| SWP_NOZORDER|SWP_NOACTIVATE|SWP_HIDEWINDOW (97h) 0044FDFF PUSH 0 ; Height = 0 0044FE01 PUSH 0 ; Width = 0 0044FE03 PUSH 0 ;Y=0 0044FE05 PUSH 0 ;X=0 0044FE07 PUSH 0 ; InsertAfter = HWND_TOP 0044FE09 MOV EAX,DWORD PTR SS:[EBP+8] 0044FE0C MOV EAX,DWORD PTR DS:[EAX-4] ; For Form1 - 00951294 0044FE0F MOV EAX,DWORD PTR DS:[EAX+30] ; handle of Window 0044FE12 PUSH EAX ; hWnd = 001700A2 ('Project1',class='TApplication') 0044FE13 CALL JMP.&user32.SetWindowPos 0044FE18 MOV BYTE PTR DS:[451FCC],BL 0044FE1E POP EBX 0044FE1F POP EBP 0044FE20 RETN When the form is showed SWP_HIDEWINDOW (97h) is replaced by SWP_SHOWWINDOW (value is 57h) Few line down you will see: 0044FE24 PUSH EBP 0044FE25 MOV EBP,ESP 0044FE27 PUSH ECX 0044FE28 PUSH EBX 0044FE29 PUSH ESI 0044FE2A PUSH EDI 0044FE2B MOV DWORD PTR SS:[EBP-4],EAX 0044FE2E MOV EAX,DWORD PTR SS:[EBP-4] 0044FE31 CMP DWORD PTR DS:[EAX+30],0 0044FE35 JE SHORT .0044FEA3 0044FE37 MOV EAX,DWORD PTR DS:[454B44] 0044FE3C CALL .0044CA00 0044FE41 MOV ESI, EAX ; return number of Forms (03) 0044FE43 DEC ESI 0044FE44 TEST ESI,ESI 0044FE46 JL SHORT .0044FE9A 0044FE48 INC ESI 0044FE49 XOR EDI,EDI ; prepare the counter 0044FE4B MOV EDX,EDI ; begin of loop 0044FE4D MOV EAX,DWORD PTR DS:[454B44] 0044FE52 CALL .0044C9EC 0044FE57 MOV EBX,EAX 0044FE59 CMP BYTE PTR DS:[EBX+57],0 ; always 0 when is hidden while when is showed is second time 1 0044FE5D JE SHORT .0044FE96 0044FE5F CMP DWORD PTR DS:[EBX+198],0 0044FE66 JE SHORT .0044FE8B 0044FE68 MOV EAX,EBX

0044FE6A 0044FE6F 0044FE71 0044FE73 0044FE79 0044FE7A 0044FE7C 0044FE81 0044FE82 0044FE87 0044FE89 0044FE8B 0044FE8C 0044FE8E 0044FE93 0044FE94 0044FE96 0044FE97 0044FE98 0044FE9A 0044FE9B 0044FE9D 0044FEA2 0044FEA3 0044FEA4 0044FEA5 0044FEA6 0044FEA7 0044FEA8

CALL Project1.00434E90 TEST AL,AL JE SHORT Project1.0044FE8B MOV EAX,DWORD PTR DS:[EBX+198] PUSH EAX MOV EAX,EBX CALL Project1.00434BBC PUSH EAX ; hParent CALL user32.IsChild TEST EAX,EAX JNZ SHORT.0044FE96 PUSH EBP MOV AL,1 CALL .0044FDC8 ; is called when a Form is showed POP ECX JMP SHORT .0044FEA3 INC EDI DEC ESI JNZ SHORT .0044FE4B PUSH EBP XOR EAX,EAX CALL .0044FDC8 ; is called when a Form is hidden POP ECX POP EDI POP ESI POP EBX POP ECX POP EBP RETN

This is all. I hope you enjoy reading this.

Das könnte Ihnen auch gefallen