10.19
For today's blog we had to do some minor engine modifications which is always fun. Even though we met these kinds of crypters before it completely slipped our mind that some crypters decrypt data in backward direction. That is why with CryptoCrackPEProtector we introduced new function for data decryption called StaticMemoryDecryptSpecial. Only thing special about it is that you can choose the direction of decryption and in later versions if it proves necessary byte skipping etc.
What is interesting about this unpacker is that we decided to make a static unpacker this time. We decided this because of the bugs in the crypter itself which prevented it from executing on x64 systems. So, in order to work around the crypters bugs today we make a static unpacker for it. This means that our analysis will be focused on different things then when we are creating dynamic unpackers.
First step when building static unpackers for crypters is to check if they have encrypted layers and if so how many. To find out this we just scroll through the code and look for sections of code that look like pure gibberish. Those sections are usually located just after the the decryption loops in-charge of their decryption. There could be several encryption layers following each other but usually for internal packer code decryption only one is used. Luckily for us this crypter doesn't have any. Entry point of the protected file looks like this:
POP EBX AND EBX,FFFFFF00 L002: CMP WORD PTR DS:[EBX],5A4D JNZ L018 MOV ESI,EBX ADD ESI,DWORD PTR DS:[EBX+3C] CMP DWORD PTR DS:[ESI],4550 JNZ L018 MOVZX EAX,WORD PTR DS:[ESI+18] MOV ECX,EAX IMUL EAX,EAX,0BAD MUL EAX SUB EAX,4B415DAB IMUL ECX,ECX,0C0DE ADD EAX,ECX JNZ L018 SUB ESP,4 JNZ 0040711E L018: SUB EBX,100 JNZ L002 NOP JB SHORT 004070BC
This code is in-charge of locating the base of kernel32.dll. One of the reasons this crypter doesn't work on x64 systems is precisely this code. After it has executed and the kernel32 base has been located crypter will execute this code:
MOV EAX,DWORD PTR FS:[30] TEST EAX,EAX MOV ECX,DWORD PTR FS:[20] JS L005 MOVZX ECX,BYTE PTR DS:[EAX+2] L005: JECXZ SHORT protecte.00407138 POP EAX JMP NEAR EAX
Which is the only antidebugging code in the crypter stub. We talked about PEB.BeingDebugged detection last week so you should know that during this code execution ECX should be NULL by the point code executes label L005. But that is of no importance since we are coding a static unpacker. However following code is very important:
L001: PUSHAD CALL L003 DB E8 L003: ADD DWORD PTR SS:[ESP],5C CMP DWORD PTR DS:[EDI],6164652E JE L031 CMP DWORD PTR DS:[EDI],7273722E JE L031 CMP DWORD PTR DS:[EDI],63727372 JE L031 CMP DWORD PTR DS:[EDI],7063632E JE L031 CMP DWORD PTR DS:[EDI+14],0 JE L031 MOV ESI,EBX ADD ESI,DWORD PTR DS:[EDI+C] MOV ECX,DWORD PTR DS:[EDI+10] MOV EAX,D4497869 ;Variable MOV EDX,2F1F0FD2 ;Variable TEST ECX,ECX JE L031 L021: DEC ECX JECXZ L031 XOR BYTE PTR DS:[ECX+ESI],AL NEG EAX SUB EDX,39E7B8FA ;Variable DEC EAX NOT EAX ADD EAX,EDX ADD EDX,EAX JMP L021 L031: RET POPAD ADD EDI,28 DEC ECX JNZ SHORT L001
This is the hearth of the crypter, code in-charge of decrypting the sections. This code also answers the question "Which sections does the crypter encrypt?". If we look at the sequence of compares at the start of this code we will see that only the sections named .rsr, rsrc and .cpp are not decrypted. It is common for crypters to use section names to determine if to decrypt the section or not. As we mentioned earlier the decryption is executed backwards, meaning that the sections are decrypted from the last to the fist byte of the section. However unlike the previous samples we can't just rip this code in order to use it in our unpacker. The reason for that is that some constants which are marked above are not the same for every packed sample. That is why those constants must be read by our unpacker and used as decryption keys for proper section decryption.
After the section data has been decrypted crypter will handle import data. This part is also interesting because of the following:
MOV ESI,0040445C PUSH EBX PUSH ESI PUSH DWORD PTR DS:[ESI+C] ADD DWORD PTR SS:[ESP],EBX CALL NEAR DWORD PTR SS:[ESP+14] ;LoadLibraryA call POP ESI POP EBX TEST EAX,EAX JE SHORT 004072FE ... Decryption: ADD DWORD PTR SS:[ESP],3C LEA EDI,DWORD PTR DS:[EBX+EAX+2] MOV EAX,F4C05BBB ;Variable MOV EDX,8B079C24 ;Variable SUB ECX,ECX L005: XOR BYTE PTR DS:[ECX+EDI],AL CMP BYTE PTR DS:[ECX+EDI],0 JE L015 INC EDX ;Random code SUB EAX,23BC3756 ADD EAX,C4A2711F ADD EAX,EDX ADD EDX,EAX INC ECX ;Random code JMP L005 L015: RET 004072FE: ADD ESP,0C JMP 004012C0 ;OEP jump
At the start of this code ESI is set to 0x0040445C which is the location of the valid import table, more specifically first IID. Following this is a call to LoadLibraryA API which loads all the dependencies until there are no more to load or the dependency isn't found on the system this file is executing on. Because of this there are cases when protected files won't execute correctly. But never the less lets imagine that this will work since all dependencies are present. What is going to happen when LoadLibrary tries to find a NULL dll file, and fails of-course, is that the jump to 0x004072FE will execute which will align the stack and jump to the original entry point. This is usually the last piece of puzzle but since here import fixing and entry point jumping are overlapping we will handle imports after we have found the entry point location.
While still looking at the code from above we find out that another decryption is performed on every member of the import table. All the strings which are the names of functions to be found in loaded DLLs are decrypted in the decryption sub-routine. Here we also have some variable constants to harden the detection but we also have a randomly generated piece of code that uses them in order to decrypt the string. This code can't be ripped (since its random) and what we are going to do is copy this random piece of code, set input variables to it and call it in order to make it decrypt the data we want. Very simple procedure which is commonly used for such cases. Once all the import strings have been decrypted the file is fully decrypted and all that is left is to set the correct PE header data for entry point and import table.
Writing an unpacker for CryptoCrackPEProtector should be an easy task since there are just a few things to look out for. As always unpacker, source code and the samples are included with the blog. Until next week...
TitanEngine![]() ReversingLabs Corporation |
RL!deCryptoCrackPE 0.9x |
