-
Level : newbie
Cracking and finding the name to serial algorithm in Chainie1 Crackme
This is my first crackme tutorial: I wrote it to try and help other real newbie’s like me to better understand the simple basics of cracking. Although this is supposed to be a very simple crackme I found it quite hard to understand what was really going on and very nearly fell into a little trap of thinking I had found the name to serial algorithm too soon.
Tools: PeID, OllyDbg 1.10 and a pencil and paper
Target:
کد:
http://www.cracklab.ru/crackme/chainie1.rar
Well where do we begin? First I check the Chainie.exe with PeID. We see that this crackme is not packed and was created with Borland Delphi 6.0 – 7.0 so hopefully we won’t have any nasty surprises like anti-debug or other tricks.
Ok let’s have a look at our target; double click Chainie.exe and a dialog box asking for a name and a serial will appear.
Enter name LucyE and enter serial 47806 and press Check.
Bam a message box appears saying, ‘Your name is too short’
Press OK and close Chainie.exe. Then right click on Chainie.exe and open with Olly.
In Olly right click and select Search for all referenced text strings. You will see this:
Text strings referenced in Chainie: CODE
Address Disassembly Text string
………………………………………....S nip……………………………………… …
0045012F ASCII "Unit1"
004501F5 PUSH Chainie.0045027C ASCII "Correct"
004501FA PUSH Chainie.00450284 ASCII "Good job you did it!!!"
00450213 PUSH Chainie.0045029C ASCII "WRONG!!!"
00450218 PUSH Chainie.004502A8 ASCII "Incorrect Serial try again...." (2)
0045022F PUSH 10 (Initial CPU selection)
00450231 PUSH Chainie.004502C8 ASCII "Oops...”
00450236 PUSH Chainie.004502D0 ASCII "Your name is too short" (1)
………………………………………....S nip……………………………………… …
I didn’t want to guess how long may name should be so I decided to double click on (1) and see if I could fix this problem first and then come back and double click on (2) to see what I could see and learn from that.
After double clicking we land here at place marked (1)
0045022F > 6A 10 PUSH 10 ß---------------------------- ; Entry point to MessageBox function
00450231 . 68 C8024500 PUSH Chainie.004502C8 ; ASCII "Oops.."
(1) 00450236 68 D0024500 PUSH Chainie.004502D0 ; ASCII "Your name is too short" 0045023B . A1 D43B4500 MOV EAX, DWORD PTR DS:[453BD4]
00450240 . E8 EF58FEFF CALL Chainie.00435B34
00450245 . 50 PUSH EAX ; |hOwner = NULL
00450246 . E8 5562FBFF CALL ; MessageBoxA
0045024B > 33C0 XOR EAX, EAX
To find out where the check that called this function right click on the entry point at 0045022F and select ‘Go to JLE from 0045017C’ and we land here:
00450179 83F8 05 CMP EAX, 5 ; is name length less than 6 letters?
0045017C 0F8E AD000000 JLE Chainie.0045022F If yes then Jump to Bad Name
There are probably many ways to fix this name check like NOP the JLE, but I like the idea of entering a name so I will patch this by changing the 5 to 1. This means that your name must have more than 2 letters to be valid. We do this by clicking on the location 00450179 pressing the Space bar, change the 5 to 1, then choose assemble followed by cancel. The code will now look like this:
00450179 83F8 01 CMP EAX, 1
0045017C 0F8E AD000000 JLE Chainie.0045022F
Now the name length check has been patched our next job is to find the correct serial for our name.
Looking at the code snippet below we see that at 004501F1 there is a check which if we fail we go to the “Good job you did it!!!” message. This tells me that there is a check for a valid serial in the Call on the line above that returns with a non-zero value if our serial is valid. Therefore, put a breakpoint (press F2) on the Call at 004501EC and Press F9 to run the crackme. Enter LucyE for the name and 47806 as the serial then Check to find out if the serial we entered is legitimate.
004501EC . E8 2B40FBFF CALL Chainie.0040421C ß----------we break here
004501F1 . 75 1E JNZ SHORT Chainie.00450211
004501F3 . 6A 30 PUSH 30
004501F5 . 68 7C024500 PUSH Chainie.0045027C ; ASCII "Correct"
004501FA . 68 84024500 PUSH Chainie.00450284 ; ASCII "Good job you did it!!!"
004501FF . A1 D43B4500 MOV EAX, DWORD PTR DS:[453BD4]
00450204 . E8 2B59FEFF CALL Chainie.00435B34
00450209 . 50 PUSH EAX ; |hOwner = 00E924E8
0045020A . E8 9162FBFF CALL ; MessageBoxA
0045020F . EB 3A JMP SHORT Chainie.0045024B
Hey look at the memory dump (stack) what do you see?
0012F604 00E924E8 ASCII "48706" ; the serial we entered
0012F608 0012F968 Pointer to next SEH record
0012F60C 0045026E SE handler
0012F610 0012F638
0012F614 0012F7B4
0012F618 00428FD0 Chainie.00428FD0
0012F61C 00E936A8
0012F620 00E924FC ASCII "3443972" ; maybe a valid serial
0012F624 00E924E8 ASCII "48706" ; the serial we entered
0012F628 00E924D4 ASCII "LucyE"
0012F62C 00E924C0 ASCII "LucyE" ; the name we entered
0012F630 00E924AC ASCII "LucyE"
0012F634 00000006
Let’s be brave and remove our breakpoint then hit F9 to run Chainie.exe.
After clicking OK at the ‘Incorrect serial try again…’ message enter this number (3443972) as the serial and press check. BINGO! We cracked it!
Our next job is to find out how this programme calculates the serial for our name.
Press Alt-E to view the Executable Modules then right click on Chanie.exe and select view names. Scroll down the list a bit and double click on the LoadStringA API then press F2 to set a breakpoint on the line highlighted in Olly at 00450152. Run Chainie.exe and press check; we break in Olly at 00450152.
00450152 . 55 PUSH EBP ß----------------------- We Break Here
00450153 . 68 6E024500 PUSH Chainie.0045026E
00450158 . 64:FF30 PUSH DWORD PTR FS:[EAX]
0045015B . 64:8920 MOV DWORD PTR FS:[EAX],ESP
0045015E . BB DBC10800 MOV EBX, 8C1DB
00450163 . 8D55 F8 LEA EDX, DWORD PTR SS:[EBP-8]
00450166 . 8B87 08030000 MOV EAX, DWORD PTR DS:[EDI+308]
0045016C . E8 E7F1FDFF CALL Chainie.0042F358
00450171 . 8B45 F8 MOV EAX, DWORD PTR SS:[EBP-8] ; name put in location EBP-8
00450174 . E8 573FFBFF CALL Chainie.004040D0 ; Length of Name = 5
00450179 83F8 05 CMP EAX, 5
Trace through the code with F8 but STOP when you reach the Call at 0045C01E3. I have commented the code to try and explain how our name is converted into a hex number.
00450182 . 8D55 F4 LEA EDX, DWORD PTR SS: [EBP-C]
00450185 . 8B87 08030000 MOV EAX, DWORD PTR DS: [EDI+308]
0045018B . E8 C8F1FDFF CALL Chainie.0042F358
00450190 . 8B45 F4 MOV EAX, DWORD PTR SS:[EBP-C] ; Name (LucyE)
00450193 . E8 383FFBFF CALL Chainie.004040D0
00450198 . 8BF0 MOV ESI, EAX ; Length of Name = 5
0045019A . 85F6 TEST ESI, ESI
0045019C . 7E 2E JLE SHORT Chainie.004501CC
0045019E . C745 FC 01000> MOV DWORD PTR SS:[EBP-4],1
004501A5 > 8D55 F0 LEA EDX,DWORD PTR SS:[EBP-10]
004501A8 . 8B87 08030000 MOV EAX, DWORD PTR DS:[EDI+308]
004501AE . E8 A5F1FDFF CALL Chainie.0042F358
004501B3 . 8B45 F0 MOV EAX, DWORD PTR SS:[EBP-10] ; Name in EAX (LucyE)
004501B6 . 8B55 FC MOV EDX, DWORD PTR SS:[EBP-4] ; 1 in EDX
004501B9 . 0FB64410 FF MOVZX EAX, BYTE PTR DS:[EAX+EDX-1] ; letter into EAX
004501BE . 03D8 ADD EBX, EAX ; Add letter to EBX (init EBX=8C1DB)
004501C0 . 81C3 DBC10800 ADD EBX, 8C1DB ; Add 8C1DB to EBX
004501C6 . FF45 FC INC DWORD PTR SS: [EBP-4] ; inc ss: [EBP-4] to get next letter
004501C9 . 4E DEC ESI ; Decrease name counter by 1
004501CA . ^ 75 D9 JNZ SHORT Chainie.004501A5 ; Get next letter until none left
004501CC > 8D55 EC LEA EDX, DWORD PTR SS:[EBP-14]
004501CF . 8B87 10030000 MOV EAX, DWORD PTR DS:[EDI+310]
004501D5 . E8 7EF1FDFF CALL Chainie.0042F358
004501DA . 8B45 EC MOV EAX, DWORD PTR SS:[EBP-14] ; Serial we entered
004501DD . 50 PUSH EAX ; Notice both serial and name entered are on the stack
004501DE . 8D55 E8 LEA EDX, DWORD PTR SS:[EBP-18]
004501E1 . 8BC3 MOV EAX, EBX ; result of manipulating our name in EAX
004501E3 . E8 6879FBFF CALL Chainie.00407B50 ; STOP HERE
The name LucyE is converted by the above code as follows:
L u c y E
8C1DB+4C+8C1DB+75+8C1DB+63+8C1DB+79+8C1DB+45+8C1DB = 348D04
Make a note or remember that EAX = 00348D04
You might think that this is the end of the name to serial algorithm but it’s not! So we will now continue from where we stopped above by pressing F7 to trace into the Call at 004501E3 until we reach here:
00407B04 /$ 08C9 OR CL, CL if result is not zero
00407B06 | 75 17 JNZ SHORT Chainie.00407B1F then Jump
00407B08 | 09C0 OR EAX, EAX if result is not signed
00407B0A | 79 0E JNS SHORT Chainie.00407B1A then Jump
…………….If both above checks fail algorithm is completed in this section…………….
00407B0C | F7D8 NEG EAX makes value in EAX positive
00407B0E | E8 07000000 CALL Chainie.00407B1A
00407B13 | B0 2D MOV AL, 2D
00407B15 | 41 INC ECX
00407B16 | 4E DEC ESI
00407B17 | 8806 MOV BYTE PTR DS: [ESI], AL ; adds value in AL to our serial
00407B19 | C3 RETN
…………………..…………Second part of algorithm………………………………
00407B1A |$ B9 0A000000 MOV ECX,0A ; Put the divisor x0A (10) in ECX
00407B1F |> 52 PUSH EDX
00407B20 | 56 PUSH ESI
00407B21 |> 31D2 /XOR EDX,EDX ; Zero EDX
00407B23 | F7F1 |DIV ECX ; Divide EAX by 0A Result in EAX remainder in EDX
00407B25 | 4E |DEC ESI ; Make room for next number of correct Serial
00407B26 | 80C2 30 |ADD DL, 30 ; Add 30 to low byte in EDX
00407B29 | 80FA 3A |CMP DL, 3A ; if DL is less than 3A
00407B2C | 72 03 |JB SHORT Chainie.00407B31 ; then Jump
00407B2E | 80C2 07 |ADD DL, 7
00407B31 |> 8816 |MOV BYTE PTR DS:[ESI],DL ; the correct serial is built here
00407B33 | 09C0 |OR EAX, EAX
00407B35 |^ 75 EA JNZ SHORT Chainie.00407B21 ; Get next number until EAX is zero
00407B37 | 59 POP ECX ; Chainie.00407B60
00407B38 | 5A POP EDX ; Chainie.00407B60
00407B39 | 29F1 SUB ECX, ESI ; the length of the correct serial
00407B3B | 29CA SUB EDX, ECX ; if EDX is bigger than ECX
00407B3D | 76 10 JBE SHORT Chainie.00407B4F ; then Jump
00407B3F | 01D1 ADD ECX, EDX
00407B41 | B0 30 MOV AL, 30
00407B43 | 29D6 SUB ESI, EDX ; Length of serial
00407B45 | EB 03 JMP SHORT Chainie.00407B4A
00407B47 |> 880432 /MOV BYTE PTR DS:[EDX+ESI],AL
00407B4A > 4A DEC EDX
00407B4B |^ 75 FA JNZ SHORT Chainie.00407B47 ; loop until EDX is zero
00407B4D | 8806 MOV BYTE PTR DS:[ESI],AL ; add AL i.e. 30 to our serial
00407B4F > C3 RETN
STOP pressing F7 when you reach RETN at 0407BF4 and note that ESI=3443972
Do you remember the number calculated from our name in the first part of the algorithm and do you recall where it was stored? Yes EAX = 348D04. Well to calculate the real valid serial this number is divided step by step by 0A at location 00407B23 until EAX is zero.
At each step the remainder of this integer division, if it is less than 0A (i.e. a single digit), is stored in ESI.
If the remainder is greater than 0A then 07 is added to DL before it is stored as a single digit in ESI.
If you recollect the serial for LucyE, was 344392. So in my case the first remainder was 2, the second 9, the third 3 and so on.
Press F8 seven times and look at the stack, at the bottom right of your screen you will see what we saw earlier, the name we entered, the serial we entered and the correct serial computed from our name. Also note that EDX = 3443972
So what have we learned?
1. How to check if a target is packed
2. How to patch a name length check
4 How to fish for a valid serial
5. How to find and understand the code that converts our name into a valid serial
6. Go slowly and keep our eye on what is happening with the registers and what is showing in the memory dump.
-
Level : beginner
----------------------------------------------------------------------------------------
Thinstall 2.521 - unpacking dependencies and injecting a DLL
---------------------------------------------------------------------------------------
Intro, hmm.... what to say for intruduction? I wrote already one tutorial for Thinstall 2.521. There I explained how to unpack target protected with DebugBlocker feature but Thinstall is not packer/protector with some strong anti-debug layer. Thinstall is bundler which is able to pack all kinds of files into one executable file. What hard at Thinstall is, to unpack target that has dependencies - bundled files into one exe. In this tutorial we will try to unpack one file that have such dependencies. It is Teddy Rogers UnPackMe_Thinstall2.521.f.exe which contains three dependencies.
1. Tools that we will need
- OllyDbg 1.10
- LordPE
- some hex editor
2. Few words before we start
In this tutorial I will not explain how to reach OEP and how to fix IAT. It is all well explained in first Thinstall tutorial. For finding OEP and to speed-up a little I wrote one script for OllyScript plugin. That script will find OEP, kill DebugBlocker, and it will kill splash bitmap at the start. Bitamp is killed by patching CreateThread API and restoring back after we reach OEP. Finding OEP is easy, just like fixing IAT. As I sad, it is already explained in first tutorial.
In case of dependencies, we cannot just restore back old good IAT. If we would just put it back, target couldn't extract dependencies when it needs them. So we would get just main executable without needed files. Thisntall doesn't exctract that file on hard drive. Instead, it places them directly to memory. Dependencies , or such virtual files, can be any type of file and number of such files is not limited. Now we have problem: How to get working unpacked target? There is 4 theoretical solutions:
- Unpack main executable and extract all virtual files to hard drive. This can be veeery hard.
- Inline patch target. Good and easy solution.
- Code loader to patch target in memory to crack application.
- Partialy unpack target, attach all blocks to main dump so protection is removed but executable is still able to extract needed files in memory. This sounds like SF but it's probably possible.
In this tutorial we will use first way. That is actually the most hardest one because target can have large number of files packed within. For example, what if our target have 100 DLL's packed inside, 100 .txt files, or if it is some game that can have thousands of files packed? Now you see the problem. We could spend days or weeks extracting those files. Ofcourse, better examning inside Thinstall work could result a smart unpacker tool that would be able to extract all files.
As said before, we will unpack Teddy's unpackme which has three dependencies. It will not be too hard. Those dependencies are three DLL's that are loaded in memory after OEP is reached. First that I noticed is, that target can be just patched after LoadLibraryA and unpackme will run fine cracked without need of those DLL's. But that is lame thing because our target is dummy application that doesn't needs those files. In case that we have "reall" app, our file would just crush.
3. OEP , API's and main dump
Run Olly, load UnPackMe_Thinstall2.521.f.exe in it and use my script to find OEP. Fix imports like I described it in first tutorial (second way in that tutorial is better and cleaner, it is even easier). And you will have main dump. So this is OEP:
004271B0 . 55 PUSH EBP
004271B1 . 8BEC MOV EBP,ESP
004271B3 . 6A FF PUSH -1
004271B5 . 68 600E4500 PUSH UnPackMe.00450E60
004271BA . 68 C8924200 PUSH UnPackMe.004292C8 ; SE handler installation
004271BF . 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
004271C5 . 50 PUSH EAX
004271C6 . 64:8925 000000>MOV DWORD PTR FS:[0],ESP
004271CD . 83C4 A8 ADD ESP,-58
004271D0 . 53 PUSH EBX
004271D1 . 56 PUSH ESI ; <&KERNEL32.GetModuleFileNameA>
004271D2 . 57 PUSH EDI ; <&KERNEL32.GetModuleFileNameA>
004271D3 . 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
004271D6 . FF15 DC0A4600 CALL DWORD PTR DS:[460ADC] ; kernel32.GetVersion
And here is our target wants DLL's:
00409FBF |. 53 PUSH EBX ;Pointer to DLL name
00409FC0 |. FF15 3C0B4600 CALL DWORD PTR DS:[460B3C] ;Thinstall LoadLibraryA code
00409FC6 |. 85C0 TEST EAX,EAX ;EAX = base of loaded DLL
Thinstall substitutes some API's with own code. API's that are replaced are all those what executable uses for accessing outside files and libraries. So all LoadLibararyA, LoadLibraryW, CreateFileA, ReadFile, etc... are replaced with Thinstall functions. If our file wants to open some file, Thisntall will check is that file bundled as a virtual file. It just compares name of that file with internal list. If file is bundled, it will allocate block for it, extract it and return it's handle to main exe. If file is not bundled, then it will use acctuall API to open that file.
In case of our DLL, Thinstall will extract it to ome virtual block, then fill it with imports and return it's block base as module handle. EBX points to DLL name, CALL calls Thinstall LoadLibraryA code, and EAX will return module base of such DLL in memory.
There are several problems that we have while traying to dump these DLL's. Dumping is easy. We use LordPE to dump that memory block. But we need to repair those DLL's and that is interesting part.
4. Dumping first DLL
Try to run dump and you will get error message:
An error has occured
Line number: 1
Please contact the program vendor
53
First DLL is very easy to dump. We load UnPackMe_Thinstall2.521.f.exe in Olly, then we find OEP with script. We place breakpoint on 409fbf line and we just run. In registeres window we can see that target wants "updatechecker_english" file to load as library. We place second breakpoint below API call, at the line 409fc6 to see EAX value. At my computer EAX=10000000 and that is base of first DLL. Now we just use LordPE to dump memory range at 10000000, size 3000. Note that this library will probably be loaded at different base. Not important, just dump your memory block and save it as updatechecker_english.dll. It can be without .dll extension.
We can check now our first dumped dll withth LordPE. You will see that this is just resource dll with two sections. Dll can be loaded in memory and we dumped first file, but if our target is not just dummy one, we still would have problem. It is because file on disk have image alignment (like in memory) but in PE header is information for file alignment. So just set these values:
FileAlignment: 00001000
Sections info:
Name VOffset VSize ROffset RSize Flags
.rsrc 1000 1000 1000 1000 E00000E0
.reloc 2000 1000 2000 1000 E20000E0
Now check resources info and you will see that it is ok. That is all, this dll doesn't have imports or exports.
5. Dumping second DLL
If we run now, we get another error message:
An error has occured
Line number: 2
Please contact the program vendor
53
We need to extract second DLL, which is called at the same place. It is updatechecker_german and it's base address on my machine is 03320000, size 0000E000. But this DLL have imports and exports. We can dump it like first one, but problem would be to repair imports since ImpREC cannot attach to this "virtual DLL". We could just dump it, then fix header info, load it in olly, load needed DLL's somehow and then use ImpREC for fixing. But it can be much easier. Also more interesting too. We restart target , find OEP again and then place two breakpoints at our old spot. We run to break when second dll is wanted, ei. when we see "updatechecker_german" in registeres window.
Then we place bp on VirtualAlloc. We know that dll will be loaded on 03320000 address (in my case) so we will wait untill Thinstall allocates that block. So Run untill you can follow in dump that address. When you can follow in dump, place memory breakpoint on first couple bytes, then run again. Thinstall has allocated this block for PE header. Then it will allocate next at 03321000 for code section, etc... You should brek at part where it writes to blocks:
00A0AE93 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
00A0AE95 FF2495 A8AFA000 JMP DWORD PTR DS:[EDX*4+A0AFA8]
Second line is good for breakpoint because we controll writing to blocks. As said, first block is for PE header. When it breaks second time at VirtualAlloc, PE header is written. Check that block:
03320000 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 MZ..............
03320010 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@.......
...cut to reduce snippet
033200E0 00 00 00 00 00 00 00 00 50 45 00 00 4C 01 03 00 ........PE..L...
033200F0 84 B2 75 3C 00 00 00 00 00 00 00 00 E0 00 0E 21 ..u<...........!
03320100 0B 01 06 00 00 40 00 00 00 70 00 00 00 00 00 00 .....@...p......
03320110 E9 10 00 00 00 10 00 00 00 50 00 00 00 00 00 10 .........P......
03320120 00 10 00 00 00 02 00 00 04 00 00 00 00 00 00 00 ................
03320130 04 00 00 00 00 00 00 00 00 E0 00 00 00 04 00 00 ................
03320140 A2 7C 00 00 02 00 00 00 00 00 10 00 00 10 00 00 .|..............
03320150 00 00 10 00 00 10 00 00 00 00 00 00 10 00 00 00 ................
03320160 00 00 00 00 00 00 00 00 14 C1 00 00 8F 00 00 00 ................
03320170 00 C0 00 00 E8 00 00 00 00 00 00 00 00 00 00 00 ................
03320180 00 00 00 00 00 00 00 00 00 D0 00 00 18 00 00 00 ................
...cut to reduce snippet
033201E0 2E 74 65 78 74 00 00 00 00 B0 00 00 00 10 00 00 .text...........
033201F0 00 36 00 00 00 04 00 00 50 45 43 32 00 00 00 00 .6......PEC2....
03320200 00 00 00 00 20 00 00 E0 00 00 00 00 00 00 00 00 .... ...........
03320210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
...
Select PE header view:
03320000 4D 5A ASCII "MZ" ; DOS EXE Signature
03320002 9000 DW 0090 ; DOS_PartPag = 90 (144.)
03320004 0300 DW 0003 ; DOS_PageCnt = 3
03320006 0000 DW 0000 ; DOS_ReloCnt = 0
03320008 0400 DW 0004 ; DOS_HdrSize = 4
0332000A 0000 DW 0000 ; DOS_MinMem = 0
0332000C FFFF DW FFFF ; DOS_MaxMem = FFFF (65535.)
0332000E 0000 DW 0000 ; DOS_ReloSS = 0
03320010 B800 DW 00B8 ; DOS_ExeSP = B8
03320012 0000 DW 0000 ; DOS_ChkSum = 0
03320014 0000 DW 0000 ; DOS_ExeIP = 0
03320016 0000 DW 0000 ; DOS_ReloCS = 0
03320018 4000 DW 0040 ; DOS_TablOff = 40
0332001A 0000 DW 0000 ; DOS_Overlay = 0
...
Now we can see information about that DLL. Interesting for us is:
03320110 E9100000 DD 000010E9 ; AddressOfEntryPoint = 10E9
...
03320168 14C10000 DD 0000C114 ; Import Table address = C114
0332016C 8F000000 DD 0000008F ; Import Table size = 8F (143.)
We now know where OEP is and where is import table. Import table is the most important thing because that is last data that will be written before jumping to OEP. So I placed memory breakpoint at 0332C114 and size 8F (ofcourse, after another block is allocated for that part of image). After breaking there:
0332C100 4C C1 00 00 5C C1 00 00 70 C1 00 00 80 C1 00 00 L......p.......
0332C110 00 00 00 00 00 C1 00 00 00 00 00 00 FF FF FF FF ................
0332C120 3C C1 00 00 00 C1 00 00 00 00 00 00 00 00 00 00 <...............
0332C130 00 00 00 00 00 00 00 00 00 00 00 00 6B 65 72 6E ............kern
0332C140 65 6C 33 32 2E 64 6C 6C 00 00 00 00 00 00 4C 6F el32.dll......Lo
0332C150 61 64 4C 69 62 72 61 72 79 41 00 00 00 00 47 65 adLibraryA....Ge
0332C160 74 50 72 6F 63 41 64 64 72 65 73 73 00 00 00 00 tProcAddress....
0332C170 00 00 56 69 72 74 75 61 6C 41 6C 6C 6F 63 00 00 ..VirtualAlloc..
0332C180 00 00 56 69 72 74 75 61 6C 46 72 65 65 00 00 EB ..VirtualFree...
0332C190 89 2C FF D0 0C C9 6F 06 23 00 0B 04 4E 70 5E A0 .,....o.#...Np^.
0332C1A0 67 C7 FE FF FF 89 45 0C 17 5E 87 99 37 57 50 5F g.....E..^..7WP_
Data from 0332C100 to 0332C114 are thunks. They will be filled last. I bynary copied all that data for later. Now go and check OEP=03320000+10E9. It looks like junk:
033210E9 B8 B0C90010 MOV EAX,1000C9B0
033210EE 50 PUSH EAX
033210EF 64:FF35 00000000 PUSH DWORD PTR FS:[0]
033210F6 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
033210FD 33C0 XOR EAX,EAX
033210FF 8908 MOV DWORD PTR DS:[EAX],ECX
03321101 50 PUSH EAX
03321102 45 INC EBP
03321103 43 INC EBX
03321104 6F OUTS DX,DWORD PTR ES:[EDI]
03321105 6D INS DWORD PTR ES:[EDI],DX
03321106 70 61 JO SHORT 03321169
03321108 637432 00 ARPL WORD PTR DS:[EDX+ESI],SI
0332110C 1F POP DS
0332110D F6B9 78F42226 IDIV BYTE PTR DS:[ECX+2622F478]
03321113 0321 ADD ESP,DWORD PTR DS:[ECX]
03321115 DB847F 42837D74 FILD DWORD PTR DS:[EDI+EDI*2+747D8342]
0332111C 112CF9 ADC DWORD PTR DS:[ECX+EDI*8],EBP
0332111F DB51 08 FIST DWORD PTR DS:[ECX+8]
03321122 8B6CE4 36 MOV EBP,DWORD PTR SS:[ESP+36]
03321126 205F 5E AND BYTE PTR DS:[EDI+5E],BL
03321129 5B POP EBX
0332112A 5D POP EBP
0332112B A1 44F1A23D MOV EAX,DWORD PTR DS:[3DA2F144]
03321130 8B740D 1B MOV ESI,DWORD PTR SS:[EBP+ECX+1B]
03321134 79 5C JNS SHORT 03321192
03321136 9B WAIT
03321137 0E PUSH CS
03321138 48 DEC EAX
Don't worry, it is not junk ;-) I will tell you later why, just place bp on first line. Run to break there and when you break, remove bp. Then paste back that thunks and dump whole block with LordPE. Save it as updatechecker_german.dll. Dump is good, but needs some PE header correction just like when I was fixing first DLL. Set FileAlignment=1000, set sections values on 1000 round numbers (if section size is 1234, set it to first bigger - 2000), RawOffset's set to be same as virtual. Now run main dump and it will gave you:
An error has occured
Line number: 3
Please contact the program vendor
53
That means our second DLL is repaired.
Ok, but why OEP of that dll looks like junk? It is because that dll was first packed with PECompact :-). Teddy probably packed dll first with PECompact. Or maybe Thinstall uses PECompact compression libraries? I don't know, but DLL is good even if it is still packed with PECompact. Even better, it was easier unpacking (less imports to resolve). But if you want, you can unpack PECompact to get totaly unpacked dlls.
6. Dumping 3. DLL
Third DLL is also packed by PECompact first so unpacking is same. Block is at 03350000 (on my machine), OEP=10E9,IAT=C114. For some reason, IAT was little screwed so I fixed it manually. And now target runs ok :-)
- The End of first part -
7. Inline patching
What the hell, why do not show this solution too, when it's so easy.
Idea is, to inject code that will load one DLL which will then patch application during runtime, just like loader. Since target is packed, there are not empty space inside exe to inject enough code for creating inline patch. We cannot add new sections because that would corrupt executable. PE header doesn't have any empty space for that too. But Thinstall doesn't have PE Header integrity check so we are allowed to change PE header a bit. It also doesn't check it's own code. This is PE header:
00400000 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 MZ..............
00400010 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@.......
00400020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00400030 00 00 00 00 00 00 00 00 00 00 00 00 D0 00 00 00 ................
00400040 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 ........!..L.!Th
00400050 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F is program canno
00400060 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 t be run in DOS
00400070 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 mode....$.......
00400080 75 92 03 D8 31 F3 6D 8B 31 F3 6D 8B 31 F3 6D 8B u...1.m.1.m.1.m.
00400090 31 F3 6C 8B 25 F3 6D 8B 53 EC 7E 8B 34 F3 6D 8B 1.l.%.m.S.~.4.m.
004000A0 37 D0 7B 8B 33 F3 6D 8B 65 D0 5C 8B 30 F3 6D 8B 7.{.3.m.e..0.m.
004000B0 52 69 63 68 31 F3 6D 8B 00 00 00 00 00 00 00 00 Rich1.m.........
004000C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004000D0 50 45 00 00 4C 01 01 00 40 34 00 42 00 00 00 00 PE..L...@4.B....
004000E0 00 00 00 00 E0 00 0E 01 0B 01 06 00 00 92 04 00 ................
004000F0 00 00 00 00 00 00 00 00 94 1A 00 00 00 10 00 00 ................
00400100 00 B0 04 00 00 00 40 00 00 10 00 00 00 02 00 00 ......@.........
00400110 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 ................
00400120 00 B0 06 00 00 02 00 00 00 00 00 00 02 00 00 00 ................
00400130 00 00 10 00 00 10 00 00 00 00 10 00 00 10 00 00 ................
00400140 D7 F2 06 00 10 00 00 00 00 00 00 00 00 00 00 00 ................
00400150 90 5B 00 00 D8 02 00 00 70 5E 00 00 80 77 00 00 .[......p^...w..
00400160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00400170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00400180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00400190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004001A0 00 00 00 00 00 00 00 00 B0 53 00 00 58 00 00 00 .........S..X...
004001B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004001C0 00 00 00 00 00 00 00 00 2E 74 65 78 74 20 20 20 .........text
004001D0 00 A0 06 00 00 10 00 00 F0 C5 00 00 00 02 00 00 ................
004001E0 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 E0 ............ ...
I reduced "This program cannot be run in DOS mode" to "X" , ended it by 0D 0D 0A 24 00, and then I filled all that space to PE header signature with 90:
00400000 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 MZ..............
00400010 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@.......
00400020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00400030 00 00 00 00 00 00 00 00 00 00 00 00 D0 00 00 00 ................
00400040 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 58 0D ........!..L.!X.
00400050 0D 0A 24 00 90 90 90 90 90 90 90 90 90 90 90 90 ..$.............
00400060 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
00400070 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
00400080 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
00400090 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
004000A0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
004000B0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
004000C0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
004000D0 50 45 00 00 4C 01 01 00 40 34 00 42 00 00 00 00 PE..L...@4.B....
004000E0 00 00 00 00 E0 00 0E 01 0B 01 06 00 00 92 04 00 ................
004000F0 00 00 00 00 00 00 00 00 94 1A 00 00 00 10 00 00 ................
00400100 00 B0 04 00 00 00 40 00 00 10 00 00 00 02 00 00 ......@.........
00400110 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 ................
00400120 00 B0 06 00 00 02 00 00 00 00 00 00 02 00 00 00 ................
00400130 00 00 10 00 00 10 00 00 00 00 10 00 00 10 00 00 ................
00400140 D7 F2 06 00 10 00 00 00 00 00 00 00 00 00 00 00 ................
00400150 90 5B 00 00 D8 02 00 00 70 5E 00 00 80 77 00 00 .[......p^...w..
00400160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00400170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00400180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00400190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004001A0 00 00 00 00 00 00 00 00 B0 53 00 00 58 00 00 00 .........S..X...
004001B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004001C0 00 00 00 00 00 00 00 00 2E 74 65 78 74 20 20 20 .........text
004001D0 00 A0 06 00 00 10 00 00 F0 C5 00 00 00 02 00 00 ................
004001E0 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 E0 ............ ...
That place is enough to inject some code that will load one DLL.
Thinstall first loads it's own loader and then it jumps to it. It uses JMP EAX right above EP:
00401A8D . FFE0 JMP EAX ; Jump to EntryPoint of Thinstall loader.
00401A8F . 5F POP EDI
00401A90 . 5E POP ESI
00401A91 . 5B POP EBX
00401A92 . C9 LEAVE
00401A93 . C3 RETN
00401A94 > $ 55 PUSH EBP ; EP of Thinstall target.
00401A95 . 8BEC MOV EBP,ESP
00401A97 . B8 DA00B39C MOV EAX,9CB300DA
00401A9C . BB 597B1C10 MOV EBX,101C7B59
00401AA1 . 50 PUSH EAX
00401AA2 . E8 00000000 CALL UnPackMe.00401AA7
I redirected that jump (code below jump is useless) to PE Header cave:
00401A8D . 60 PUSHAD ;Save registeres.
00401A8E .-E9 CBE5FFFF JMP injected.0040005E ;Jump to injected code.
00401A93 . C3 RETN
00401A94 > $ 55 PUSH EBP
00401A95 . 8BEC MOV EBP,ESP
00401A97 . B8 DA00B39C MOV EAX,9CB300DA
00401A9C . BB 597B1C10 MOV EBX,101C7B59
00401AA1 . 50 PUSH EAX
00401AA2 . E8 00000000 CALL injected.00401AA7
And inside PE header I loaded my DLL:
0040005E 68 54004000 PUSH injected.00400054 ; ASCII "X.DLL"
00400063 FF15 D0534000 CALL DWORD PTR DS:[<&KERNEL32.LoadLibraryA>] ; kernel32.LoadLibraryA
00400069 05 00100000 ADD EAX,1000
0040006E -FFE0 JMP EAX
0040004E 58 0D 0D 0A 24 00 58 2E 44 4C 4C 00 90 90 90 90 X...$.X.DLL.....
0040005E 68 54 00 40 00 FF 15 D0 53 40 00 05 00 10 00 00 hT.@....S@......
I called my dll X.DLL (short name because small free space). Below it I placed injected code which loads X.DLL in memory using LoadLibraryA API. I pointed call to Thinstall it's own IAT since it uses this API. I didn't code DLL, instead I just copied that "updatechecker_english.dll". I erased everything out of it and Injected few lines of code in it. It is easier than code some DLL.
So process is this: Thinstall unpacks it's own loader and then it uses JMP EAX to jump in it. I patched that jump to PE Header (before jump I used PUSHAD to save registeres) where my X.DLL is loaded. Then it jumps into my dll. Inside my dll, it restores original registers first:
10001000 61 POPAD ;Restore registers back.
10001001 60 PUSHAD ;Then save them again.
10001002 8BC8 MOV ECX,EAX ;ECX=EAX= EP of thinstall loader.
10001004 81E9 A98E0300 SUB ECX,38EA9 ;Difference to part where it calls target's OEP.
1000100A C601 68 MOV BYTE PTR DS:[ECX],68 ;I will redirect that with PUSH xxxxxxxx.
1000100D 41 INC ECX
1000100E E8 00000000 CALL 10001013 ;This trick retrieves current offset
10001013 5A POP EDX ;to EDX.
10001014 83C2 2D ADD EDX,2D ;EDX will point little further (to empty space).
10001017 8911 MOV DWORD PTR DS:[ECX],EDX ;PUSH that_empty_space_in_my_dll
10001019 83C1 04 ADD ECX,4
1000101C C601 C3 MOV BYTE PTR DS:[ECX],0C3 ;And I'm placing RETN that will redirect flow.
1000101F 61 POPAD ;Restore registers.
10001020 ^FFE0 JMP EAX ;And now it jumps to Thinstall loader EP.
10001022 90 NOP
10001023 90 NOP
10001024 90 NOP
10001025 90 NOP
This code has changed OEP call to redirection in my dll. Before:
7FF52C4B FF95 48FCFFFF CALL DWORD PTR SS:[EBP-3B8]
and after my code has changed it:
7FF52C4B 68 40100010 PUSH 10001040
7FF52C50 C3 RETN
Then, Thinstall will run normally. It will do all unpacking, it will check for debugger presence, it will even use DebugBlocker. So it will be two processes and both processes will use my dll, but only second process will jump to last part of my dll code which is final patch that patches target. Since this is just simple app and there is nothing to patch, I will just change text in message box. In my dll I placed new text:
10002000 54 68 69 73 20 69 73 20 61 20 54 68 69 6E 73 74 This is a Thinst
10002010 61 6C 6C 20 32 2E 35 32 31 20 55 6E 50 61 63 6B all 2.521 UnPack
10002020 4D 45 0D 28 49 74 20 63 6F 6E 74 61 69 6E 73 20 ME.(It contains
10002030 33 20 64 65 70 65 6E 64 65 6E 63 69 65 73 29 0D 3 dependencies).
10002040 0D 41 6C 6C 20 50 72 6F 74 65 63 74 69 6F 6E 73 .All Protections
10002050 20 45 6E 61 62 6C 65 64 20 2B 20 53 70 6C 61 73 Enabled + Splas
10002060 68 20 53 63 72 65 65 6E 20 2B 20 4D 61 78 69 6D h Screen + Maxim
10002070 75 6D 20 43 6F 6D 70 72 65 73 73 69 6F 6E 20 2B um Compression +
10002080 20 52 6F 6C 6C 69 6E 67 20 43 68 65 63 6B 73 75 Rolling Checksu
10002090 6D 20 45 6E 61 62 6C 65 64 0D 0D 7E 7E 20 43 52 m Enabled..~~ CR
100020A0 41 43 4B 45 44 20 42 59 20 48 41 47 47 41 52 20 ACKED BY HAGGAR
100020B0 7E 7E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ~~..............
Then I found where it shows that message box so my patch can patch it. Here is finall code:
10001040 60 PUSHAD ;Save registers.
10001041 C705 09E54000 68>MOV DWORD PTR DS:[40E509],68 ;Place PUSH.
1000104B C705 0DE54000 00>MOV DWORD PTR DS:[40E50D],90909000 ;NOP all.
10001055 C605 11E54000 90 MOV BYTE PTR DS:[40E511],90
1000105C 90 NOP
1000105D 90 NOP
1000105E 90 NOP
1000105F E8 00000000 CALL X.10001064 ;Get current offset to EAX and point
10001064 58 POP EAX ;my text. DLL can be loaded at any base
10001065 05 9C0F0000 ADD EAX,0F9C ;so this will make ure that it works OK.
1000106A A3 0AE54000 MOV DWORD PTR DS:[40E50A],EAX ;Place PUSH destination.
1000106F 61 POPAD ;Registers back.
10001070 68 B0714200 PUSH 4271B0 ;OEP value. It is static always.
10001075 C3 RETN ;Jump to it.
10001076 90 NOP
10001077 90 NOP
10001078 90 NOP
10001079 90 NOP
And that is it! Target is inline patched.
8. For the end
And that would be all. Script is included in archive along with dumped files. I included my X.DLL and patch too.
Greets goes to everybody on BIW, ARTEAM , Morteza_SOS, CRACKMES.DE, .... and many more....
See you :-)
haggar (c) 2006
PS
I forgot to include script in archive so I'll place it here.
/*
-----------------------------------------------------------------
Thinstall v2.5xx (v2.521 tested)- OEP finder script, by haggar
-----------------------------------------------------------------
¤ Script features:
+ finds OEP,
+ kills DebugBlocker,
+ kills SPLASH window,
+ prevents allocating blocks in top memory (7xxxxxxx).
- it doesn't repair IAT since it can be manually fixed.
- script needs Windows XP.
-----------------------------------------------------------------
*/
var thread_patch
var version
var alloc
var alloc_end
var oep_call
var loader
var opcode
var addr
var counter
var AllocType
//------------ Patch CreateThread API to kill splash window --------------
gpa "CreateThread","kernel32.dll"
mov thread_patch,[$RESULT]
mov [$RESULT],#900018c2#
//-------------- Breakpoints ---------------
gpa "VirtualAlloc","kernel32.dll"
mov alloc,$RESULT
findop alloc,#c21000#
mov alloc_end,$RESULT
gpa "GetVersionExA","kernel32.dll"
findop $RESULT,#c20400#
mov version,$RESULT
bp alloc
bp alloc_end
bp version
dbh //Hide from IsDebuggerPresent API.
//--------- Get loader base , Check & kill DebugBlocker , find OEP ----------
mov counter,0 //Counter, just to check is it first VirtualAlloc.
LABEL_01:
esto
cmp eip,version //Stopped on GetVersionExA?
jne NEXT_01a //If not, go on VirtualAlloc part.
sti //If yes, check opcodes (are we at the right place).
mov addr,eip
mov opcode,[addr]
and opcode,0ff
cmp opcode,0a1
jne LABEL_01
add addr,5
mov opcode,[addr]
cmp opcode,25
jne LABEL_01
sti
sti
mov eax,0 //Right place, so kill DebugBlocker.
bc version //I don't need this breakpoint anymore.
sti //This is leftover from first script. Trace to CreateThread.
sti
sti
sti
mov eax,0 //Don't run thread. I don't need this part since I patched API.
//Then find OEP call and place bp on it.
find loader,#FF9548FCFFFF6A00E8????020083A544FCFFFF0080 65FC0083BDD4FDFFFF007412#
cmp $RESULT,0
je ABORT
mov oep_call,$RESULT
bp oep_call //Place breakpoint on OEP call.
jmp LABEL_01
NEXT_01a:
cmp eip,alloc //Are we at VirtualAlloc?
jne NEXT_01b
mov AllocType,esp
add AllocType,0c
and [AllocType],0FFFF //Yes? Then prevent allocating above 7xxxxxxx.
esto
cmp counter,0 //Is it first stop?
jne NEXT_01c
mov counter,1
mov loader,eax //Then get loader base.
NEXT_01c:
log eax
jmp LABEL_01
NEXT_01b:
bc alloc
bc alloc_end
bc eip
sti
an eip
cmt eip,"<-- I found OEP, imports are your problem."
log loader
//------------ Restore original bytes at CreateThread API --------------
gpa "CreateThread","kernel32.dll"
mov [$RESULT],thread_patch
dbs //Restore debugger.
ret
ABORT:
msg "Error or aborted by user."
dbs
ret
-
Level : advanced
------------------------------------------
Obsidium 1.3.0.4 - unpacking
------------------------------------------
This tutorial is not exact solution for unpacking obsidium, instead it will just discuss some protection features and new implemented tricks in this version of obsidium. The new version of obsidium, 1.3.0.4 one, is pretty much the same as 1.2.5.0 one. One new trick is implemented, more junk code is added and that would be it.
1. Tools and stuff
Tools that you will need:
- OllyDbg (with OllyScript plugin, one by SHaG or Epsylon3)
- ImpREC
- LordPE
- CFF Explorer
- some hex editor
- DAMN hash calculator can be usefull
Anti debug tricks that Obsidium uses:
- UnhandledExceptionFilter
- FindWindowA
- CheckRemoteDebuggerPresent
- IsDebuggerPresent
- CreateToolhelp32Snapshot
First four tricks are described in previous obsidium tutorial, so here only goes explanation for CreateToolhelp32Snapshot.
This API creates snapshot of all running processes. Than, that snapshot is examned by Process32First and Process32Next APIs. In our target, we place breakpoint at the end of this API and we break there. EAX returns handle of snapshot, in my case EAX = 0000003C which is handle of snapshot. But main part is just comming. Now, that snapshot is examned with Process32Next and Process32First APIs. Process32First examnes only first process in snapshot, while second API examnes all others.
These two APIs returns some very usefull informations about specific process. Part of that information is name of process, it's PID and it's "father" PID. "Father" PID is PID of process that started some process. So protector can see does OLLYDBG.EXE is running in background. But that is not some smart trick since we can just rename olly and obsidium will not do that.
Obsidium will find it's own process and it will take PID of it's "father", the process who started it. Then it will examne all processes to find process with "father" PID, and it will check something. I din't trace to see what and how obsidium checking. Code is full of junk and I didn't want to waste time. I assumed that obsidium checking is "father" process explorer.exe, but I think that it is not exactly that. Nevermind, here is simple trick how to avoid olly detection:
After breaking at CreateToolhelp32Snapshot , I place bp on end of Process32Next API. End because obsidium checks couple bytes for breakpoints, and it also emulate first couple opcodes. Then I check stack window:
0012FD04 009074A1 RETURN to 009074A1
0012FD08 0000003C
0012FD0C 0012FD18
Note that in your case, these values will probably be different, but that is not important. Third line from the top shows address of buffer for process info 0012FD18. Now I go there in dump window:
0012FD18 28 01 00 00 00 00 00 00[04 00 00 00]00 00 00 00 (...............
0012FD28 00 00 00 00 46 00 00 00(00 00 00 00)08 00 00 00 ....F...........
0012FD38 00 00 00 00 53 79 73 74 65 6D 00 20 50 72 6F 63 ....System. Proc
0012FD48 65 73 73 5D 00 00 00 00 00 00 00 00 00 00 00 00 ess]............
We can see what process now this API examnes - System. It's PID is between [] while it's "father" PID is in (). This process doesn't have father PID. I'm searching for explorer.exe, ollydbg.exe and target.exe. Kep pressing F9:
0012FD18 28 01 00 00 00 00 00 00 E0 04 00 00 00 00 00 00 (...............
0012FD28 00 00 00 00 13 00 00 00 C8 04 00 00 08 00 00 00 ................
0012FD38 00 00 00 00 65 78 70 6C 6F 72 65 72 2E 65 78 65 ....explorer.exe
0012FD48 00 73 73 5D 00 00 00 00 00 00 00 00 00 00 00 00 .ss]............
0012FD18 28 01 00 00 00 00 00 00 60 04 00 00 00 00 00 00 (.......`.......
0012FD28 00 00 00 00 05 00 00 00 E0 04 00 00 08 00 00 00 ................
0012FD38 00 00 00 00 4F 4C 4C 59 44 42 47 2E 45 58 45 00 ....OLLYDBG.EXE.
0012FD48 00 73 73 5D 00 00 00 00 00 00 00 00 00 00 00 00 .ss]............
0012FD18 28 01 00 00 00 00 00 00 28 01 00 00 00 00 00 00 (.......(.......
0012FD28 00 00 00 00 02 00 00 00(60 04 00 00)08 00 00 00 ........`.......
0012FD38 00 00 00 00 74 61 72 67 65 74 2E 65 78 65 00 00 ....target.exe..
0012FD48 00 73 73 5D 00 00 00 00 00 00 00 00 00 00 00 00 .ss]............
We can see: name of process - PID - father's PID
explorer.exe - 4E0 - 4C8
OLLYDBG.EXE - 460 - 4E0
target.exe - 128 - 460
Those PID's will always be different, but you can see that Olly is "father" for target.exe. Obsidium will see that too. But, we can simply change that return value in buffer. We change "father" PID from Olly's to eplorer.exe:
0012FD18 28 01 00 00 00 00 00 00 28 01 00 00 00 00 00 00 (.......(.......
0012FD28 00 00 00 00 02 00 00 00(E0 04 00 00)08 00 00 00 ................
0012FD38 00 00 00 00 74 61 72 67 65 74 2E 65 78 65 00 00 ....target.exe..
0012FD48 00 73 73 5D 00 00 00 00 00 00 00 00 00 00 00 00 .ss]............
And now obsidium will not notice that it's been debugged. It will search again all processes for explorer.exe and it will see that explorer.exe is good buddy.
Now we can run file under olly, but obsidium created one thread that check for debugger presence all the time. We can simply suspend that thread in olly and then run target normally under olly. Red thread in thread window is our main process, so you can suspend second one (first in this example):
Threads
Ident Entry Data block Last error Status Priority User time System time
000001D0 7C810856 7FFDB000 ERROR_SUCCESS (000 Active 32 - 2 0.0000 s 0.0000 s
000004EC 00408000 7FFDD000 ERROR_SUCCESS (000 Active 32 + 0 0.0000 s 0.0000 s
Threads
Ident Entry Data block Last error Status Priority User time System time
000001D0 7C810856 7FFDB000 ERROR_SUCCESS (000 Suspended 32 - 2 0.0000 s 0.0000 s
000004EC 00408000 7FFDD000 ERROR_SUCCESS (000 Active 32 + 0 0.0000 s 0.0000 s
2. OEP
OEP can be found like in previous tutorial, but obsidium code it to much filled with crappy opcodes. Since my target is Delphi one, first import that it uses is GetModuleHandleA. I placed bp on the end of that API,stopped there and returned to code:
003B45BC BA 98803B00 MOV EDX,3B8098
003B45C1 52 PUSH EDX
003B45C2 8905 B4943B00 MOV DWORD PTR DS:[3B94B4],EAX
003B45C8 8942 04 MOV DWORD PTR DS:[EDX+4],EAX
003B45CB E8 98FFFFFF CALL 003B4568
003B45D0 5A POP EDX
003B45D1 58 POP EAX
003B45D2 E8 DDE7FFFF CALL 003B2DB4
003B45D7 C3 RETN
003B45D8 55 PUSH EBP
003B45D9 8BEC MOV EBP,ESP
003B45DB 33C0 XOR EAX,EAX
003B45DD 55 PUSH EBP
003B45DE 68 FD453B00 PUSH 3B45FD
003B45E3 64:FF30 PUSH DWORD PTR FS:[EAX]
003B45E6 64:8920 MOV DWORD PTR FS:[EAX],ESP
003B45E9 FF05 B8943B00 INC DWORD PTR DS:[3B94B8]
003B45EF 33C0 XOR EAX,EAX
003B45F1 5A POP EDX
003B45F2 59 POP ECX
003B45F3 59 POP ECX
003B45F4 64:8910 MOV DWORD PTR FS:[EAX],EDX
003B45F7 68 04463B00 PUSH 3B4604
003B45FC C3 RETN
003B45FD ^E9 92E4FFFF JMP 003B2A94
003B4602 ^EB F8 JMP SHORT 003B45FC
003B4604 5D POP EBP
003B4605 C3 RETN
003B4606 8BC0 MOV EAX,EAX
003B4608 832D B8943B00 01 SUB DWORD PTR DS:[3B94B8],1
003B460F C3 RETN
003B4610 -FF25 A8A13B00 JMP DWORD PTR DS:[3BA1A8] <--- Imports.
003B4616 8BC0 MOV EAX,EAX
003B4618 -FF25 A4A13B00 JMP DWORD PTR DS:[3BA1A4]
003B461E 8BC0 MOV EAX,EAX
003B4620 -FF25 A0A13B00 JMP DWORD PTR DS:[3BA1A0]
003B4626 8BC0 MOV EAX,EAX
003B4628 -FF25 9CA13B00 JMP DWORD PTR DS:[3BA19C]
003B462E 8BC0 MOV EAX,EAX
Knowing Delphi apps, first RETN below leads back close to OEP:
003B79D0 55 PUSH EBP <----------------- This is OEP.
003B79D1 8BEC MOV EBP,ESP
003B79D3 83C4 F4 ADD ESP,-0C
003B79D6 53 PUSH EBX
003B79D7 56 PUSH ESI
003B79D8 57 PUSH EDI
003B79D9 B8 98793B00 MOV EAX,3B7998
003B79DE E8 D1CBFFFF CALL 003B45B4
003B79E3 BE CC953B00 MOV ESI,3B95CC <----------- Here I am right now.
003B79E8 BF E4953B00 MOV EDI,3B95E4
003B79ED BB A0953B00 MOV EBX,3B95A0
003B79F2 33C0 XOR EAX,EAX
003B79F4 55 PUSH EBP
003B79F5 68 847C3B00 PUSH 3B7C84
003B79FA 64:FF30 PUSH DWORD PTR FS:[EAX]
003B79FD 64:8920 MOV DWORD PTR FS:[EAX],ESP
003B7A00 A1 80823B00 MOV EAX,DWORD PTR DS:[3B8280]
003B7A05 8B00 MOV EAX,DWORD PTR DS:[EAX]
003B7A07 A3 C8953B00 MOV DWORD PTR DS:[3B95C8],EAX
003B7A0C C703 C0000000 MOV DWORD PTR DS:[EBX],0C0
003B7A12 C743 04 90783B00 MOV DWORD PTR DS:[EBX+4],3B7890
003B7A19 A1 C8953B00 MOV EAX,DWORD PTR DS:[3B95C8]
003B7A1E 8943 10 MOV DWORD PTR DS:[EBX+10],EAX
003B7A21 C743 1C 10000000 MOV DWORD PTR DS:[EBX+1C],10
003B7A28 B8 947C3B00 MOV EAX,3B7C94
003B7A2D 8943 24 MOV DWORD PTR DS:[EBX+24],EAX
003B7A30 68 007F0000 PUSH 7F00
003B7A35 6A 00 PUSH 0
003B7A37 E8 44CCFFFF CALL 003B4680
Ok, I didn't packed this file with stolen code option so we know OEP bytes. Problem in this file are imports and fact that whole code is relocated to this 003B0000 block. Maybe this can be prevented during obsidium runtime execution, but I have no will to trace trough obsidium junk code. Instead I will go around this, making weird dump. But I will leave that as a last thing. First imports.
3. Imports
Imports can be found like in previous tutorial, but script needs some changes because Delphi uses JMP DWORD[IMPORT] which affects stack diffrent. Also , obsidum has lot of junk there so I needed to clean it somehow (I manually patched lot of those small jumps). From previous tutorial (obsidium 1.2.5.0) I sow how obsidium handles imports. Obsidium has some internal table that consist from two parts (three, third was DLL names but they are erased after unpacking):
First part are two DWORDS, first one is base of DLL and second is FFFFFFFF flag which probably means that DLL is loaded in memory. If that flag is 0, it means that API which is called is Obsidium protection API.
Second is decrypted imports. It consinst from 1. WORD value, which determs type of protection, 2. WORD value and 3. DWORD value. Everything depends about 1. WORD value. Let's call that value "import code". There are couple such codes types:
- "code 2" type:
"Code 2" means that import is stored as first character of import and import CRC32 hash, information in table looks like this:
code number - first char - CRC32 hash
02 00 47 00 A0 F7 BF 08
After decrypting, that import becomes "code 4" import, where code number is changed to 4, first char info is erased and hash is replaced with xor-ed import with value that it seams to be some unique constant for target. That is probably for speeding up execution. In executables who uses imports via CALL -> JMP DWORD[API] combination, such as ASM, Borland C++, Delphi, etc. compiled programs, CALL is then replaced with direct jump to import CALL API. For MSVC++ and similar compilers, who use CALL DWORD[API], that is not case.
- "code 4" type:
It seams that these imports are already used ones. Some targets doesn't have these ones by default because I checked every single import in some of them, but I notced that there are such values in their obsidium table. Question stays.
- "code 1" type:
code number - ? - ?
01 00 00 00 11 00 00 00
Code number is 1, first char is zero, hash is not hash. I didn't tried to find what those values means since this import can be retrieved similar to "type 2". Also, this type becames then type 4 too.
- "code 80" type:
Something similar to "code 1".
code number - ? - ?
80 00 00 00 04 00 00 00
This type goes in little longer way. It returns to main image where emulation is performed and then it jumps to import. Again, import can be retrieved at one point, but this import doesn't becomes type 4 after.
- "code 40" type:
code number - ? - ?
40 00 00 00 03 00 00 00
This one doesn't seem to be emulated or obfuscated. ExitProcess is usually one of this type. Little harder for script but probably there are no many these ones.
- "code 10" type:
code number - ? - CRC32 hash
10 00 00 00 C2 2E C7 4E
This is internal Obsidium API. There are about 10 such API's whic are used to check registration information or trial one. Information about these API's can be found in obsidium help file.
00905FBA 3D C22EC74E CMP EAX,4EC72EC2 -> isRegistered
00905FCE 3D A1A0C163 CMP EAX,63C1A0A1 -> getRegInfo
00905FE1 3D 09586CFC CMP EAX,FC6C5809 -> ??? maybe setExternalKey
00905FF6 3D 8019A9B7 CMP EAX,B7A91980 -> getSystemID
0090600D 3D B4D93687 CMP EAX,8736D9B4 -> setKeyfile
00906020 3D 261923D3 CMP EAX,D3231926 -> getTrialDays
00906037 3D 5F9EFDB8 CMP EAX,B8FD9E5F -> getTrialRuns
0090604A 3D 925D522F CMP EAX,2F525D92 -> getLicenseExpiration
00906060 3D E4234726 CMP EAX,264723E4 -> setShortKey
00906072 3D 057EC561 CMP EAX,61C57E05 -> getCustomValue
- rest of possible imports
I noticed some code 8 type. Don't know what is that.
Below code snippet is taken from previous obsidium tutorial (example target) and shows how imports type 1 and 2 can be found using script. It is indentical in both versions , only 1.3.0.4 one have tons of junk between "real" code (and that is reason why I didn't show that snippet).
00394505 MOVZX EDX,CL
00394508 MOVZX EAX,AX
0039450B CALL 00394510
00394510 POP EBX
00394511 MOV EBP,EBX
00394513 MOV EBX,DWORD PTR DS:[EBX-74]
00394516 SUB EBP,0A8A0CB
0039451C CALL 00394521
00394521 ADD DWORD PTR SS:[ESP],0D
00394525 MOV ECX,DWORD PTR SS:[EBP+A8A0BC]
0039452B CALL ECX
0039452D RETN
0039452E MOV ESI,DWORD PTR DS:[EBX+44]
00394531 XOR DL,BYTE PTR SS:[EBP+A8A18C]
00394537 XOR AX,WORD PTR SS:[EBP+A8A18D]
0039453E SHL EDX,3
00394541 MOV ECX,EDX
00394543 SHL EDX,1
00394545 ADD ECX,EDX
00394547 LEA EDI,DWORD PTR DS:[ESI+ECX+4]
0039454B CMP DWORD PTR DS:[EDI+4],0 <--------------------- DLL check.
0039454F JE 00394768
00394555 ADD ESI,DWORD PTR DS:[EDI+14]
00394558 LEA ESI,DWORD PTR DS:[ESI+EAX*8]
0039455B MOVZX EAX,WORD PTR DS:[ESI] <-------------------- Obsidium import table.
0039455E CMP EAX,4 <-------------------------------------- "code 4" check.
00394561 JE 003945FE
00394567 CMP EAX,1 <-------------------------------------- "code 1" check.
0039456A JE SHORT 003945D4
0039456C CMP EAX,80
00394571 JE 00394830
00394577 CMP EAX,40
0039457A JE 0039462A
00394580 MOVZX EAX,WORD PTR DS:[ESI+2]
00394584 PUSH 1
00394586 PUSH EAX
00394587 PUSH 0
00394589 PUSH DWORD PTR DS:[ESI+4]
0039458C PUSH DWORD PTR DS:[EDI]
0039458E CALL DWORD PTR DS:[EBX+50]
00394591 TEST EAX,EAX <----------------------- Here we can retrieve "code 2" import.
00394593 JNZ SHORT 003945E6
....
....
003945D9 JBE SHORT 003945DF
003945DB PUSH 0
003945DD PUSH DWORD PTR DS:[EDI]
003945DF CALL DWORD PTR DS:[EBX+50]
003945E2 TEST EAX,EAX <------------------------- Here we can retrieve "code 1" import.
003945E4 JE SHORT 003945B5
003945E6 MOV EDX,EAX
Before trying script or manually tracing, set access on image to "full access". Ok, after writting script here is partially found IAT, it misses about 20 imports:
003BA0B4 8A 18 91 7C ED 10 90 7C 05 10 90 7C A1 9F 80 7C ...|...|...|...|
003BA0C4 14 9B 80 7C 81 9A 80 7C 5D 99 80 7C BD 99 80 7C ...|...|]..|...|
003BA0D4 60 00 3C 00 C7 A0 80 7C AD 9C 80 7C 78 67 90 00 `.<....|...|xg..
003BA0E4 29 C7 80 7C 9C 00 3C 00 05 A4 80 7C EE 1E 80 7C )..|..<....|...|
003BA0F4 57 B3 80 7C 7E D4 80 7C 31 03 91 7C CF 01 3C 00 W..|~..|1..|..<.
003BA104 F0 00 3C 00 FC 00 3C 00 08 01 3C 00 14 01 3C 00 ..<...<...<...<.
003BA114 50 F8 81 7C 40 7A 93 7C 38 01 3C 00 E1 EA 81 7C P..|@z.|8.<....|
003BA124 A9 2C 81 7C 5C 01 3C 00 69 10 81 7C 74 01 3C 00 ©,.|.<.i..|t.<.
003BA134 80 01 3C 00 8C 01 3C 00 46 FA D6 77 98 EC D6 77 ..<...<.F..w...w
003BA144 0B 05 D8 77 FD 01 3C 00 83 78 DD 77 1B 76 DD 77 ...w..<..x.w.v.w
003BA154 F0 6B DD 77 2D 02 3C 00 C4 65 12 77 48 D3 14 77 .k.w-.<..e.wH..w
003BA164 C0 48 12 77 3B 4C 12 77 50 48 12 77 59 4B 12 77 .H.w;L.wPH.wYK.w
003BA174 81 02 3C 00 F5 9B 80 7C 50 97 80 7C BD 99 80 7C ..<....|P..|...|
003BA184 29 B5 80 7C 57 B3 80 7C C9 02 3C 00 78 67 90 00 )..|W..|..<.xg..
003BA194 E1 02 3C 00 51 28 81 7C 05 A4 80 7C 57 B3 80 7C ..<.Q(.|...|W..|
003BA1A4 7E D4 80 7C E6 2B 81 7C 29 03 3C 00 80 AD F3 77 ~..|.+.|).<....w
003BA1B4 41 03 3C 00 64 C0 D4 77 CE 8B D4 77 DC E5 D4 77 A.<.d..w...w...w
003BA1C4 AE E2 D4 77 16 23 D5 77 0B 05 D8 77 98 EC D6 77 ®..w.#.w...w...w
003BA1D4 FA E8 D4 77 2E F8 D6 77 75 8F D4 77 45 EA D6 77 ...w...wu..wE..w
003BA1E4 BD BC D4 77 6B DF D4 77 0B 19 D5 77 F5 03 3C 00 ...wk..w...w..<.
After long and painfull, manually tracing, I found some others:
003BA0B4 8A 18 91 7C ED 10 90 7C 05 10 90 7C A1 9F 80 7C ...|...|...|...|
003BA0C4 14 9B 80 7C 81 9A 80 7C 5D 99 80 7C BD 99 80 7C ...|...|]..|...|
003BA0D4 59 B8 80 7C C7 A0 80 7C AD 9C 80 7C E0 C6 80 7C Y..|...|...|...|
003BA0E4 29 C7 80 7C 4F 1D 80 7C 05 A4 80 7C EE 1E 80 7C )..|O..|...|...|
003BA0F4 57 B3 80 7C 7E D4 80 7C 31 03 91 7C 8D 2C 81 7C W..|~..|1..|.,.|
003BA104 66 AA 80 7C A2 CA 81 7C 9F 0F 81 7C A6 0D 81 7C f..|...|...|...|
003BA114 50 F8 81 7C 40 7A 93 7C 0E 18 80 7C E1 EA 81 7C P..|@z.|...|...|
003BA124 A9 2C 81 7C 8F 0C 81 7C 69 10 81 7C 24 1A 80 7C ©,.|...|i..|$..|
003BA134 77 9B 80 7C 8C 01 3C 00 46 FA D6 77 98 EC D6 77 w..|..<.F..w...w
003BA144 0B 05 D8 77 FD 01 3C 00 83 78 DD 77 1B 76 DD 77 ...w..<..x.w.v.w
003BA154 F0 6B DD 77 2D 02 3C 00 C4 65 12 77 48 D3 14 77 .k.w-.<..e.wH..w
003BA164 C0 48 12 77 3B 4C 12 77 50 48 12 77 59 4B 12 77 .H.w;L.wPH.wYK.w
003BA174 81 02 3C 00 F5 9B 80 7C 50 97 80 7C BD 99 80 7C ..<....|P..|...|
003BA184 29 B5 80 7C 57 B3 80 7C C9 02 3C 00 E0 C6 80 7C )..|W..|..<....|
003BA194 59 B8 80 7C 51 28 81 7C 05 A4 80 7C 57 B3 80 7C Y..|Q(.|...|W..|
003BA1A4 7E D4 80 7C E6 2B 81 7C 29 03 3C 00 80 AD F3 77 ~..|.+.|).<....w
003BA1B4 41 03 3C 00 64 C0 D4 77 CE 8B D4 77 DC E5 D4 77 A.<.d..w...w...w
003BA1C4 AE E2 D4 77 16 23 D5 77 0B 05 D8 77 98 EC D6 77 ®..w.#.w...w...w
003BA1D4 FA E8 D4 77 2E F8 D6 77 75 8F D4 77 45 EA D6 77 ...w...wu..wE..w
003BA1E4 BD BC D4 77 6B DF D4 77 0B 19 D5 77 F5 03 3C 00 ...wk..w...w..<.
It looks like 7 of them are still unknown but searching all intermodular calls, I didn't find any missing one. Maybe if I check jumps !?! Hmm, no, everything looks fine. Those leftovers are probably just space between thunks. I will not use ImpREC now, because it cannot get imports (image is relocated). Ok, now dumping problem.
4. Dumping
Buah, how to dump now, when target is totally crippled? This is how target looks in memory:
Memory map
Address Size Owner Section Contains Type Access Initial Mapped as
003B0000 0000E000 Priv RWE RWE
003C0000 00001000 Priv RWE RWE
003D0000 00004000 Priv RW RW
003E0000 00003000 Map R R
00400000 00001000 target PE header Imag RWE RWE
00401000 00001000 target code,imports Imag RWE RWE
00402000 00004000 target .rsrc code,resourc Imag RWE RWE
00406000 00001000 target code,resourc Imag RWE RWE
00407000 00001000 target code,resourc Imag RWE RWE
00408000 0000C000 target SFX,data,res Imag RWE RWE
Our target is located at 00400000 while code is relocated in 003B0000 to prevent dumping. So I dumped my target with lor PE, then I dumped whole block from 003B0000 to 00400000. Difference from 003B0000 to 00400000 is 50000. Then I added to dump 50000 bytes, but at the beggining of file! Now, PE header in dump is at 50000 offset and dump is totally invalid. At the beggining of file, in that 0-50000 range, I paste that dumped block from 003B0000-00400000. Now, in my dump is all that I need, but everything is screwed. Ok, time to fix dump.
I moved header from 50000 to 0 offset (simply copying 400 bytes from 50000 and paste it to 0 offset). Now dump has header (old one at 50000 can be erased - filled with zeros), but it's invalid. Using LordPE, of maybe better CFF Explorer, I need to shift all values up for 50000 bytes. These are values that I set in LordPE:
OEP = 000079D0
ImageBase = 003B0000
ImageSize = 00064000
BaseOfCode = 00001000
BaseOfData = 00001000
NumberOfSections = 0002
NumOfRvaAndSizes = 00000010
Notice that I set file to have only 2 sections. Rest of them will not be erased, I will just expand those two. Sections are set like this, again LordPE is used:
First section:
Name = CODE
VOffset = 1000
VSize = 51000
ROffset = 1000
RSize = 51000
Flags = E00000E0
Second section
Name = .rsrc
VOffset = 52000
VSize = 12000
ROffset = 52000
RSize = 12000
Flags = E00000E0
Sections are on the place now, but resources, imports and TLS data needs to be corrected. TLS and resources I changed with CFF (but it can be done with LordPE too):
TLS table:
RVA = 00057000 (it was 7000, I added 50000)
Resurce:
RVA = 00052000 (shifted up for 50000)
Resources can be viewed clicking on "..." button by the resurce info. My first two entries are incorrect. I fix them to:
first one (which shows as bitamp when fixed):
RVA = 00052910
Offset = 00052910
second (icon)
RVA = 000520C0
Offset = 000520C0
Now is everything almost fixed. Imports are only that's left. In LordPE, I set ImportTable RVA and Size to 00000000. Now I can load it in olly without problem.
5. Rebuilding import table
What is wrong with imports? We don't have IAT, DLLs are not loaded in memory, but jumps point to some pointers/thunks who are correct pointers to imports if DLLs are loaded. So if I load DLLs in memory, this dump will work on my machine. If I load DLLs , I could then use ImpREC to get imports (since pointers now are in main image an ImpREC can find thm).
How to load DLLs? Simply, by injecting two lines of code that will push DLL name and then call LoadLibraryA API. And that for all DLLs. What DLLs I need? When I'm at OEP of my unpacked (not yet dumped , or you can just attach to target if you don't want to search OEP again) target, I check modules window:
Executable modules
Base Size Entry Name File version Path
77DD0000 0009B000 77DD70D4 advapi32 5.1.2600.2180 (x C:WINDOWSsystem32advapi32.dll
77F10000 00046000 77F163CA GDI32 5.1.2600.2180 (x C:WINDOWSsystem32GDI32.dll
7C800000 000F4000 7C80B436 kernel32 5.1.2600.2180 (x C:WINDOWSsystem32kernel32.dll
77C10000 00058000 77C1F2A1 msvcrt 7.0.2600.2180 (x C:WINDOWSsystem32msvcrt.dll
7C900000 000B0000 7C913156 ntdll 5.1.2600.2180 (x C:WINDOWSsystem32ntdll.dll
774E0000 0013C000 774F20C1 ole32 5.1.2600.2180 (x C:WINDOWSsystem32ole32.dll
77120000 0008C000 77121558 oleaut32 5.1.2600.2180 C:WINDOWSsystem32oleaut32.dll
77E70000 00091000 77E76284 RPCRT4 5.1.2600.2180 (x C:WINDOWSsystem32RPCRT4.dll
77FE0000 00011000 77FE2131 Secur32 5.1.2600.2180 (x C:WINDOWSsystem32Secur32.dll
77D40000 00090000 77D50EB9 USER32 5.1.2600.2180 (x C:WINDOWSsystem32USER32.DLL
00400000 00014000 00408000 target 1.0.0.0 D:Obsidium 1.3.0.4target.exe
Some of those DLLs are not needed, but nevermind. Now I find some empty place in my dump, I write in that DLL names, then I just inject three lines for every DLL:
push offset_to_dll_name (example ASCII "advapi32.dll")
call LoadLibraryA
nop
After doing that and executing that code, all DLLs are loaded and now I use ImpREC. Dump is fixed now.
6. The end
-
Level : intermediate
------------------------------------------
Obsidium 1.2.5.0 - unpacking
------------------------------------------
I didn't unpack anything for a while due to my coledge obligations, but today I gave on examne and I'm in good mood. Obsidium is pretty good protector that comes from chinese author. I didn't notice any apps packed with it and I don't know why, since it has some pretty cool options. In this tutorial I will try 1.2.5.0 version which is older one, but it's good as starting point. I think that this unpackme doesn't have all protection options enabled since I know that obsidium has option to relocate whole image. That is probably only possible with Borland applications.
1. Intro
Tools that we need are usual:
Windows XP; OllyDbg; ImpREC; LordPE; some hex editor; PEiD is not important but if you have good external database, at least it can detect correct version of packer.
Obsidium has some cool anti-debug trickcs:
- CheckRemoteDebuggerPresent; this API detects debugger on XP machines,
- UnhandledExceptionFilter; trick to crush application which runs under debugger,
- FindWindowA; it finds olly window class "OLLYDBG",
- IsDebuggerPresent; detects debugger,
- threads; obsidium runs one or two threads that uses previous tricks,
- stolen OEP code; it steal couple bytes from OEP,
- cool import protection.
Also, obsidum code is full of junk opcodes like short jumps that makes tracing very annoying. That is actually one thing that I realy hate. Obsidium doesn't use GetProcAddress to find imports, instead it have custom procedure that find imports. It also checks for breakpoints at API's and emulate them partialy, so we need to place bp's on end of API's.
Anti-debug tricks, CheckRemoteDebuggerPresent, FindWindowA and IsDebuggerPresent, can be bypassed simply by placing bp's at the end of those API's and then seting return value EAX=0.
UnhandledExceptionFilter is used to handle some exceptions that protector generate on purpose. Problem is that if we are in debugger, application expects that debugger will take control instead system debugger (Dr. Watson). We can force system to think that application is not running within debugger. This is UnhandledExceptionFilter function and below shows how to trick it:
PUSH 248 <------------------------- UnhandledExceptionFilter function starts here.
PUSH kernel32.7C8635E0
CALL kernel32.7C8024CB
MOV EAX,DWORD PTR DS:[7C8836CC]
MOV DWORD PTR SS:[EBP-1C],EAX
MOV EBX,DWORD PTR SS:[EBP+8]
MOV DWORD PTR SS:[EBP-178],EBX
MOV DWORD PTR SS:[EBP-148],4
XOR EDI,EDI
MOV DWORD PTR SS:[EBP-13C],EDI
MOV DWORD PTR SS:[EBP-16C],EDI
MOV EAX,DWORD PTR DS:[EBX]
TEST BYTE PTR DS:[EAX+4],10
JE SHORT kernel32.7C862BD4
PUSH DWORD PTR DS:[EAX]
PUSH -1
CALL DWORD PTR DS:[<&ntdll.NtTerminatePr>; ntdll.ZwTerminateProcess
MOV EAX,DWORD PTR DS:[EBX]
MOV ESI,C0000005
CMP DWORD PTR DS:[EAX],ESI
JNZ SHORT kernel32.7C862BF9
CMP DWORD PTR DS:[EAX+14],1
JNZ SHORT kernel32.7C862BF9
PUSH DWORD PTR DS:[EAX+18]
CALL kernel32.7C862874
CMP EAX,-1
JNZ SHORT kernel32.7C862BF9
OR EAX,EAX
JMP kernel32.7C863458
MOV DWORD PTR SS:[EBP-124],EDI
PUSH EDI
PUSH 4
LEA EAX,DWORD PTR SS:[EBP-124]
PUSH EAX
PUSH 7
CALL kernel32.GetCurrentProcess <------------- Here is trick! This API returns FFFFFFFF. Change EAX to 0.
PUSH EAX
CALL DWORD PTR DS:[<&ntdll.NtQueryInform>; ntdll.ZwQueryInformationProcess
TEST EAX,EAX
...
...
2. OEP and stolen code
While writing this text, I didn't find some genereic way to find OEP that would be suitable for writing script. Instead that, I traced and experimented with code, exceptions , breakpoints. I found where import jumps are allocated, conclusion is that when crackme is unpacked, it will try to use some import so I just placed memory bp on whole import block. Crackme is small so it is not problem to find starting point.
But let's go in other direction. I placed breakpoints on all checks to kill anti tricks. When new thread started, I took a look in log window. Last occured exception is interesting:
Log data
Address Message
00415022 Access violation when reading [00000000]
0041EFA5 Access violation when reading [FFFFFFFF]
77DD0000 Module C:WINDOWSsystem32advapi32.dll
00390295 Access violation when reading [FFFFFFFF]
7C85994E Breakpoint at kernel32.7C85994E
7C901230 INT3 command at ntdll.DbgBreakPoint
Access violation when executing [00000000]
7C862C10 Breakpoint at kernel32.7C862C10
003907E8 Illegal instruction
00390854 Integer division by zero
77D6F3DC Breakpoint at USER32.77D6F3DC
Access violation when executing [00000000]
7C862C10 Breakpoint at kernel32.7C862C10
00395F82 Integer division by zero
00395F84 Access violation when reading [00000000]
0039655D Access violation when reading [8003F41E]
0039654A Integer division by zero
0039654C Access violation when reading [00000000]
0039654F Access violation when reading [FFFFFFFF]
0039654D Access violation when reading [FFFFFFFF]
0039655D Access violation when reading [8003F418]
00396550 Illegal instruction
003965BC Integer division by zero
7C812E10 Breakpoint at kernel32.7C812E10
00396555 INT3 command at 00396555
00395041 Integer division by zero
00395041 Integer division by zero
0039520A Integer division by zero
00395041 Integer division by zero
0039520A Integer division by zero
0039520A Integer division by zero
00415DA1 Illegal instruction
00415F9B Integer division by zero <---------------- This one.
7C810856 New thread with ID 00000304 created
7C85994E Breakpoint at kernel32.7C85994E
Last exception is in main image (last section, protectors one) and it is "Integer division by zero". So I done it all again, only this time I unchecked "Intiger divison by 0" in olly options, so I could break on that exception. After couple ones, here is last one:
00415F9B F7F0 DIV EAX <------------------------- Exception.
00415F9D 55 PUSH EBP
00415F9E 8BEC MOV EBP,ESP
00415FA0 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
00415FA3 6A 00 PUSH 0
00415FA5 68 CC971025 PUSH 251097CC
00415FAA 6A 00 PUSH 0
00415FAC FF50 18 CALL DWORD PTR DS:[EAX+18]
00415FAF 5D POP EBP
00415FB0 C2 0800 RETN 8
00415FB3 55 PUSH EBP
00415FB4 8BEC MOV EBP,ESP
00415FB6 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
00415FB9 8B00 MOV EAX,DWORD PTR DS:[EAX]
00415FBB 3D 1D0000C0 CMP EAX,C000001D
00415FC0 74 0E JE SHORT UnPackMe.00415FD0
00415FC2 3D 940000C0 CMP EAX,C0000094
00415FC7 75 41 JNZ SHORT UnPackMe.0041600A
00415FC9 B9 AB92AD01 MOV ECX,1AD92AB
00415FCE EB 05 JMP SHORT UnPackMe.00415FD5
00415FD0 B9 4E90AD01 MOV ECX,1AD904E
00415FD5 E8 00000000 CALL UnPackMe.00415FDA
00415FDA 5A POP EDX
00415FDB 8B45 10 MOV EAX,DWORD PTR SS:[EBP+10]
00415FDE 81EA 893CBA00 SUB EDX,0BA3C89
00415FE4 8D940A EFAB0CFF LEA EDX,DWORD PTR DS:[EDX+ECX+FF0CABEF]
00415FEB 8990 B8000000 MOV DWORD PTR DS:[EAX+B8],EDX
00415FF1 33C9 XOR ECX,ECX
00415FF3 8948 04 MOV DWORD PTR DS:[EAX+4],ECX
00415FF6 8948 08 MOV DWORD PTR DS:[EAX+8],ECX
00415FF9 8948 0C MOV DWORD PTR DS:[EAX+C],ECX
00415FFC 8948 10 MOV DWORD PTR DS:[EAX+10],ECX
00415FFF C740 18 55003333 MOV DWORD PTR DS:[EAX+18],33330055
00416006 33C0 XOR EAX,EAX
00416008 5D POP EBP
00416009 C3 RETN
0041600A 33C0 XOR EAX,EAX
0041600C 40 INC EAX
0041600D 5D POP EBP
0041600E C3 RETN
0041600F 33C0 XOR EAX,EAX
00416011 8D44C0 03 LEA EAX,DWORD PTR DS:[EAX+EAX*8+3]
00416015 010424 ADD DWORD PTR SS:[ESP],EAX
00416018 C3 RETN
By this time, crackme code section is already unpacked. I could place memory breakpoint on it and I would land on "false oep". First, this crackme has code section at 404000. Second, Obsidium ALWAYS JUMPS ON FALSE OEP. Even if it doesn't steal bytes, it jumps couple opcodes further (I've been little examning obsidium work).
If we check stack, we can see where is SEH handler that will handle exception:
0012FF94 0012FFE0 Pointer to next SEH record
0012FF98 00415FB3 SE handler
That is code below our exception, so we can place bp there 00415FB3. We came to RETN that throw us to ntdll.dll:
00416009 C3 RETN
That is Ok, let's enter in and then place memory breakpoint on last section to return to obsidium code.
004161EB E8 67000000 CALL UnPackMe.00416257 <------------- We returning here.
Tracing in we find some decryptor loop which ends here:
004162BF ^0F85 D4FFFFFF JNZ UnPackMe.00416299
004162C5 EB 05 JMP SHORT UnPackMe.004162CC
004162C7 BD 7C3BF3D8 MOV EBP,D8F33B7C
004162CC 61 POPAD
004162CD C3 RETN
Then we go here:
004161EB FF96 8C010000 CALL DWORD PTR DS:[ESI+18C]
004161F1 8B7E 04 MOV EDI,DWORD PTR DS:[ESI+4]
004161F4 FFB6 44010000 PUSH DWORD PTR DS:[ESI+144]
004161FA FF96 80000000 CALL DWORD PTR DS:[ESI+80]
00416200 25 FF030000 AND EAX,3FF
00416205 8DBC07 00010000 LEA EDI,DWORD PTR DS:[EDI+EAX+100]
0041620C 56 PUSH ESI
0041620D 57 PUSH EDI
0041620E 8DB5 C83CBA00 LEA ESI,DWORD PTR SS:[EBP+BA3CC8]
00416214 B9 D2010000 MOV ECX,1D2
00416219 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
0041621B 5F POP EDI
0041621C 5E POP ESI
0041621D 8D85 F63EBA00 LEA EAX,DWORD PTR SS:[EBP+BA3EF6]
00416223 68 80000000 PUSH 80
00416228 50 PUSH EAX
00416229 FF76 04 PUSH DWORD PTR DS:[ESI+4]
0041622C FF76 20 PUSH DWORD PTR DS:[ESI+20]
0041622F FF96 80000000 CALL DWORD PTR DS:[ESI+80]
00416235 6A 3A PUSH 3A
00416237 57 PUSH EDI
00416238 50 PUSH EAX
00416239 FF76 04 PUSH DWORD PTR DS:[ESI+4]
0041623C FF76 28 PUSH DWORD PTR DS:[ESI+28]
0041623F FF96 80000000 CALL DWORD PTR DS:[ESI+80]
00416245 -FFE7 JMP EDI <--------------------- Skip all and go here!
Skipp all calls before JMP EDI (some decrypting is performed there) and to it (be aware of possible breakpoin checks if you place bp on JMP EDI). That JMP EDI leads to part which throws you at false oep. Trace through that code and it will transform to:
00398305 E8 00000000 CALL 0039830A
0039830A 5D POP EBP
0039830B 81ED CD3CBA00 SUB EBP,0BA3CCD
00398311 64:67:8F06 0000 POP DWORD PTR FS:[0]
00398317 83C4 04 ADD ESP,4
0039831A C685 FD3CBA00 E9 MOV BYTE PTR SS:[EBP+BA3CFD],0E9
00398321 8B95 063DBA00 MOV EDX,DWORD PTR SS:[EBP+BA3D06]
00398327 0356 10 ADD EDX,DWORD PTR DS:[ESI+10]
0039832A 8D8D 023DBA00 LEA ECX,DWORD PTR SS:[EBP+BA3D02]
00398330 2BD1 SUB EDX,ECX
00398332 8995 FE3CBA00 MOV DWORD PTR SS:[EBP+BA3CFE],EDX
00398338 61 POPAD
00398339 9D POPFD
0039833A -E9 D0BC0600 JMP UnPackMe.0040400F <----------------------- Jump to false OEP.
And here it is:
0040400F E8 01040000 CALL UnPackMe.00404415
00404014 68 00000000 PUSH 0
00404019 FF15 F4114000 CALL DWORD PTR DS:[4011F4]
0040401F A3 07F04000 MOV DWORD PTR DS:[40F007],EAX
00404024 60 PUSHAD
00404025 8925 0BF04000 MOV DWORD PTR DS:[40F00B],ESP
0040402B E9 30000000 JMP UnPackMe.00404060
00404030 8B25 0BF04000 MOV ESP,DWORD PTR DS:[40F00B]
00404036 61 POPAD
00404037 E8 19090000 CALL UnPackMe.00404955
0040403C E8 6D040000 CALL UnPackMe.004044AE
00404041 89EC MOV ESP,EBP
00404043 5D POP EBP
00404044 FF35 D4F14000 PUSH DWORD PTR DS:[40F1D4]
0040404A FF15 EC114000 CALL DWORD PTR DS:[4011EC]
00404050 9B WAIT
00404051 DBE2 FCLEX
00404053 D92D 00604000 FLDCW WORD PTR DS:[406000]
00404059 C3 RETN
0040405A 0000 ADD BYTE PTR DS:[EAX],AL
0040405C 0000 ADD BYTE PTR DS:[EAX],AL
0040405E 0000 ADD BYTE PTR DS:[EAX],AL
00404060 68 00000000 PUSH 0
00404065 B8 7C604000 MOV EAX,UnPackMe.0040607C ; ASCII "Coded by Teddy Rogers / SnD Team 2005"
0040406A 89C2 MOV EDX,EAX
0040406C 52 PUSH EDX
0040406D B8 54604000 MOV EAX,UnPackMe.00406054 ; ASCII "If you unpack it write a tutorial... :)"
00404072 89C6 MOV ESI,EAX
00404074 56 PUSH ESI
00404075 E8 8B0F0000 CALL UnPackMe.00405005
0040407A 89C7 MOV EDI,EAX
0040407C 57 PUSH EDI
0040407D B8 0D000000 MOV EAX,0D
00404082 50 PUSH EAX
00404083 E8 4D030000 CALL UnPackMe.004043D5
00404088 89C6 MOV ESI,EAX
0040408A 56 PUSH ESI
0040408B E8 750F0000 CALL UnPackMe.00405005
Problem is that OEP should be at 404000. That couple instructions are junk now, and they where probably obfuscated trough obsidium layer. But in this crackme, those few bytes are not important and file will work without it, so just leave it like that. So, make dump now.
3. Imports
Ah, imports are realy pain in the ass. If you check where calls lead, you will see this:
00B00000 60 PUSHAD
00B00001 66:BE 22A2 MOV SI,0A222
00B00005 B7 66 MOV BH,66
00B00007 -E9 1D5489FF JMP 00395429
00B0000C 60 PUSHAD
00B0000D 66:BE 23A2 MOV SI,0A223
00B00011 B7 66 MOV BH,66
00B00013 -E9 115489FF JMP 00395429
00B00018 60 PUSHAD
00B00019 66:BE 20A2 MOV SI,0A220
00B0001D B7 66 MOV BH,66
00B0001F -E9 055489FF JMP 00395429
...
...
Import call goes in some obsidium procedure. There, obsidim doesn't use GetProcAddress. Intead, import is saved as
- dll image base (handle),
- some flag (FFFFFFFF is good one),
- import code (which is 2,4,1,80, or 40),
- first character of import,
- CRC32 hash.
Obsidium will get info which dll program needs, then it will do some internal checks (is this import already used, is it emulated whole or partially, etc...), then it will search in that DLL export (which is our import) which first character corespond with one in "obsidium import info" table, then it will search for such import which CRC32 hash is equal to one in table. When it finds it, then it will do some emulation and finaly jump to it.
Let's see on one example, our import call:
00404453 |. FF15 08124000 CALL DWORD PTR DS:[401208]
leads to
00B000B4 60 PUSHAD
00B000B5 66:BE 25A2 MOV SI,0A225 <------ These two values gives info about host DLL.
00B000B9 B7 67 MOV BH,67 <--------/
00B000BB -E9 695389FF JMP 00395429
then
MOVZX EDX,BH
MOVZX EAX,SI
CALL 00395434
POP EBX
MOV EBP,EBX
MOV EBX,DWORD PTR DS:[EBX-74]
SUB EBP,0B8A0CB
CALL 00395445
ADD DWORD PTR SS:[ESP],0D
MOV ECX,DWORD PTR SS:[EBP+B8A0BC]
CALL ECX
RETN
MOV ESI,DWORD PTR DS:[EBX+44]
XOR DL,BYTE PTR SS:[EBP+B8A18C]
XOR AX,WORD PTR SS:[EBP+B8A18D]
SHL EDX,3
MOV ECX,EDX
SHL EDX,1
ADD ECX,EDX
LEA EDI,DWORD PTR DS:[ESI+ECX+4]
CMP DWORD PTR DS:[EDI+4],0 <---------- [1] Check, probably is DLL loaded. Must be FFFFFFFF.
JE 0039568C
ADD ESI,DWORD PTR DS:[EDI+14]
LEA ESI,DWORD PTR DS:[ESI+EAX*8]
MOVZX EAX,WORD PTR DS:[ESI] <--------- [2] It takes from table "import code" (2,4,1,80, or 40).
CMP EAX,4
JE 00395522
CMP EAX,1
JE SHORT 003954F8
CMP EAX,80
JE 00395754
CMP EAX,40
JE 0039554E
MOVZX EAX,WORD PTR DS:[ESI+2]
PUSH 1
PUSH EAX
PUSH 0
PUSH DWORD PTR DS:[ESI+4]
PUSH DWORD PTR DS:[EDI]
CALL DWORD PTR DS:[EBX+50] <--------- If "import code" is 2, it finds that import here.
TEST EAX,EAX <----------------------- [3] EAX will be API value.
JNZ SHORT 0039550A
Most of imports in some file are "code 2" type. It is just what type of obfuscation obsidium will use.
I repaired imports like this using OllyScript. First, this is import table in our unpackme:
0040119C 00 00 B0 00 0C 00 B0 00 18 00 B0 00 24 00 B0 00 ............$...
004011AC 30 00 B0 00 3C 00 B0 00 48 00 B0 00 54 00 B0 00 0...<...H...T...
004011BC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004011CC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004011DC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004011EC 60 00 B0 00 6C 00 B0 00 78 00 B0 00 84 00 B0 00 `...l...x.......
004011FC 90 00 B0 00 9C 00 B0 00 A8 00 B0 00 B4 00 B0 00 ................
0040120C C0 00 B0 00 CC 00 B0 00 D8 00 B0 00 E4 00 B0 00 ................
It is very small. I placed breakpoints on [1], [2] and [3] lines in obsidium code. Then I wrote small script that will find one import call, place eip=that_line, run, check if DLL is OK [1] , check if "import code" is 2 [2], and in that case it will break on [3]. There eax holds correct import, now my script will take that API, place it in IAT, restore stack to default value and go searching for next one.
Basicly, I'm just using obsidium import algorithm against itself. Better would be, ofcourse, to code some app or plugin that could just do this by itself. After using script, IAT is:
0040119C 6E ED D4 77 BB D7 D4 77 7C B5 D4 77 0B 05 D8 77 n..w...w|..w...w
004011AC B2 02 D7 77 54 05 D5 77 9F F2 D6 77 54 00 B0 00 ...wT..w...wT...
004011BC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004011CC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004011DC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004011EC 60 00 B0 00 37 97 80 7C 29 B5 80 7C 2D FF 80 7C `...7..|)..|-..|
004011FC 2F FE 80 7C D4 05 91 7C AE 30 82 7C 29 29 81 7C /..|...|®0.|)).|
0040120C 10 11 81 7C 3D 04 91 7C B9 8F 83 7C E4 00 B0 00 ...|=..|...|....
Three imports are not repaired because they are not "code 2" type. I could write new script for those types, or rewrite this one, but that would first need examning obsidium code again (I'm little tired now) , so I will just try to fix it manually.
If I check "all intermodular calls", I see that only one call is bad:
0040404A CALL DWORD PTR DS:[4011EC] DS:[004011EC]=00B00060
00404EF2 CALL DWORD PTR DS:[40119C] USER32.CallNextHookEx
00404F89 CALL DWORD PTR DS:[4011F0] kernel32.GetCurrentThreadId
00404C66 CALL DWORD PTR DS:[4011A0] USER32.GetDesktopWindow
00404019 CALL DWORD PTR DS:[4011F4] kernel32.GetModuleHandleA
00404C7F CALL DWORD PTR DS:[4011A4] USER32.GetWindowRect
004057CE CALL DWORD PTR DS:[4011F8] kernel32.GlobalAlloc
00405614 CALL DWORD PTR DS:[4011FC] kernel32.GlobalFree
00404622 CALL DWORD PTR DS:[401204] kernel32.HeapCompact
00404453 CALL DWORD PTR DS:[401208] kernel32.HeapCreate
00404584 CALL DWORD PTR DS:[40120C] kernel32.HeapDestroy
00404326 CALL DWORD PTR DS:[401214] kernel32.lstrcatA
00404FC9 CALL DWORD PTR DS:[4011A8] USER32.MessageBoxA
004045EF CALL DWORD PTR DS:[401200] ntdll.RtlAllocateHeap
0040463E CALL DWORD PTR DS:[401200] ntdll.RtlAllocateHeap
004046DD CALL DWORD PTR DS:[401210] ntdll.RtlFreeHeap
00404FA4 CALL DWORD PTR DS:[4011AC] USER32.SetWindowsHookExA
00404C17 CALL DWORD PTR DS:[4011B0] USER32.SystemParametersInfoA
00404FDB CALL DWORD PTR DS:[4011B4] USER32.UnhookWindowsHookEx
I traced that call, it is "code 40" type which then lead to ExitProcess:
0039562E FFD0 CALL EAX ; kernel32.ExitProcess
When I found that import, I repaired dump and it worked.
4. Final words
Altough target works, job is not completed. Stolen OEP code can be easily restored if target is not compiled in ASM since obsidium steals only few opcodes. Imports that are not used in target need to be examned.
Obsidium has some more tricks like relocating image and little different import protection, but I will maybe onother tutorial on obsidium. It jus needs little more researching.
I hope that this tutorial will be of some use to somebody. Scripts that I used are included in archive, OEP one should work one evry Obsidium 1.2.5.0 target, while IAT one needs to be used as described in this tutorial. I'm not sure will my dump work on other machines, this was simply first touch on this good protector.
-
Level : newbie
Victim: MSN Messenger
Goal: Patching it so we can run multiple sessions at once instead of just one.
Tools: A Debugger (I use Ollydbg)
Author: Devoney
--------------------------------------------------------------------------------
intro:
When you already have a MSN running you can not launch another MSN at the same time of the same version. When creating your own program you have to build in some security if you want people to be able to run only one session at the same time. So MSN has a moment where it checks if there is a previous version running. We need to find out where that is!
Get to work:
Ok, open msnmsgr.exe with your favorite debugger. Normally its localized in this dir: C:Program FilesMSN Messenger. Now set a breakpoint on every reference on th CreateEvent API. Maybe you will notice that its CreateEventA in MSN. The A only stand for 32-bit. Now run the program tru the debugger (in ollydbg press F9 or the play button). The debugger probarly breaks a couple of times, not much. In my case it breaks 3 times. Now we are not interested in all those CreateEventA moments. We are only interested in this piece of code:
005575E3 |> 68 4C4A4100 PUSH msnmsgr.00414A4C ; /EventName = "MSNMSGR"
005575E8 |. 53 PUSH EBX ; |InitiallySignaled
005575E9 |. 6A 01 PUSH 1 ; |ManualReset = TRUE
005575EB |. 53 PUSH EBX ; |pSecurity
005575EC |. FF15 58134000 CALL DWORD PTR DS:[; CreateEventA
The addresses might differ from yours but the way the security is coded is the same in every MSN so far. Take a look at the EventName (the first parameter pushed). Its MSNMSGR.
Now read this:
"Return Values
A handle to the event object indicates success. If the named event object existed before the function call, the function returns a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS. NULL indicates failure. To get extended error information, call GetLastError."
Now scroll a couple of lines down in the code of the debugger and notice this piece:
00557600 |. FF15 74144000 CALL DWORD PTR DS:[; [GetLastError
00557606 |. 3D B7000000 CMP EAX,0B7
0055760B |. 0F85 B5010000 JNZ msnmsgr.005577C6
So GetLastError api checks the value which was returned from CreateEventA. It compares it with 0B7. 0B7 probarly is the hex value for constant which says SUCCES or somekind. When there is no SUCCES (so there is already a version running) the API CreateEvent does not return SUCCES. And EAX wont be 0B7. So this jump isn't made when there is already a version running. So we have to be sure the jump is made no mather what! SO change the JNZ (which is a conditional jump) to a unconditional jump JMP. (still to the same address ofcourse). Now save the modifcations. In Ollydbg you do that by right mouse click in the screen -> Copy to executable -> All Modifications -> Copy All -> Right mouse button in the new screen -> Save file. (this is not exactly the same for older versions of ollydbg.)
Now start the app as many times if you like and you see that it does not terminated itself anymore when there is another session running.
We could also speed up the code little. This jump can be made earlier because CreateEvent is not neccesary because the result of the checkup isn't important anymore. So go back to the CreateEventA api. Replace the first Parameter pushed "PUSH 414A4C" by "JMP " (in my case the address is "005577C6").
Outro:
Hopefully you have understood what I was trying to explain and that you have learned out of this! How did I know it was all about this CreateEvent? I started to set a breakpoint on ExitProcess and worked my way back to see where it came from when there was already a version of msn running. Then I compared the code when there was no MSN running so that I knew I was not too far already. Finally after 2 hours I ended up with CreateEvent.
Devoney
-
Level : beginner
=======================
PESpin 1.304 public - repairing IAT
=======================
This version of PESpin is completly indentical as previous 1.3. There are no new things in this public version interesting for unpacking, because this version is out just for compability issues. But in previous tutorials there was no explanation for proper IAT fixing , just some tricks to run dump on user machine, so that part will be covered in this tutorial better.
1. Tools and stuff
Tools are usual:
- OllyDbg 1.10
- ImpREC
- LordPE
- Windows XP
This tutorial will be short, so it would not be bad that you check previous ones which are more detailed.
2. OEP and stolen code
If you remember from past tutorials, last API that PESpin will use before jumping to OEP is GetTickCount. It will probably use it to get some random value, to destroy code/data that it doesn't need anymore. So we can place breakpoint at the end of that API (PESpin checks first bytes for CC bytes) and return to protectors code. So we are now very close to OEP jump. PESpin will use long jump to jump into original code section. So we can go to beggining of this PESpin section and search for bytes E9??????FF which corespond to long jump. Then we find all those possible jumps and first one that jumps in 401000 section is good one. I found this jump:
00417BC4 -E9 0F9AFEFF JMP KeygenMe.004015D8
But PESpin also can stole some bytes from OEP, as said before. That stolen code is obfuscated, but it always start with POPAD opcode which is 61 in hexadecimal. So scroll up and search for this byte. You will find it obfuscated within call opcode:
00417AD8 E8 61F7D239 CALL 3A14723E
Patch first byte:
00417AD8 90 NOP
00417AD9 61 POPAD
Now place bp on POPAD , run and you will break there. There starts execution of stolen code, right after POPAD. But stolen bytes are obfuscated with junk code. It is not big problem to find it if we know that our target is Visual C++ program. Code obfuscation isn't hard and I can find exact bytes:
00417B36 55 PUSH EBP
00417B3A 8BEC MOV EBP,ESP
00417B3F 6A FF PUSH -1
00417B44 68 3482111F PUSH 1F118234 <------------------ These 4 opcodes are just obfuscation of two PUSH.
00417B49 812C24 6421D11E SUB DWORD PTR SS:[ESP],1ED12164
00417B50 68 48F020F7 PUSH F720F048
00417B55 810424 B03D1F09 ADD DWORD PTR SS:[ESP],91F3DB0
00417B5C 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
00417B65 50 PUSH EAX
00417B69 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
00417B73 83EC 58 SUB ESP,58
00417B79 53 PUSH EBX
00417B7D 56 PUSH ESI
00417B81 57 PUSH EDI
00417B85 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
00417B8B FF15 CDA44100 CALL DWORD PTR DS:[41A4CD] <---- Obfuscated API call!
00417B94 33D2 XOR EDX,EDX
00417B99 8AD4 MOV DL,AH
00417B9E 8915 5C764000 MOV DWORD PTR DS:[40765C],EDX
00417BA7 8BC8 MOV ECX,EAX
00417BAC 81E1 FF000000 AND ECX,0FF
00417BB5 890D 58764000 MOV DWORD PTR DS:[407658],ECX
00417BBE C1E1 08 SHL ECX,8
00417BC4 -E9 0F9AFEFF JMP KeygenMe.004015D8 <------ Not stolen, it's jump to false OEP.
And we can restore later those bytes. But these 4 ones:
PUSH 1F118234
SUB DWORD PTR SS:[ESP],1ED12164
PUSH F720F048
ADD DWORD PTR SS:[ESP],91F3DB0
with:
PUSH 4060D0
PUSH 402DF8
But all that is not necessery at all. We could just dump at this line:
00417B36 55 PUSH EBP
and dump would be good. Just OEP would be in PESpin section and not in first one.
3. Redirected code
If we execute OEP jump:
00417BC4 -E9 0F9AFEFF JMP KeygenMe.004015D8
we will be in code section where we can see redirected code
004015EB . E8 E8EBFFFF CALL KeygenMe.004001D8
It is all well known from previous version. CALL leads to
004001D8 -E9 E6160000 JMP KeygenMe.004018C3
and that JMP just returns to some location. So CALL to PE header was in original file CALL to some address.
Second redirection looks like this:
0040169E .-E9 95EBFFFF JMP KeygenMe.00400238
jumps to
00400238 68 FF000000 PUSH 0FF <--------------- This is redirected opcode.
0040023D -E9 61140000 JMP KeygenMe.004016A3 <-- Returning back.
Jumps are substitution pushes.
There is new redirection that I didn't see in 1.3 version of PEspin:
00401687 $-E9 94EBFFFF JMP KeygenMe.00400220
0040168C 90 NOP
0040168D 90 NOP
jumps to
00400220 833D 38764000 01 CMP DWORD PTR DS:[407638],1 <------ Redirected code.
00400227 -E9 62140000 JMP KeygenMe.0040168E <------------ Returning back.
And now I can write small script for OllyScrip plugn that can fix that code. See it in attachment. That script will work (it should) for all 1.x versions of PESpin.
Blocks of code that are decrypted during runtime, so called "clear and crypt markers" will not be discussed in this tutorial. Check last one for that.
And that is almost all. Just IAT is left to fix :)
4. Problem with IAT
This is not hard problem, but in previous tutorial I didn't have enough knowlege about ImpREC, PE structure, etc. First we need to see what happened with our imports and then we will think how to fix them. I was placing breakpoints on LoadLibraryA and GetProcAddress to catch where imports are being processed, but I found nothing. That means PESpin doesn't use use those api's to load libraries and find api's, instead it may have custom coded functions to do such job. Then I checked already loaded modules when target is opened in olly and sow that all needed dll's are already loaded by windows loader:
Executable modules
Base Size Entry Name File version Path
00400000 0001C000 004160D4 KeygenMe D:PESpinPESpin v1.304KeygenMe.exe
77340000 0008B000 773419ED COMCTL32 5.82 (xpsp1.0208 C:WINDOWSsystem32COMCTL32.DLL
77D40000 0008C000 77D53A05 USER32 5.1.2600.1561 (x C:WINDOWSsystem32USER32.DLL
77DD0000 0008D000 77DD1D3D ADVAPI32 5.1.2600.1106 (x C:WINDOWSsystem32ADVAPI32.dll
77E60000 000E6000 77E7ADB3 kernel32 5.1.2600.1560 (x C:WINDOWSsystem32kernel32.dll
77F50000 000A7000 ntdll 5.1.2600.1106 (x C:WINDOWSSystem32ntdll.dll
78000000 00087000 78001E0D RPCRT4 5.1.2600.1361 (x C:WINDOWSsystem32RPCRT4.dll
7F000000 00041000 GDI32 5.1.2600.1561 (x C:WINDOWSsystem32GDI32.dll
If I just run target in olly, no new module is loaded. That give us first possible answer why LoadLibraryA isn't used. Actually it is used couple times, but not for API redirecting purpose. GetProcAddress is also used couple times but not for our api's. So I tok another approach to save time tracing trough code.
Let's find OEP and see where first import jump leads. This is first import jump, ie. call:
00401605 . FF15 C8A44100 CALL DWORD PTR DS:[41A4C8]
we see that is points to PESpin section and not to 406000 which should be IAT one. Check dump at that address:
0041A4C8 4B 02 3B 00 00 6A 02 3B 00 00 8E 02 3B 00 00 A9 K.;..j.;....;..©
0041A4D8 02 3B 00 00 BD 02 3B 00 00 DD 02 3B 00 00 F9 02 .;....;....;....
0041A4E8 3B 00 00 11 03 3B 00 00 30 03 3B 00 00 48 03 3B ;....;..0.;..H.;
...
We see that PESpin created table with pointers to 003B0000 section. Pointer dwords are separated with 0 bytes. That probably disturb ImpREC to get table, because pointers should be separated with zero terminating dword. And pointer leads to obfuscated api jump, or whole emulated api (if it's very small one, just one line):
003B024B EB 01 JMP SHORT 003B024E
003B024D D9A1 1476ED77 FLDENV (28-BYTE) PTR DS:[ECX+77ED7614]
003B0253 C3 RETN
It is easy to write small script for Olly that could rebuild new table, but we can remove obfuscation from import jumps. As easies option, I decide to place memory breakpoint on 003B0000 section so I could find place where PESpin writes obfuscated code there. Ok, bp on VirtualAlloc, and wait untill it allocates that section. Then memory bp on it. And I stopped here:
00416BCE 66:C706 EB01 MOV WORD PTR DS:[ESI],1EB
00416BD3 C646 02 D9 MOV BYTE PTR DS:[ESI+2],0D9
00416BD7 83C6 03 ADD ESI,3
00416BDA 2BD2 SUB EDX,EDX
00416BDC EB 01 JMP SHORT KeygenMe.00416BDF
00416BDE EA EB04EAEB 0400 JMP FAR 0004:EBEA04EB
...
...
PESpin writes here something. We are in part of code where obfuscation is in progress. If we remember from previous tutorial, this procedure starts with POPAD and ends with PUSHAD. Scroll up to find PUSHAD:
00416BAF 60 PUSHAD <----------------------- Yep, here it is.
00416BB0 EB 01 JMP SHORT KeygenMe.00416BB3
00416BB2 ^71 EB JNO SHORT KeygenMe.00416B9F
00416BB4 04 D5 ADD AL,0D5
00416BB6 EB 04 JMP SHORT KeygenMe.00416BBC
00416BB8 6BEB FB IMUL EBP,EBX,-5
...
...
We are very close to the end of our task, Place hardware bp on execution on that PUSHAD and restart target. Run it in Olly and you should break there. Ance again, in previous tutorial we sow that this procedure can be NOP-ed from PUSHAD to POPAD and we will kill obfuscation:
00416BAF 60 PUSHAD
00416BB0 90 NOP
00416BB1 90 NOP
00416BB2 90 NOP
...
...
...
00416C95 90 NOP
00416C96 90 NOP
00416C97 90 NOP
00416C98 61 POPAD
00416C99 C9 LEAVE
00416C9A C2 0800 RETN 8
This code also isn't checked with integrity checks so we can just leave it. Now we find stolen code like described in first part of this tutorial (beware, GetTickCount is already used so you need find address of code before this step), and we restore it on right OEP:
00401591 /. 55 PUSH EBP
00401592 |. 8BEC MOV EBP,ESP
00401594 |. 6A FF PUSH -1
00401596 |. 68 D0604000 PUSH KeygenMe.004060D0
0040159B |. 68 F82D4000 PUSH KeygenMe.00402DF8 ; SE handler installation
004015A0 |. 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
004015A6 |. 50 PUSH EAX
004015A7 |. 64:8925 000000>MOV DWORD PTR FS:[0],ESP
004015AE |. 83EC 58 SUB ESP,58
004015B1 |. 53 PUSH EBX
004015B2 |. 56 PUSH ESI
004015B3 |. 57 PUSH EDI ; KeygenMe.004188EA
004015B4 |. 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
004015B7 |. FF15 CDA44100 CALL DWORD PTR DS:[41A4CD] ; kernel32.GetVersion
004015BD |. 33D2 XOR EDX,EDX
004015BF |. 8AD4 MOV DL,AH
004015C1 |. 8915 5C764000 MOV DWORD PTR DS:[40765C],EDX
004015C7 |. 8BC8 MOV ECX,EAX
004015C9 |. 81E1 FF000000 AND ECX,0FF
004015CF |. 890D 58764000 MOV DWORD PTR DS:[407658],ECX
004015D5 |. C1E1 08 SHL ECX,8
Then we use script for fixing redirected code.
5. Fixing IAT
And now we can fix IAT. I wrote small script just for this VC++ app that will change all pointers to new tab. Example, this call:
004015B7 |. FF15 CDA44100 CALL DWORD PTR DS:[41A4CD] ; kernel32.GetVersion
will now point to new place
004015B7 |. FF15 90604100 CALL DWORD PTR DS:[416090] ; kernel32.GetVersion
and there I created new tab:
00416000 00 00 00 00 00 00 00 00 E0 56 D6 77 00 00 00 00 .........V.w....
00416010 1F 97 D4 77 00 00 00 00 1D 5F D4 77 00 00 00 00 ...w....._.w....
00416020 52 65 D6 77 00 00 00 00 52 65 D6 77 00 00 00 00 Re.w....Re.w....
00416030 52 65 D6 77 00 00 00 00 E4 D9 D4 77 00 00 00 00 Re.w.......w....
00416040 FE 65 D6 77 00 00 00 00 FE 65 D6 77 00 00 00 00 .e.w.....e.w....
00416050 >FE 65 D6 77 00 00 00 00 0F 6E D5 77 00 00 00 00 .e.w.....n.w....
00416060 >0F 6E D5 77 00 00 00 00 0F 6E D5 77 00 00 00 00 .n.w.....n.w....
00416070 93 5A E7 77 00 00 00 00 0C 16 E6 77 00 00 00 00 .Z.w.......w....
00416080 53 95 E7 77 00 00 00 00 A1 16 F5 77 00 00 00 00 S..w.......w....
00416090 95 D0 E7 77 00 00 00 00 AB E2 E7 77 00 00 00 00 ...w.......w....
004160A0 7E 17 E6 77 00 00 00 00 D9 AC E7 77 00 00 00 00 ~..w.......w....
004160B0 63 98 E7 77 00 00 00 00 25 E2 E7 77 00 00 00 00 c..w....%..w....
004160C0 7C 88 E7 77 00 00 00 00 A1 16 F5 77 00 00 00 00 |..w.......w....
004160D0 6B 15 F5 77 00 00 00 00 5F 8C F5 77 00 00 00 00 k..w...._..w....
004160E0 A1 16 F5 77 00 00 00 00 C5 AB E7 77 00 00 00 00 ...w.......w....
004160F0 6B 15 F5 77 00 00 00 00 C5 AB E7 77 00 00 00 00 k..w.......w....
00416100 0C E6 E7 77 00 00 00 00 B8 16 E6 77 00 00 00 00 ...w.......w....
00416110 63 98 E7 77 00 00 00 00 95 9B E9 77 00 00 00 00 c..w.......w....
00416120 FC AC E7 77 00 00 00 00 3B 95 E6 77 00 00 00 00 ...w....;..w....
00416130 E8 E4 E7 77 00 00 00 00 3B 95 E6 77 00 00 00 00 ...w....;..w....
00416140 2F E0 E9 77 00 00 00 00 7E 17 E6 77 00 00 00 00 /..w....~..w....
00416150 4E E3 E7 77 00 00 00 00 E7 E3 E7 77 00 00 00 00 N..w.......w....
00416160 4E E3 E7 77 00 00 00 00 A4 E2 E7 77 00 00 00 00 N..w.......w....
00416170 FC AC E7 77 00 00 00 00 E7 E3 E7 77 00 00 00 00 ...w.......w....
00416180 8D F0 E7 77 00 00 00 00 53 95 E7 77 00 00 00 00 ...w....S..w....
00416190 1B E0 E7 77 00 00 00 00 7F C9 E6 77 00 00 00 00 ...w.......w....
004161A0 7F C9 E6 77 00 00 00 00 4F A7 E7 77 00 00 00 00 ...w....O..w....
004161B0 4F A7 E7 77 00 00 00 00 1B E0 E7 77 00 00 00 00 O..w.......w....
004161C0 B1 E2 E7 77 00 00 00 00 B1 E2 E7 77 00 00 00 00 ...w.......w....
004161D0 6B 15 F5 77 00 00 00 00 A1 16 F5 77 00 00 00 00 k..w.......w....
004161E0 B4 D8 E7 77 00 00 00 00 D1 DD E7 77 00 00 00 00 ...w.......w....
004161F0 84 9F E7 77 00 00 00 00 84 9F E7 77 00 00 00 00 ...w.......w....
00416200 4F A7 E7 77 00 00 00 00 4F A7 E7 77 00 00 00 00 O..w....O..w....
00416210 D1 DD E7 77 00 00 00 00 D1 DD E7 77 00 00 00 00 ...w.......w....
00416220 D1 DD E7 77 00 00 00 00 9C A8 E7 77 00 00 00 00 ...w.......w....
00416230 E8 D8 E7 77 00 00 00 00 23 5D E7 77 00 00 00 00 ...w....#].w....
00416240 23 5D E7 77 00 00 00 00 B1 E2 E7 77 00 00 00 00 #].w.......w....
00416250 4F A7 E7 77 00 00 00 00 4F A7 E7 77 00 00 00 00 O..w....O..w....
00416260 E8 D8 E7 77 00 00 00 00 B4 90 E6 77 00 00 00 00 ...w.......w....
00416270 A1 16 F5 77 00 00 00 00 5F 8C F5 77 00 00 00 00 ...w...._..w....
00416280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00416290 C2 25 E7 77 00 00 00 00 DE AF E7 77 00 00 00 00 .%.w.......w....
004162A0 44 0C F6 77 00 00 00 00 00 00 00 00 00 00 00 00 D..w............
004162B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...............
Now we have thunks where every import value is separated with 4 zero terminating bytes and ImpREC can easily read this. Now we just run ImpREC and fix dumped file. Save file, run,..., yep, it works :)
It is not perfect work because thunks that belong to same dll should be sticked together which would reduce size of file, also we could remove PESpin section, but hey, now dump works on every machine :)
-
Level : intermediate
==============================================
ASProtect 2.0 - unpacking and repairing IAT
==============================================
1. Tools and stuff
In this tutorial we will see how can ASPR 2.0 be unpacked manually. It is not so hard , but you should have experience in manually unpacking, some knowledge about PE structure, and ASProtect shouldn't be new to you. If you never played with ASPR before, check my tutrial about ASPR 1.23RC4 that will intruduce you.
Target for this tutorial is black-eye's "nightmare keygenme". I think that it is protected with all options that ASPR2.0 has.
- Windows XP
- OllyDbg 1.10
- ImpREC
- LordPE
- hex editor
2. OEP and stolen code
After loading target in Olly, I ignored all exceptions and just runned target trough olly. After keygenme opened, I checked log window to see what exceptions ASPR produced:
************************************************** ********************
Log data
Address Message
77C00000 Module C:WINDOWSsystem32version.dll
71AD0000 Module C:WINDOWSSystem32wsock32.dll
71AB0000 Module C:WINDOWSSystem32WS2_32.dll
00987758 Access violation when writing to [00000000]
00986BE6 Access violation when writing to [00000000]
00986C6B Access violation when writing to [00000000]
00986EA2 Access violation when writing to [00000000]
0098703B Access violation when writing to [00000000]
00987174 Access violation when writing to [00000000]
009871BD Access violation when writing to [00000000]
00984760 Access violation when writing to [00000000]
009847D8 Access violation when writing to [00000000]
00984966 Access violation when writing to [00000000]
00984C0F Access violation when writing to [00000000]
00984D5B Access violation when writing to [00000000]
00984E3D Access violation when writing to [00000000]
009872D8 Access violation when writing to [00000000]
0098732F Access violation when writing to [00000000]
009874F1 Access violation when writing to [00000000]
009875AA Access violation when writing to [00000000]
00987683 Access violation when writing to [00000000]
0098341E INT3 command at 0098341E
009850B4 Access violation when writing to [00000000]
0098418C Access violation when writing to [00000000]
00984328 Access violation when writing to [00000000]
00985268 Access violation when writing to [00000000]
00985389 Access violation when writing to [00000000]
00986487 Access violation when writing to [00000000]
00985616 Access violation when writing to [00000000]
00985AAF Access violation when writing to [00000000]
00985BF0 Access violation when writing to [00000000]
0097CC59 Access violation when writing to [00000000]
00985DD4 Access violation when writing to [00000000]
00985F95 Access violation when writing to [00000000]
00986050 Access violation when writing to [00000000]
00987995 Access violation when writing to [00000000]
00987A9A Access violation when writing to [00000000]
00987C95 Access violation when writing to [00000000]
00987EAD Access violation when writing to [00000000]
0098806A Access violation when writing to [00000000]
009881EF Access violation when writing to [00000000]
009833BC INT3 command at 009833BC
0098341E INT3 command at 0098341E
00986807 Access violation when writing to [00000000]
00986A16 Access violation when writing to [00000000]
71AA0000 Module C:WINDOWSSystem32WS2HELP.dll
************************************************** *******************
What we can see from this log? ASPR produces lot of memory acces violation exceptions and three INT3 ones. In many tutorials you can read "uncheck all exceptions and press Shift+F9 untill you reach last one...". In ASPR we can use exceptions for getting close to OEP, that is correct, but we can make that little bit easier. Since we have only three INT3 exceptions and two of them are very close to the end, we can ignore all exceptions and uncheck only INT3. Then we press Shift+F9 3 times to stop on last INT3, we place memory bp on CODE section, Shift+F9 again and we will break on false OEP:
00406E1A E8 E1918900 CALL 00CA0000
00406E1F 36:31D2 XOR EDX,EDX
00406E22 8AD4 MOV DL,AH
00406E24 8915 B4D04000 MOV DWORD PTR DS:[40D0B4],EDX
00406E2A 89C1 MOV ECX,EAX
00406E2C 81E1 FF000000 AND ECX,0FF
00406E32 890D B0D04000 MOV DWORD PTR DS:[40D0B0],ECX
00406E38 C1E1 08 SHL ECX,8
00406E3B 01D1 ADD ECX,EDX
00406E3D 890D ACD04000 MOV DWORD PTR DS:[40D0AC],ECX
00406E43 C1E8 10 SHR EAX,10
...
...
First call that you see is some redirected import and that will be explained in next part, but scroll up to see what happened with OEP code:
00406DF4 -E9 7F937100 JMP 00B20178
00406DF9 54 PUSH ESP
00406DFA DEC0 FADDP ST,ST
00406DFC 888F 99E23384 MOV BYTE PTR DS:[EDI+8433E299],CL
00406E02 AD LODS DWORD PTR DS:[ESI]
00406E03 0F71B7 30B961C3 >PSLLW QWORD PTR DS:[EDI+C361B930],0C4
00406E0B 1B27 SBB ESP,DWORD PTR DS:[EDI]
00406E0D 3A5E 01 CMP BL,BYTE PTR DS:[ESI+1]
00406E10 A3 AA614693 MOV DWORD PTR DS:[934661AA],EAX
00406E15 B1 4B MOV CL,4B
00406E17 2F DAS
00406E18 FA CLI
00406E19 81E8 E1918900 SUB EAX,8991E1
...
...
We can see lot of weird junky code. That is just garbage. Real code is redirected and obfuscated. Real OEP should be on this address instead this jump:
00406DF4 -E9 7F937100 JMP 00B20178
That jump leads to some block where stolen code is hidden. Follow that jump and you will find it:
00B20178 55 PUSH EBP
00B20179 89E5 MOV EBP,ESP
00B2017B 6A FF PUSH -1
00B2017D F3: PREFIX REP:
00B2017E EB 02 JMP SHORT 00B20182
00B20180 CD 20 INT 20
00B20182 FF7424 1C PUSH DWORD PTR SS:[ESP+1C]
...
...
00B20242 5E POP ESI
00B20243 5B POP EBX
00B20244 F3: PREFIX REP:
00B20245 EB 02 JMP SHORT 00B20249
00B20247 CD 20 INT 20
00B20249 66:9D POPFW
00B2024B ^E9 62FFFFFF JMP 00B201B2
00B20250 F4 HLT
00B20251 6D INS DWORD PTR ES:[EDI],DX
00B20252 0000 ADD BYTE PTR DS:[EAX],AL
00B20254 0000 ADD BYTE PTR DS:[EAX],AL
00B20256 0000 ADD BYTE PTR DS:[EAX],AL
...
...
Ofcourse, code is obfuscated. There is two ways for fixing stolen code. First way is to binary copy all that bytes and after dumping just attach them to main dump and set OEP there. Second way is to find exact bytes which is not hard, app is VC++ and since we know how VC++ OEP looks we know what to look for. I will leave this for end. That is easy job. But lets write down some important information:
00406DF4 - jump to stolen code
3. Import problems
There is couple different ways how imports are processed.
3.a) First way:
First is that ASPR replaces import jumps/calls with calls:
00406E1A E8 E1918900 CALL 00CA0000
00406E1F 36:31D2 XOR EDX,EDX
Instead this CALL CONSTANT (which is 5 bytes long opcode), in original file was CALL DWORD[IMPORT] (6 bytes long). Restarting target and placing memory breakpoint on that line will get us to place where ASPR writes this calls (we will break couple times before this location):
0097377E 8BC0 MOV EAX,EAX
00973780 53 PUSH EBX
00973781 8BD8 MOV EBX,EAX
00973783 8BC3 MOV EAX,EBX
00973785 E8 56FFFFFF CALL 009736E0
0097378A C603 E8 MOV BYTE PTR DS:[EBX],0E8 <----- Here it writes CALL
0097378D 43 INC EBX
0097378E 8903 MOV DWORD PTR DS:[EBX],EAX <---- And here it writes destination.
00973790 5B POP EBX
00973791 C3 RETN
But check registers window:
EAX 008991E1 <------------------------ Redirection destination.
ECX 0012FF01
EDX 008991E1
EBX 00406E1A ASProtec.00406E1A <------ Address where import jump is.
ESP 0012FEF0
EBP 77E7D095 kernel32.GetVersion <---- This is API that is beeing redirected!
ESI 00406E1C ASProtec.00406E1C
EDI 009B48E8
EIP 0097378A
Since we see what API call should be there, we can change this code to write good import:
00973780 66:C700 FF15 MOV WORD PTR DS:[EAX],15FF
00973785 40 INC EAX ; ASProtec.004011A4
00973786 40 INC EAX ; ASProtec.004011A4
00973787 8910 MOV DWORD PTR DS:[EAX],EDX
00973789 892A MOV DWORD PTR DS:[EDX],EBP ; kernel32.GetVersionExA
0097378B 90 NOP
0097378C 90 NOP
0097378D 90 NOP
0097378E 90 NOP
0097378F 90 NOP
00973790 90 NOP
00973791 C3 RETN
66 C7 00 FF 15 40 40 89 10 89 2A 90 90 90 90 90 90
Now , our call will look like this:
00406E1A . FF15 0000CA00 CALL DWORD PTR DS:[CA0000] ; kernel32.GetVersion
00406E20 . 31D2 XOR EDX,EDX
Not bad for start, keygenme works OK with all these calls changed altough this is not yet good for ImpREC.
3.b) Second way:
00401043 FF15 FC489B00 CALL DWORD PTR DS:[9B48FC] ; kernel32.CreateFileA
Well, this is same thing as to what we bring our first example. We are skipping this for now.
3.c) Third way:
004030A8 FF15 8C499B00 CALL DWORD PTR DS:[9B498C]
Again, memory bp on it and find the routine:
0097CEAD 8B5424 0C MOV EDX,DWORD PTR SS:[ESP+C]
0097CEB1 8902 MOV DWORD PTR DS:[EDX],EAX
0097CEB3 8B4424 0C MOV EAX,DWORD PTR SS:[ESP+C]
0097CEB7 8906 MOV DWORD PTR DS:[ESI],EAX <------- Here it writes pointer!
0097CEB9 0FB74424 04 MOVZX EAX,WORD PTR SS:[ESP+4]
0097CEBE 0143 08 ADD DWORD PTR DS:[EBX+8],EAX
0097CEC1 ^E9 ADFDFFFF JMP 0097CC73 <--------------------- I have decided to redirect this jump.
0097CEC6 3C 04 CMP AL,4
0097CEC8 0F85 F4000000 JNZ 0097CFC2
Again, check registeres:
EAX 009B498C
ECX 00000000
EDX 009B498C <------------------------- Destination.
EBX 009B04D4
ESP 0012FEF8
EBP 77D47DE3 user32.ReleaseCapture <--- API.
ESI 004030AA ASProtec.004030AA <------- CALL pointer.
EDI 009B48E8
EIP 0097CEB7
I will redirect jump to beggining of loader section because it has empty space there. Then I will inject code there that will write API where call points. Changing this:
0097CEC1 ^E9 ADFDFFFF JMP 0097CC73
to this:
0097CEC1 ^E9 3A31FEFF JMP 00960000
And there, injection:
00960000 892A MOV DWORD PTR DS:[EDX],EBP <---- Place API to destination.
00960002 E9 6CCC0100 JMP 0097CC73 <------------------ Return to normal ASPR code.
After this , our api call looks:
004030A8 FF15 8C499B00 CALL DWORD PTR DS:[9B498C] ; user32.ReleaseCapture
Now I made one script that can do all these changes. After using it, target runs fine under Olly. Problem is that all this pointer needs to be placed inside main image of file. I explained how and way in many tutorials before, so I made new script that will do that. So I fixed IAT, then I dumped and attached OEP stolen code, and I used ImpREC to rebuild new IAT, but dump crashed. Why? Because VC++ has one more way to access imports.
You see, usually imports are acessed via JMP DWORD PTR[API_HERE] or CALL DWORD PTR[API_HERE], but VC++ also uses
MOV REGISTER,address
JMP REGISTER
where REGISTER holds API address, and couple more variations.
3.d) Fourth way:
Dump crashes at:
00408208 8B2D F44A9B00 MOV EBP,DWORD PTR DS:[9B4AF4]
I checked that line under Olly after using script:
00408208 8B2D F44A9B00 MOV EBP,DWORD PTR DS:[9B4AF4] ; kernel32.GetEnvironmentStringsW
Import is there. I should include it my script to move pointer in main image. Couple more examples:
0040827D 8B3D 0C4B9B00 MOV EDI,DWORD PTR DS:[9B4B0C]
0040344E 8B35 F8499B00 MOV ESI,DWORD PTR DS:[9B49F8]
After including it in script, I used it, dumped, rebuilded IAT with ImpREC, attached stolen code and saved all that. I double clicked on keygenme and it opened just fine :)! Yep, ASProtect 2.0 is defeated! Now try to keygen this crackme...
4. Couple words for end
Scripts that I have included here my not work for all other files packed with ASProtect 2.0. There are lot of builds of ASPR 2.0 which has some small differences that can disturb scripts. For example, script for finding OEP will not work if there is no stolen code. But this way of unpacking is pretty much the same for all ASPR 2.0 versions. Acctually, almost every ASPR version before 2.0 can be unpacked like this.
And that would be all for this tutorial. See you in next one ;)
-
Level : beginner
=========================
Fishing and keygenning games CD keys
=========================
Maybe the best protection today are game protections. Although such protections are very hard to unwrap, it's interesting how their key checks stay very simple in most cases.
Indeed, they are simple but the hardest problem is to actually find where the keygen algo lies in application. This tutorial will show a couple of examples on how game CD keys can be fished or keygened more or less with ease in some cases.
1. HL1
Half Life 1 is now pretty much old game from 10/30/98, but still impressive one and better than some todays. Game has very simple key check and this was first game that I manage to keygen. CD check is very simple too, but that is not objective of this tutorial. After installing game you will probably want to play it. At game start we get nice dialog asking us for game key. On inserting some fake key , we will normally get BadBoy message. Loading game exe in Olly, placing bp on GetWindowTextA, we can break when game is grabbing key from dialog:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
But main problem is to find where key check is. Games are big and find such algo is usually mayor problem. But after returning from api, I placed memory bp on serial in memory and found where algo reading it. This first check will read my serial:
It will count how many there is digits in serial and if there is no 0D(hex) digits, I will get BadBoy message. That mean, serial can be long as we wish and non-numeric characters are not important. Conclusion – serial is all in numbers and it's length is 13.
00442FB1 . 8A10 MOV DL,BYTE PTR DS:[EAX]
00442FB3 . 84D2 TEST DL,DL
00442FB5 . 74 0E JE SHORT hl.00442FC5
00442FB7 . 80FA 30 CMP DL,30
00442FBA . 7C 09 JL SHORT hl.00442FC5
00442FBC . 80FA 39 CMP DL,39
00442FBF . 7F 04 JG SHORT hl.00442FC5
00442FC1 . 8813 MOV BYTE PTR DS:[EBX],DL
00442FC3 . 43 INC EBX
00442FC4 . 46 INC ESI
00442FC5 > 40 INC EAX
00442FC6 . 83FE 0D CMP ESI,0D 8D7D BC LEA EDI,DWORD PTR SS:[EBP-44]
00442FCE . B9 FFFFFFFF MOV ECX,-1
00442FD3 . 2BC0 SUB EAX,EAX
00442FD5 . C603 00 MOV BYTE PTR DS:[EBX],0
00442FD8 . F2:AE REPNE SCAS BYTE PTR ES:[EDI]
00442FDA . F7D1 NOT ECX
00442FDC . 49 DEC ECX
00442FDD . 75 21 JNZ SHORT hl.00443000 85F6 TEST ESI,ESI
00442FE3 . 0F84 A5000000 JE hl.0044308E
I entered new serial to pass that check, 1111222233334. GoodBoy jump throws us below:
00443000 > 8D45 BC LEA EAX,DWORD PTR SS:[EBP-44]
00443003 . 8D4D F0 LEA ECX,DWORD PTR SS:[EBP-10]
00443006 . 50 PUSH EAX
00443007 . E8 D1020500 CALL hl.004932DD
0044300C . C645 FC 04 MOV BYTE PTR SS:[EBP-4],4
00443010 . E8 C2000000 CALL hl.004430D7
00443015 . C645 FC 01 MOV BYTE PTR SS:[EBP-4],1
00443019 . E8 C1000000 CALL hl.004430DF
0044301E > 8B45 F0 MOV EAX,DWORD PTR SS:[EBP-10]
00443021 . 8B4D EC MOV ECX,DWORD PTR SS:[EBP-14]
00443024 . 50 PUSH EAX
00443025 . E8 D6FCFFFF CALL hl.00442D00
0044302A . 85C0 TEST EAX,EAX
0044302C . 0F85 B8000000 JNZ hl.004430EA
This TEST EAX,EAX and CALL abowe are interesting. Tracing in we can see another length check:
00442D1C |. 83F9 0D CMP ECX,0D 33C0 XOR EAX,EAX
00442D38 |. 5F POP EDI
00442D39 . C2 0400 RETN 4
If we pass that check, we enter to serial algo routine which is very small and simple:
00401000 PUSH ESI
00401001 MOV EAX,3 31-30=1,
0040101D |XOR EDX,EDI
That algo will take al characters except last one. Then that summ in EAX is divided by 0Ah and we get reminder in EDX. Reminder is placed in AL then, and to EDX is placed last
00401026 MOV ECX,0A
0040102B SUB EDX,EDX
0040102D DIV ECX
0040102F MOVSX EAX,DL
And here is end. Remider must be equal to last character of serial. So my serial 111122223333(4) must be 111122223333(3) and CD key will be correct.
2. STEF2
Star Treck Elite Force 2 is shooting 3D game from 5/21/2003. Altough newer, keycheck is still very simple. Main problem would be to find actuall keycheck algorithm. Upon inserting CD and lounching installer, "CD key" dialog box shows:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
Todays games/games protections create couple temp files in temp folders which are actually dll’s. Some of those dll’s are checking CD key. But in this example, dll that check key is ordinal dll on CD, ef2dll.dll. There is also ef2dll.ini file in same folder. This ini file just holds GoodBoy/BadBoy messages.
Easy approach for finding key routine is to attach Olly to main exe, which is probably install louncher. In this example, application gets key characters using GetWindowTextA api. After entering fake serial, Olly stopped in user32.dll on api. Returning from api brings me to subroutine which is part of another one:
00AE13B0 . 53 PUSH EBX
00AE13B1 . 56 PUSH ESI
00AE13B2 . 8B7424 0C MOV ESI,DWORD PTR SS:[ESP+C] ; ef2dll.00AED059
00AE13B6 . 57 PUSH EDI
00AE13B7 . 8BF9 MOV EDI,ECX
00AE13B9 . 8D5F 5C LEA EBX,DWORD PTR DS:[EDI+5C]
00AE13BC . 53 PUSH EBX ; /Arg3 = 0013DAE8
00AE13BD . 68 D0070000 PUSH 7D0 ; |Arg2 = 000007D0
00AE13C2 . 56 PUSH ESI ; |Arg1 = 0013D4BC
00AE13C3 . E8 34DC0000 CALL ef2dll.00AEEFFC ; ef2dll.00AEEFFC
00AE13C8 . 6A 04 PUSH 4
00AE13CA . 53 PUSH EBX
00AE13CB . 56 PUSH ESI
00AE13CC . E8 7DDC0000 CALL ef2dll.00AEF04E
…
…
…
00AE1447 . E8 02DC0000 CALL ef2dll.00AEF04E
00AE144C . 83C7 74 ADD EDI,74
00AE144F . 57 PUSH EDI ; /Arg3 = 0013DA8C
00AE1450 . 68 D5070000 PUSH 7D5 ; |Arg2 = 000007D5
00AE1455 . 56 PUSH ESI ; |Arg1 = 0013D4BC
00AE1456 . E8 A1DB0000 CALL ef2dll.00AEEFFC ; ef2dll.00AEEFFC
00AE145B . 68 FF000000 PUSH 0FF
00AE1460 . 57 PUSH EDI
00AE1461 . 56 PUSH ESI
00AE1462 . E8 E7DB0000 CALL ef2dll.00AEF04E
00AE1467 . 5F POP EDI ; 00159448
00AE1468 . 5E POP ESI ; 00159448
00AE1469 . 5B POP EBX ; 00159448
00AE146A . C2 0400 RETN 4
This subroutine just collects characters from those 5 fields. And that routine is subroutine of main key check one:
00AE1620 . 83EC 0C SUB ESP,0C
00AE1623 . 53 PUSH EBX
00AE1624 . 55 PUSH EBP
00AE1625 . 56 PUSH ESI
00AE1626 . 57 PUSH EDI
00AE1627 . 8BE9 MOV EBP,ECX
00AE1629 . 6A 01 PUSH 1
00AE162B . C64424 17 00 MOV BYTE PTR SS:[ESP+17],0
00AE1630 . C74424 1C E8A4>MOV DWORD PTR SS:[ESP+1C],ef2dll.00AFA4E>
00AE1638 . E8 C7B90000 CALL ef2dll.00AED004
00AE16FF . 8BCA MOV ECX,EDX
00AE1701 . 83E1 03 AND ECX,3
00AE1704 . F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
00AE1706 . BF D8A4AF00 MOV EDI,ef2dll.00AFA4D8
00AE170B . 83C9 FF OR ECX,FFFFFFFF
00AE170E . F2:AE REPNE SCAS BYTE PTR ES:[EDI]
00AE1710 . F7D1 NOT ECX
00AE1712 . 49 DEC ECX
00AE1713 . 51 PUSH ECX
00AE1714 . E8 9A8B0000 CALL ef2dll.00AEA2B3
00AE1719 . 8BD8 MOV EBX,EAX
00AE171B . BF D8A4AF00 MOV EDI,ef2dll.00AFA4D8
00AE1720 . 83C9 FF OR ECX,FFFFFFFF
00AE1723 . 33C0 XOR EAX,EAX
00AE1725 . F2:AE REPNE SCAS BYTE PTR ES:[EDI]
00AE1727 . F7D1 NOT ECX
00AE1729 . 2BF9 SUB EDI,ECX
00AE172B . 68 D8A4AF00 PUSH ef2dll.00AFA4D8
00AE1730 . 8BC1 MOV EAX,ECX
00AE1732 . 8BF7 MOV ESI,EDI
00AE1734 . 8BFB MOV EDI,EBX
00AE1736 . C1E9 02 SHR ECX,2
00AE1739 . F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
00AE173B . 8BC8 MOV ECX,EAX
00AE173D . 83E1 03 AND ECX,3
00AE1740 . F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
00AE1742 . E8 79FEFFFF CALL ef2dll.00AE15C0
My serial is 11112222333344445555. In this call above, real serial will be calculated:
00AE15C0 PUSH
00AE15C1 MOV ESI,DWORD PTR SS:[ESP+8]
00AE15C5 XOR EAX,EAX
00AE15C7 XOR ECX,ECX
00AE15C9 SUB ESI,ef2dll.00AFA4C0
00AE15CF /MOV DL,BYTE PTR DS:[ESI+ECX+AFA4C0]
So correct serial is 11112222333444457EF. That check will be performed below:
00AE178F . BF D4A4AF00 MOV EDI,ef2dll.00AFA4D4 ; ASCII "57ef"
00AE1794 . C1E9 02 SHR ECX,2
00AE1797 . F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
00AE1799 . 8BCA MOV ECX,EDX
00AE179B . 83E1 03 AND ECX,3
00AE179E . F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
00AE17A0 . 33F6 XOR ESI,ESI
00AE17A2 > 33C0 XOR EAX,EAX
00AE17A4 . 8A86 D4A4AF00 MOV AL,BYTE PTR DS:[ESI+AFA4D4]
00AE17AA . 50 PUSH EAX
00AE17AB . E8 A20F0000 CALL ef2dll.00AE2752
00AE17B0 . 8B7C24 1C MOV EDI,DWORD PTR SS:[ESP+1C]
00AE17B4 . 33C9 XOR ECX,ECX
00AE17B6 . 83C4 04 ADD ESP,4
00AE17B9 . 8A0F MOV CL,BYTE PTR DS:[EDI]
00AE17BB . 47 INC EDI
00AE17BC . 3BC1 CMP EAX,ECX B2 01 MOV DL,1
00AE17D2 > 8BFB MOV EDI,EBX
00AE17D4 . 83C9 FF OR ECX,FFFFFFFF
00AE17D7 . 33C0 XOR EAX,EAX
00AE17D9 . 6A 00 PUSH 0
00AE17DB . F2:AE REPNE SCAS BYTE PTR ES:[EDI]
00AE17DD . F7D1 NOT ECX
00AE17DF . 2BF9 SUB EDI,ECX
00AE17E1 . 68 5C72AF00 PUSH ef2dll.00AF725C ; ASCII "CD Key"
00AE17E6 . 8BC1 MOV EAX,ECX
00AE17E8 . 8BF7 MOV ESI,EDI
00AE17EA . BF D8A4AF00 MOV EDI,ef2dll.00AFA4D8
00AE17EF . C1E9 02 SHR ECX,2
00AE17F2 . F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
00AE17F4 . 8BC8 MOV ECX,EAX
00AE17F6 . 83E1 03 AND ECX,3
00AE17F9 . 84D2 TEST DL,DL
00AE17FB . F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
00AE17FD . 74 13 JE SHORT ef2dll.00AE1812
00AE17FF . 8B4D 7C MOV ECX,DWORD PTR SS:[EBP+7C]
00AE1802 . 51 PUSH ECX
00AE1803 . 8BCD MOV ECX,EBP
00AE1805 . E8 9EB00000 CALL ef2dll.00AEC8A8 8B55 78 MOV EDX,DWORD PTR SS:[EBP+78]
00AE1815 . 8BCD MOV ECX,EBP
00AE1817 . 52 PUSH EDX
00AE1818 . E8 8BB00000 CALL ef2dll.00AEC8A8
As we sow, serial routine is very small and simple. It is easy to make small keygen that would give correct random keys and all games from this publisher (Activision) has similar algorithm , from older RTCW (2001) to newer Doom3 (2005).
3. FF
Freedom fighters is game from well known Electronic Arts developers. Date of game is 29-July-2003. Game has interesting registration algo, very easy to find it. In a one folder on CD, there is "Freedom Fighters_Code.exe" that is doing registration check. Running it gives registration dialog:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
Finding place where serial is taken from dialog is not easy since all api's that can do that are triggered numerous times. GetWindowsTextA is api that takes serial, but I tok different approach. On "Next>" click, program shows error message giving user information that serial is invalid. I just placed bp on MessageBoxA and found routine that calls it. Then I placed bp on beggining of that procedure. Rest was easy , just tracing and I found algo. However, there is second way. This registration app uses CRC32 table while producing serial. We can simply scan app with PEiD and found where is CRC32 referenced. And all EA games have same registration scheme (that means hundreds of games).
Algo is little long so I want paste it here. I entered 1234567890ABCDEFGHIJ as serial. First algo creates CRC32 table with constants. Then it swaps places of 14 characters in my serial
ASCII "1234567890ABCDEFGHIJ"
ASCII "1IDJEF7G90ABH3568C24"
It takes 13 chars from such serial and caculate some summ of it:
ASCII "1IDJEF7G90ABH"
16FA0346
16FBC8B8
then is some string calculated:
ASCII "6MRZU9A"
which is concated to 13 char one:
ASCII "1IDJEF7G90ABH6MRZU9A"
On a base of that string characters, algo picks dwords from CRC32 table and calculates some summ's and then new string is created:
ASCII "FTGLRUP"
again attached to that 13 chars:
ASCII "1IDJEF7G90ABHFTGLRUP")
and chars are swaped again like first time:
ASCII "1UFPTG7L90ABRDEFGHIJ"
And that makes valid CD key.
Algo easily can be ripped for making generic keygen. As said before, ALL EA games from that year has same register algo. I sow that in "Medal Of Honnor - Paccific Assault" , algo is little different. They probably every year change algo a little.
4. Conclusion
As you can see, it is not so hard to fish serial or to keygen some game. Some games uses Wise Installer and there serial check differ because wise installer uses scripts (.msi files). In this case finding serial can be hard but there are some decompilers like "Wise For Windows Installer" that can help.
-
Level : newbie
Type: Patching
Victim: Taskmgr.exe
Protection: register value
Tool: Ollydbg
Intro:
Ever had the problem when you try to start taskmanager it says it has been disabled by your administrator? Well that can be solved, off course ;) There is place in the registry where you can disable or enable the use of Taskmg.exe (I am talking about the original one which comes with your windows installation).
Get to work:
Fire up ollydbg and open taskmgr.exe, (probably located in the system or system32 folder in your windows installation map). Get to the main window (press ALT + C to get there). You see a lot of code. Now go to the following line:
010039EB |. FF15 28100001 CALL DWORD PTR DS:[; RegOpenKeyExW
How did I get to this line? I was just searching for this api, since I know the security works by communicated with the registry to check if you are allowed to use Taskmgr.exe. You can not set a breakpoint on every reference of this api in ollydbg because it will generate some memory access errors (or something like that). So get to the line ;) Right mouse button in the main window -> Goto -> Expression. Fill in the address of the line. Which will be 010039EB. Press ok. You can set a breakpoint here and check if ollydbg breaks.
So this line you have found gets a value out the registry (which probably checks if you are allowed to access the taskmgr.exe). Take a look at the two lines below 010039EB.
010039F1 |. 85C0 TEST EAX,EAX 010039F3 |. 74 6F JNZ SHORT TASKMGR.01003A64
It checks the returned value of EAX, and it doesn’t jump when they returned value is zero. Look a bit further down the code and you will run into this line:
01003A09 |. 68 98160001 PUSH TASKMGR.01001698 ; |ValueName = "DisableTaskMgr" 01003A0E |. FF75 EC PUSH DWORD PTR SS:[EBP-14] ; |hKey 01003A11 |. FF15 2C100001 CALL DWORD PTR DS:[; RegQueryValueExW
You see it has a text line: DiableTaskMgr. That doesn’t sound very good. Now take a look at the conditional jump we saw earlier. If it DOES jump, it jumps to line 01003A64 and skips the lines which will disable you taskmgr.exe. So change that jump to JMP or JE so it does jump to skip the security check ;). (Change it by clicking on the code or by selecting the line and press SPACEBAR).
Now rightmouse button in the mainwindow -> Copy to executable -> All modifications.
Press copy call, a new window will pop-up. Right mouse button in that window and choose to save your file! Now open up your file and it should work now!
Outro:
I have included the original Taskmg.exe, the tutorial itself in .Doc format and the patched version (called Taskmgr_patched.exe) Hopefully you learned something and understood me.
-
به نام خدا
به زودی مقالات سطح متوسط و پیشرفته و فوق پیشرفته قرار می دهم .