11.30
Commonly targets chosen for demonstration of TitanEngine static unpacking functions were very simple and so the code that unpacks the target would be very short but still enough to understand the basic principal of static unpackers. But today we do something very different. We decent into madness by testing the far limits of the TitanEngine and ourselves. Yoda's Crypter is one though nut to crack so may the force be with us on this journey.
Proof of this thesis is found at the very beginning of our task. Entry point itself lays on a challenge. Polymorphic decryption is used to decrypt most of the crypter body. Since this code is random we must do something to handle it and all similar cases found in the crypter body.
L000: LODS BYTE PTR DS:[ESI] ; ; Totally random decryption code ; STOS BYTE PTR ES:[EDI] LOOPD L000
Since both start and end patter can be defined with the LODS and STOS instructions code in between can be easily located. But what to do with it? Simple way of handling this would be extraction of this code and dynamic generation of decryption code with the following structure:
; __stdcall function long Decrypt(EAX, ECX) PUSH EBP MOV EBP,ESP MOV EAX,DWORD PTR[EBP+8] MOV ECX,DWORD PTR[EBP+C] ; ; Totally random decryption code pasted here ; LEAVE RET 8
Here ECX and EAX are input variables since they change. EAX is equal to byte pointed by ESI since its loaded with LODS, and ESI at start points to first byte after LOOPD instruction. Decryption size is static and its calculated and stored just before this decryption loop inside the crypter body. Since LOOPD decrements the ECX value it must be handled before every call to our decryption function. Return value of our decryption function is the value of decrypted byte. This is one way of dealing with polymorphic decryption functions and therefore this or similar approach will be used every time we encounter such obstacle while unpacking the crypter. If we were making a dynamic unpacker skipping this polymorphic decryption would be as easy as setting a hardware breakpoint on the first byte after LOOPD and waiting for it to hit.
This first layer of encryption is the most important one since all the data needed for our unpacker is encrypted by it. If you remember when coding dynamic unpacker first logical step is to collect data about imports. Situation is a bit different when it comes to static unpackers. First thing to do is of course decrypt everything that needs decrypting. With the first layer already decrypted we move on to decrypting section content. Following code processes sections:
MOV EDI,EAX ADD EDI,DWORD PTR DS:[EDI+3C] MOV ESI,EDI ADD ESI,0F8 XOR EDX,EDX L005: CMP DWORD PTR DS:[ESI],63727372 ;rsrc JE L046 CMP DWORD PTR DS:[ESI],7273722E ;.rsr JE L046 CMP DWORD PTR DS:[ESI],6F6C6572 ;relo JE L046 CMP DWORD PTR DS:[ESI],6C65722E ;.rel JE L046 CMP DWORD PTR DS:[ESI],4379 ;yC JE L046 CMP DWORD PTR DS:[ESI],6164652E ;.eda JE L046 CMP DWORD PTR DS:[ESI],6164722E ;.rda JE L046 CMP DWORD PTR DS:[ESI],6164692E ;.ida JE L046 CMP DWORD PTR DS:[ESI],736C742E ;.tls JE L046 CMP DWORD PTR DS:[ESI+14],0 JE L046 CMP DWORD PTR DS:[ESI+10],0 JE L046 PUSHAD MOV ECX,DWORD PTR DS:[ESI+10] OR EBX,EBX JNZ L035 MOV ESI,DWORD PTR DS:[ESI+14] ADD ESI,EAX CALL 0040748B JMP L038 L035: MOV ESI,DWORD PTR DS:[ESI+C] ADD ESI,EAX CALL 0040744E ;Decrypt content L038: MOV EDX,EBP ADD EDX,00402D3E LEA EAX,DWORD PTR DS:[EDX] PUSH EAX RET MOV EAX,0 INT 0D POPAD L046: ADD ESI,28 INC EDX CMP DX,WORD PTR DS:[EDI+6] JNZ L005 RET
All sections are processed by names. Every section except the ones named rsrc, idata, edata, rdata, tls and yC is decrypted. That kind of logic must be incorporated in our unpacker aswell. Decryption of content is done with another polymorphic decryption loop. Same procedure as described above can be applied. After that is done all that remains is that we fix imports and correct the entry point.
Now for the imports... Not exactly a hard task once we locate yC's internal data. Scroll down to the end of the crypter code, until you find this:
PUSH EBP MOV EBP,ESP PUSH EDI MOV EAX,DWORD PTR SS:[EBP+10] MOV EDI,DWORD PTR DS:[EAX+9C] MOV EDX,EDI ADD EDX,crackme_.00403393 PUSH DWORD PTR DS:[EDX] POP DWORD PTR DS:[EAX+B8] MOV DWORD PTR DS:[EAX+B4],EDI MOV DWORD PTR DS:[EAX+9C],0 MOV EAX,0 POP EDI LEAVE RET
Following this is a simple data structure containing the following:
; DWORD - LoadedBase (default: ImageBase) ; DWORD - OriginalEntryPoint address (RVA) ; DWORD - yC protection options selected (All options: 0x3C) ; DWORD - File checksum (custom algorithm) ; DWORD - Crypter body memory checksum (custom algorithm) ; DWORD - Reserved; Store place for a boolean variable ; ; Simplified IID (Image Import Descriptor) ; ; DWORD - Pointer to name of the first DLL in the IAT (RVA) ; DWORD - Pointer to IAT for the first DLL(RVA) ; DWORD - Reserved; OriginalFirstTrunk ; DWORD - Pointer to name of the second DLL in the IAT (RVA) ; DWORD - Pointer to IAT for the second DLL(RVA) ; DWORD - Reserved; OriginalFirstTrunk ; etc. for all DLLs
As we can see all the data we need is decrypted with the first polymorphic decryption and easily located. What we need from this is to read the location of DLL names and API pointers for every DLL and rebuild IIDs linking this data. Additionally all strings are encrypted so we need to go through the API pointers and decrypt them with the following algorithm:
PUSH ESI PUSH EDI MOV ESI,EAX MOV EDI,EAX L004: LODS BYTE PTR DS:[ESI] ROR AL,4 STOS BYTE PTR ES:[EDI] CMP BYTE PTR DS:[EDI],0 JNZ L004 POP EDI POP ESI RET
Once that is done we have imports sorted and since we know the address of the original entry point we only need to write it to PE header and optionally strip to crypter section to complete the unpacker. Optionally because if crypter is used on programs with TLS table it will be moved to the crypter section and if we don't want to rebuild that as well we can just keep the crypter section.
Writing an unpacker for Yoda's Crypter is a fairly complex task since there are few details to worry about. It provides an interesting challenge for any reverser. As always unpacker, source code and the samples are included with the blog. Until next week...
TitanEngine![]() ReversingLabs Corporation |
RL!deY0daCrypter 1.x |

