Log in

View Full Version : G6 FTP server


jomamameister
January 8th, 2002, 10:26
hello all,
i'm looking for some guidance in finishing this unpacking project on the g6 ftp server.
what i have done:
1. unpack file through dumpfx
2. used revirgin 1.2 to create an iat resolved .txt file
3. not all references are resolved, they are simply zeroes or numeric references to a module that doesn't show up in the modules listing in revirgin when the proc is running.
4. used revirgin to insert this mostly resolved file into the end of the dumped.exe

of course, it still does not work.
most importantly, i seem to be missing the new oep for the unpacked .exe, as it is exactly the same as before
any suggestions for me to try next to retrieve the new oep and to get these possible references to an obscure module? or anything else i may be leaving out.
thanks,
jomamameister

Js
January 8th, 2002, 11:04
Hi,
Can't help with the unresolved iat as I didn't on this occasion use
rv. Problems when resolved are around Getmodulehandlea and it checks the file size with FindfirstfileA. Erm not much help at all really sorry about that.
regards

Clandestiny
January 8th, 2002, 12:06
Hiya,

G6 ftp, eh? ...That app brings back *bad* memories Indeed, this is quite a difficult Asprotected prog to unpack using a number of Alexy's most recent tricks. Suffice it to say, I found the OEP unpacked it, and rebuilt the IAT but it never actually ran correctly. I kept getting some weird stack corruption that I never could figure out and patching the crash points only led to more crashes. This was actually the first AsProtected app I ever tried and I wouldn't have even made it as far as I did w/o some help from Kayaker. Indeed, he sent me quite a nice, *detailed* explanation for manually unpacking Asp apps. I don't think he would mind if I shared it here

-------------------------------------------------------------------------------
Original explanation follows...

Finding the OEP

1. Start Icedump and load the app
2. Display the address 401000 in the data window
3. Set a bpx VirtualAlloc, press F5 around 11-13 times until the code at 401000 changes and is recognizable (for this Delphi app it will be easy)
4. As soon as the code changes set a bpx GetProcAddress. Press F5 until it breaks (will be 2 or 3 more VirtualAlloc breaks first)
5. As soon as GetProcAddress breaks, press F12 about 3 times until you see:

0167:011FF984 AC LODSB
0167:011FF985 80F800 CMP AL,00
0167:011FF988 74DD JZ 011FF967
0167:011FF98A 80F806 CMP AL,06
0167:011FF98D 7506 JNZ 011FF995
0167:011FF98F 8345F804 ADD DWORD PTR [EBP-08],04
0167:011FF993 EBEF JMP 011FF984
0167:011FF995 53 PUSH EBX
0167:011FF996 56 PUSH ESI
0167:011FF997 53 PUSH EBX
0167:011FF998 8D5DF8 LEA EBX,[EBP-08]
0167:011FF99B 53 PUSH EBX
0167:011FF99C 80F802 CMP AL,02
0167:011FF99F 7406 JZ 011FF9A7
0167:011FF9A1 0FB60E MOVZX ECX,BYTE PTR [ESI]
0167:011FF9A4 41 INC ECX
0167:011FF9A5 EB05 JMP 011FF9AC
0167:011FF9A7 B904000000 MOV ECX,00000004
0167:011FF9AC 01CE ADD ESI,ECX
0167:011FF9AE E8C1FCFFFF CALL 011FF674
0167:011FF9B3 5B POP EBX ; return from GetProcAddress
0167:011FF9B4 EBCE JMP 011FF984
0167:011FF9B6 61 POPAD
0167:011FF9B7 E83A000000 CALL 011FF9F6

The loop from 011FF984 to 011FF9B4 is consistent in every Asprotect app and is where the IAT is decrypted. You can trace through this loop as many times as you like checking out how each API is decrypted.

6. AS SOON as you reach the above code after the 1ST GetProcAddress, display EDI, this will be the IAT Start. Each Import address is inserted starting here. Some of them will be regular API addresses (i.e. BFFxxxxx), but most of them will be redirected. Revirgin or Imprec will trace and rebuild most of these later.

7. Disable all breakpoints. To continue tracing manually you need to get out of the loop. Do this by setting a bpm on the call after the popad:
BPM 11FF9B7 X
If you now check the address in EDX, this will be the END of the IAT. So you can get the Length of the IAT by subtracting this value in EDX with the earlier one in EDI. *Sometimes* this doesn't hold, you need to check the IAT in the data window and see if it all makes sense.

8. I could tell you how to continue tracing manually from here to find the OEP, and it's actually good to know, but from here you can just set the /tracex. Some Asp apps break in the .code section *before* the real OEP. With G6ftp the 2nd /tracex break IS the OEP. Dump it as soon as you reach it with
/dump 400000 170000 filename

OEP = 503ee4
170000 = size of file

9. Use PEditor to change the OEP to the proper one, make VA=RA and VS=RS for each section, and change the code characteristics of the 1st section to E0000020. If you've done this right, the icon should now show.

10. Use Revirgin or Imprec to rebuild a valid import table and attach it to the end of the file (at hex offset 170000).

11. There's a bunch of other issues since RV/Imprec will not resolve all the API's. This is a (series of) Asp tricks that is easy to fix, but will require several more paragraphs to explain

Clandestiny
January 8th, 2002, 12:10
...Explanation Continued...

Rebuilding / Resolvign the IAT:

There are 11 unresolved entries with RV. The 5 ones labelled to_Resolve can be Traced with EnableTrace/Trace. The trace function seems to actually work with this version! The API address should resolve, but the name doesn't. I use Exescope to match up the addresses with the names, then edit in the dll name and api name. If you select ResolveAgain the Hints column should update correctly.

These 5 entries are usually duplicate GetProcAddress and GetModuleHandleA. This used to be the extent of Asprotect tricks, now you have to deal with the other ones on the other 6 IAT entries.

20 0010D208 011FC468 0000 ?????? to_Resolve
21 0010D20C 011FC818 0000 ?????? to_Resolve

25 0010D21C 011FC86C 0000 ?????? ??????

63 0010D2C4 011FC818 0000 ?????? to_Resolve

105 0010D374 011FC874 0000 ?????? ??????
122 0010D3B8 011FC834 0000 ?????? ??????

130 0010D3D8 011FC468 0000 ?????? to_Resolve
132 0010D3E0 011FC818 0000 ?????? to_Resolve

147 0010D41C 011FC864 0000 ?????? ??????
148 0010D420 011FC85C 0000 ?????? ??????
150 0010D428 011FC87C 0000 ?????? ??????

Should be:

20 0010D208 BFF76DA8 01A3 KERNEL32.dll GetProcAddress
21 0010D20C BFF77716 018D KERNEL32.dll GetModuleHandleA

25 0010D21C BFF8C5DA 0149 KERNEL32.dll GetCommandLineA

63 0010D2C4 BFF77716 018D KERNEL32.dll GetModuleHandleA

105 0010D374 BFF92F1B 01DC KERNEL32.dll GetVersion
122 0010D3B8 BFF92F1B 01DC KERNEL32.dll GetVersion

130 0010D3D8 BFF76DA8 01A3 KERNEL32.dll GetProcAddress
132 0010D3E0 BFF77716 018D KERNEL32.dll GetModuleHandleA

147 0010D41C BFF84948 015B KERNEL32.dll GetCurrentProcessId
148 0010D420 BFF96347 015A KERNEL32.dll GetCurrentProcess
150 0010D428 BFF92F1B 01DC KERNEL32.dll GetVersion

--------------------------------------

There's part of the unpacking code that has this routine:

0167:011FC7D8 6A00 PUSH 00
0167:011FC7DA E82D7CFFFF CALL KERNEL32!GetModuleHandleA
0167:011FC7DF A3D4352001 MOV [012035D4],EAX
0167:011FC7E4 E83B7CFFFF CALL KERNEL32!GetVersion
0167:011FC7E9 A3D8352001 MOV [012035D8],EAX
0167:011FC7EE 68E4352001 PUSH 012035E4
0167:011FC7F3 E8347CFFFF CALL KERNEL32!GetVersionExA
0167:011FC7F8 E8DF7BFFFF CALL KERNEL32!GetCurrentProcess
0167:011FC7FD A3DC352001 MOV [012035DC],EAX
0167:011FC802 E8DD7BFFFF CALL KERNEL32!GetCurrentProcessId
0167:011FC807 A3E0352001 MOV [012035E0],EAX
0167:011FC80C E8C37BFFFF CALL KERNEL32!GetCommandLineA
0167:011FC811 A378362001 MOV [01203678],EAX
0167:011FC816 C3 RET


The values returned from each of these calls are stored in variables that the program accesses later on after it's unpacked, rather than making the calls directly. There's a few ways you could approach the next problem.

You could trace in packed and unpacked code to find where the call to the variable is made and change it directly to a Call to the api, opcode bytes are preserved.

Or write yourself the return value in unpacked code somewhere and change the call address. You might have to do this if the app is really tricky and runs some code that is *only* in unpacking code, you'd need to duplicate the code.

I've learned that the easiest way is to access the memory of the original proggy and unassemble the redirected address pointed to in the IAT. Then decide what API name and address you want to set each unresolved IAT entry to and edit it into Revirgin and then update the .bin file. I set a break on some early api like GetStartupInfoA so it breaks in unpacked code and then unassemble each address.

Take the 1st example:

25 0010D21C 011FC86C 0000 ?????? ??????

:u 11FC86C

mov eax,[1203678]
ret

[1203678] = 8171AD30 ; return value from GetCommandLineA

That's it for the redirected code that's there. Knowing this you can go back to Revirgin and edit in the name and address for this api.


2nd example:

105 0010D374 011FC874 0000 ?????? ??????

:u 11FC874

push ebp
mov ebp, esp
pop ebp
ret 4

This is another new Asprotect twist, a short piece of garbage code made only to confuse Revirgin et al. What I do is replace the api with something that pushes no parameters, such as GetVersion. Or you could just nop out the call in the unpacked code. If I remember correctly, ALL of above 6 unresolved entries can be replaced with a simple ret. I think in most cases the api return value is never used in any important code, this is just an Asprotect routine that comes up all the time now.


There is another one that's kind of interesting. FindResourceA, Load/FreeResource, are also other unresolved apis that come up sometimes. These caused some problems with Revirgin because FindResourceA is a Kernel32 function that was grouped with the User32 calls. The rebuilt file wouldn't even load with this one.


Once you've edited in all the missing api's, create a new .bin file ready for pasting. There's an auto-fix feature in RV, but I've never used it, I like to do everything manually so I know exactly what is going on. Uncheck the Autofix Sections feature and set the IT RVA to the end of the file (170000), then click IAT Generator and create a .bin file ready to be pasted into your file at 170000.

Then use PEditor to add a section (I call mine .kayaker of course , and manually insert enough bytes to the end of your dumped file to create a section large enough to hold the .bin file. (I used 3000 filled with 0's). Then just paste it in.

You still need to fix up the pointer to the IAT table and its size in the PE header. This is at offset PE+80

00000180 0000 1700 7C01 0000 ....|...

which points to RVA 170000 and size 17C. (The size isn't really critical). Notice why I like doing raw dumps to work with rather that using Procdump or whatever. WYSIWYG when it comes to raw and virtual addresses, makes rebuilding the file so much easier. You can always repack the file with Asprotect if you want


Now if you're really lucky the proggy should run as is. In many cases however you may still have some work to do, this depends on what the programmer has implemented. There may be crippled functions and there may be a nag. There may be something else happening that causes a crash which you need to ferret out. Asprotect has options to be set as a time trial and/or requiring a keyfile. Or the programmer may have used his own protection routine.

There's 2 interesting things that happens when you skin Asprotect alive like this (I've used the phrase Like peeling a banana). If it's set as a time trial, the #days you have left at the time you dump it will be hardcoded into the dumped file. I've dumped at 1 day and 30 days left in an app and was told so by the nag box. Then I compared the 2 files and saw where that was stored. (Just don't change it to 00)

The other thing is that the registry, where the CLSID value is stored, is no longer read. This was all done in Asprotect code and this is where is checks your days left/key code. You can set a bp on RegQueryValueExA and break in unpacking code and watch it happen.

To prove that Asprotect code is no longer used, try deleting those sections at the end of the file and rebuild the PE header to reflect this. You can clean out a lot of the crap and make the file smaller this way. You need to make sure your IAT is referenced to the correct address of course.

Clandestiny
January 8th, 2002, 12:14
...Explanation Continued...

Runtime Errors / Crash Points:

503ee4 is the OEP. Sort of

This is why I like to trace manually, so there is no discrepancy. I think I mentioned Asp sometimes goes into program code to do some nastys before the OEP. This is one of those times I just didn't follow up by manually tracing or doing another /tracex as you did.

As soon as I changed the OEP the program started crashing with Runtime errors. I remember this as a familiar thing I first came upon in Chameleon Clock. We had a thread about it. I found this weird solution to dump the file only after BYPASSING that piece of unpacking code that jumps into program code which seemed to be causing all the problems.

4C894C is where /tracex 1st broke after the IAT decryption routine. The CS:EIP indicated it was called from 12008F9.

0167:012008F0 833D8035200100 CMP DWORD PTR [01203580],00
0167:012008F7 741E JZ 01200917
0167:012008F9 FF1580352001 CALL [01203580] ;4C894C
0167:012008FF 8D95CCD7FFFF LEA EDX,[EBP+FFFFD7CC]

So if you change 4C894C to a RET (or bypass the jz) you immediately return back to 12008FF without the code having been run. Then you continue tracing (or use /tracex again) to the real OEP at 503ee4 and dump the file.

-----------------

Now what did this do? If you check the code at the Call EAX where you saw it crashing:

0167:00403A9C 8B04DF MOV EAX,[EBX*8+EDI]
0167:00403A9F 43 INC EBX
0167:00403AA0 891DB0745000 MOV [005074B0],EBX
0167:00403AA6 85C0 TEST EAX,EAX
0167:00403AA8 7402 JZ 00403AAC
0167:00403AAA FFD0 CALL EAX
0167:00403AAC 3BF3 CMP ESI,EBX
0167:00403AAE 7FEC JG 00403A9C

You see that it cycles through EAX. ESI is the #cycles (6D) and EBX is the counter.

If you DON'T bypass that piece of unpacking code, the cycle crashes at EBX=0D when EAX=40F604. Now you can step into each EAX and you notice many of them are simple RET's. So if you replace 40F604 (hex offset f604) with a RET, the routine returns gracefully.

Or, check out the jump table pointed to by
MOV EAX,[EBX*8+EDI]
and change the address there to 00000000. If EAX=00000000, the JZ statement skips over the Call EAX.

If you DO bypass that piece of unpacking code and work with that dump instead, the EAX cycle continues on past EBX=0D (and others) and doesn't crash until EBX=31 (EAX=4712f8). This doesn't sound too great, but it does sort of show the kind of thing Asp does, and how you can fiddle with it.

Now bypassing that piece of unpacking code doesn't solve all the problems. You still need to treat each crash point individually, usually by changing the call to a RET.

I found the program crashed when EBX was 31,32,33,34,35, 51,54, 6a,6c.
For 31-35 I either changed the 1st byte of the address to C3, or I changed the value in the jump table pointed to by [EBX*8+EDI] to 00000000.

Same with 51,54 and 6a,6c. The only problem is I got a bad system crash. I think there may be an import problem still, I'm not sure if RV really got them all. You know the GetProcAddress calls in the IAT encryption routine? The last one is the last IAT address processed, and I think RV may have missed some. It "finds" the end of the IAT by looking for 00 terminators. But Asprotect may scatter the IAT addresses a little further or try to hide them.

Or maybe you just need to patch it differently. I used RET's, and in many cases this is all you need, but maybe some of the code is necessary and you need to trace a bit further before changing something to a RET.

Who knows? At this point of rebuilding all bets are off. I don't really know what all these screwed up calls really do. Some can be ignored by using a RET, maybe others are critical. This is cutting edge Asprotect, so your mode of attack is as good as any. I just want to give you an idea of what's going on, it's hard to be definitive with any one program.


I found I could set a BPM on
0167:00403AAA FFD0 CALL EAX
in the *packed* file as well, so you could always compare the routines with the unpacked version, even bypass some of them to see if they are really critical to running the program. (I used the 'if ebx==xx' qualifier on the bpm for easy tracing)


Heh, see why I don't crack much anymore? I've had my fill of Asprotect and unless I *really* want the program I won't bother jumping through its hoops except to keep my hand in it. I've been able to patch several Asprotect apps this way, but I've still got a few I could never patch fully, it just depends on how lucky you are and how much work you put into it.


BTW, to trace to the OEP manually: remember the jmp [ebp-14] at 11ffe6c I mentioned? Set a bpm here and trace into it, F8 single step everything. There are about half a dozen short loops that you need to get past before the OEP at:

0167:01219E00 8944241C MOV [ESP+1C],EAX
0167:01219E04 61 POPAD
0167:01219E05 FFE0 JMP EAX ;real OEP

Just keep a close eye on the SMC code and see where you can exit the loops, then set advanced bpm's until you see the above code.

----------------------------------------------------------------------------------

Maybe this will give you some ideas or at least get you started in the right direction. Like I said, I never could resolve the runtime crash issues in the dumped & rebuilt app. Finally, I got boored and opted for the registry CLSID hack. If you make any more progress, I'd be very interested in learning what the source of those crashes are.

Cheers,
Clandestiny

PS:

Hey, Kayaker... You *really* should publish this as a tut IMO, its one of the best step by step explanations of Asprotect I've ever ran across. Hope you don't mind that I shared the wealth

jomamameister
January 8th, 2002, 12:44
thanks to all. i will read these teachings carefully and keep persuing this and let you know. this has given me food for thought.
jomamameister

evaluator
January 8th, 2002, 14:16
Clandestiny, OOPS!

YOU WROTE:

""""""
push ebp
mov ebp, esp
pop ebp
ret 4

This is another new Asprotect twist, a short piece of garbage code
made only to confuse Revirgin et al. What I do is replace the api
with something that pushes no parameters, such as GetVersion.

"""""""""""

Wrong!

Those "short piece of garbage codes" are special control codes
for programmers. You can place any export name from same dll.(:FatalExit
Usually in many programs this future is inactive and nothing happens.
(Lasy programmers
BUT IF they will activate this ASPR-future, your unpacked+revirgined EXE
will crash!
For example:
Digisecret 1.1.272 & 1.1.278
ArtIcons 3.12
CloneCD 3.2.3.1
AO2000pr..

For AO2000pr I just submitted IDATA+ASPRtrickMANAGERcode.
GOTO:
http://www.woodmann.net/forum/showthread.php?s=&threadid=2476

Kayaker
January 8th, 2002, 16:53
Quote:
Originally posted by Clandestiny

Hope you don't mind that I shared the wealth


Hi

Did I really write all that crap? Nope, don't mind, but I did remove a small snippet about manually tracing to the OEP to protect the innocent. All the rest is likely already public knowledge anyway. Not that it matters too much, people just seem to use /tracex now anyway. Or like Splaj, get impatient enough while waiting for /tracex to cook his dinner to come up with an even better way. Personally I like his AB (arrogant-bastard) technique , but I can't seem to get out of the habit of manually tracing (almost as quick as /tracex anyway after you've done it 17 or 170 times, heh.)

Not sure if anyone else breaks into the IAT loop and uses the EDI/EDX values to get the IAT start and ending location as it is being processed. How DO you guys do it? Jeez I hope you don't all just depend on automagic patching apps like RV to do *everything* for you!

Besides, if you've missed tracing within that IAT decryption loop then you've missed out on the heart of Aprotect protection. If you're not learning something from cracking an app, then you're just cracking it.

Cheers,
Kayaker