Thinstall 2.521 - unpacking dependencies and injecting a DLL

Saturday, July 08 2006 @ 07:14 PM CEST

Contributed by: haggar

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
- here is stuff http://www.reversing.be/binaries/articles/20060708190831876.rar





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 , SND, 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????020083A544FCFFFF008065FC0083BDD4FDFFFF007412#
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



0 comments



http://www.reversing.be/article.php?story=20060708191417910