2009
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
(package contains unpacker binary, source and samples used)

VN:F [1.9.13_1145]
Rating: +6 (from 6 votes)
Share