12.28
Today's blog post brings TitanEngine to the test and its a good way to end this years series of our blog posts dedicated to unpacking. The reason why TitanEngine is put to the test is because tELock is a protector riddled with protection tricks. That is why some modifications were needed so that we can debug files protected with this protection without getting detected by its anti-debugging tricks. Furthermore because of the specific packing method used our PE file validation routine needed some tweaks too. These small inconveniences resulted in today's publishing of two tools named: PEValidate and LogException. Those two tools are used to test the engine's performance with file execution and validation.
PEValidate is used to validate any PE32/PE32+ file. It evaluates the plausibility of file execution and provides detail error description about every found problem. This tool is a wrapper around TitanEngine function IsPE32FileValidEx and its used as a test for it.
LogException is used to track all exceptions that occur while debugging any PE32 file. It does execute the file fully so run it in the safe environment for suspicious samples. This tool also demonstrates the usage of TitanEngine debugging features and it is used as a test for detection from debugging tricks. What this tool showed us is that tELock uses debug registers to decode data and to place hardware breakpoints it itself handles. That made us redo the part of engine that handles hardware breakpoints for TitanEngine 2.0.3. This way plugins such as AntiDRx for OllyDBG are not needed for our engine.
Writing a blog that describes in detail how tELock works isn't an easy task because files protected with it are riddled with protection tricks, polymorphic decryption loops and exceptions specifically targeting possible errors in debuggers. Simply put tELock is roller coaster made of fun. For the purpose of this blog we are using a sample packed by tELock 0.99 with all protection options turned on.
After the first jump instruction in the sample we land on the start of the protector's section. Executing several obfuscations leads us to first poly decryptor code. Right here:
MOV ECX,1DC9 MOV EAX,ECX ADD ESI,36 DecryptionLoop: LEA EAX,DWORD PTR DS:[ECX+EAX*4+67] CALL DummyLabel DummyLabel: XOR BYTE PTR DS:[ESI],AL INC ESI POP EDX DEC ECX JG DecryptionLoop
After the first encrypted layer has been decrypted we continue tracing until the first exception:
SUB ECX,ECX PUSH DWORD PTR FS:[ECX] MOV DWORD PTR FS:[ECX],ESP INC BYTE PTR DS:[ECX]
Which is in this case an access violation. Just after we return from its handler execution next decryption awaits us here:
MOV EDX,DWORD PTR SS:[ESP] POP EAX SUB EDX,004128BF MOV EAX,729017A8 MOV EDI,EAX SUB EDI,724EEF46 STC ADD EDI,EDX MOV EAX,72901D20 MOV ECX,EAX SUB ECX,72901D12 DEC EAX MOV EBX,72900C26 DecryptionLoop: XOR DWORD PTR DS:[EDI],EBX ADD EBX,72900C76 XOR EAX,ESI CLC MOV EAX,4 ADD EDI,EAX SBB EAX,72BFB05C SUB EAX,72BFB131 SUB ECX,1 STC OR EAX,ESI PUSH ECX MOV ECX,ECX JECXZ ExitDecryption POP ECX JMP DecryptionLoop
And this is immediately followed by the next decryption loop:
DecryptionLoop: OR CX,CX JNZ OverDummy INT 20 OverDummy: ROL BYTE PTR DS:[EBX+ECX],5 ADD BYTE PTR DS:[EBX+ECX],CL XOR BYTE PTR DS:[EBX+ECX],0F1 INC BYTE PTR DS:[EBX+ECX] DEC ECX JG DecryptionLoop
From this execution pattern we see two things. One, tELock decrypts internal data with multiple polymorphic decryption loops followed by exceptions and two this blog would be too long to list them all. Therefore we will only focus on two important parts: imports and entry point. To find the import filling part we reset our sample and set a breakpoint on the RET instruction of GetModuleHandleA API. First time that breakpoint hits tELock is just locating EnumWindows API to try to locate debugger windows. But the second time that breakpoint hits its going to take us exactly where we want to be. Here:
PUSH EAX ;Get DLL base CALL NEAR DWORD PTR SS:[EBP+4036D1] TEST EAX,EAX JNZ DLLFound ... DLLFound: PUSHAD ;Check if that DLL can be redirected L001: XOR ECX,ECX SUB DH,DH L003: MOV DL,BYTE PTR DS:[EBX] TEST DL,40 JE L007 AND DL,5F L007: OR DL,DL ;Check ASCII "GDI32.DLLUSER32.DLLSHELL32.DLLKERNEL32.DLL" JE L021 INC EBX INC DH INC ECX CMP DL,BYTE PTR DS:[EAX+ECX-1] JE L003 CMP DL,BYTE PTR DS:[EAX+ECX+8] JE L003 CMP DL,BYTE PTR DS:[EAX+ECX+12] JE L003 CMP DL,BYTE PTR DS:[EAX+ECX+1D] JE L003 JMP L001 L021: OR DH,DH MOV DWORD PTR SS:[ESP+1C],EDX POPAD ... AND EBX,7FFFFFFF ;Get API address PUSH EBX PUSH DWORD PTR SS:[EBP+40374B] CALL NEAR DWORD PTR SS:[EBP+401C6E] INC EAX DEC EAX ... PUSHAD ;Check whether to write a redirection AND DWORD PTR DS:[EDI],0 MOV EAX,DWORD PTR SS:[EBP+403853] INC EAX JE WriteAPIPointer ;Patch to always jump! WriteAPIPointer: POPAD XOR DWORD PTR DS:[EDI],EAX POP EAX DEC EAX JE NextAPIPointe
Quite a long piece of code which does the following:
- Get the base of loaded DLL with GetModuleHandleA or load new DLL with LoadLibraryA
- Check the name of current DLL in the import table to see if that DLL can be redirected
- Allocate memory for redirection of APIs inside kernel32, user32, gdi32 and shell32 dlls
- Check whether to redirect the API (if yes then redirect, if no the write normal pointer)
- Process next API and next DLL
From this we can easily collect all the data we need to fix the import table. Only one jump needs to be patched to prevent tELock from ever redirecting APIs. This patch isn't necessary for our unpacker but can be used as a work around tELock's redirection. Due to the fact that everything inside the packers import table is encrypted with layers of polymorphic encryption data gathering with dynamic unpacking is much easier then static option.
After the import table has been filled tELock will execute a couple of exceptions more before it jumps to the entry point. Last polymorphic decryption loop which decrypts the entry point jump is here:
XOR DWORD PTR DS:[EDI],EDX DEC EBX SBB EAX,EDI ADD EDX,726C7434 CMP EAX,-7C ... CWDE POPAD ADD EAX,ESP RET
After POPAD and RET instructions execute you will land here:
MOV EBX,DWORD PTR SS:[EBP+403783] ... MOV ECX,39E REP STOS BYTE PTR ES:[EDI] ;Zero out packer code LEA EDI,DWORD PTR SS:[EBP+4015DC] MOV ECX,1BFF REP STOS BYTE PTR ES:[EDI] ;Zero out packer code STOS WORD PTR ES:[EDI] LEA EDI,DWORD PTR SS:[EBP+4015DC] TEST ESI,ESI JNZ L011 MOV DWORD PTR DS:[EDI],C340C033 JMP L016 L011: MOV BYTE PTR DS:[EDI],0E9 ;Write OEP JUMP INC EDI SUB EBX,EDI SUB EBX,4 MOV DWORD PTR DS:[EDI],EBX ;Write OEP JUMP L016: LEA EDI,DWORD PTR SS:[EBP+4031DB] MOV ECX,2C REP STOS BYTE PTR ES:[EDI] STOS WORD PTR ES:[EDI] JMP L022 INT 20 L022: POPAD CALL L024 L024: POP EDX SUB EDX,0E PUSH EDI PUSH ECX XOR EAX,EAX LEA EDI,DWORD PTR DS:[EDX] MOV ECX,20 REP STOS BYTE PTR ES:[EDI] STOS WORD PTR ES:[EDI] POP ECX POP EDI RET ;JUMP TO OEP
This code erases the protector memory and creates a new jump at the start of the tELock section which leads to the entry point. Purpose of that jump is to provide a redirection to entry point when tELock is used to protect DLL files. However we couldn't make tELock protect any DLL file successfully which is why our unpacker only supports executable files.
If you only want to unpack tELock protected file you can see how it is done in our video tutorial which shows ImportStudio usage:
Writing an unpacker for tELock should be a nice exercise for any reverser. As always unpacker, source code and the samples are included with the blog. Until next week...
TitanEngine![]() ReversingLabs Corporation |
RL!deTELock |
