2009
11.23

Dynamic unpacking has a couple of benefits and couple of drawbacks. Main benefit would of course be the quick unpacker writing and natural resilience to minor packer changes including multiple shell versions that use different compression and/or encryption algorithms. Our only real concern would be possibility of file malformation so that the file we are unpacking does a jail break. But since we can virtualize our unpackers with either system or application virtualization or sandboxing this isn't a huge issue any more. Also remember that TitanEngine now runs even under Linux making it as safe as possible to execute live application code. With this in mind we extend the functionality of our static unpacker functions to provide most help when it come to making such unpackers, since even though dynamic unpackers solve most of our problems static unpacking is still the best option. Further recommended reading on this topic can be found here.

However this is a "code your own dynamic unpacker" Monday. Today we take a look at nPack a straight forward packer that comes to us from Russia. Since it supports compression of both dynamic link library and executable files it should give us a nice exercise on how to write dynamic unpackers. Shall we?

Entry point of the packed file gives more useful information than we usually see with packers. Take a look.

  CMP DWORD PTR DS:[407E48],0  ;File already decompressed check
  JNZ L003
  JMP L004
L003:
  RET
L004:
  CALL 0040720A
  CALL 0040723C
  MOV EAX,
  SUB EAX,DWORD PTR DS:[407E08]
  MOV DWORD PTR DS:[407E44],EAX
  CALL 0040727A
  CALL 004073FD
  CALL 004078B2
  CALL 00407806
  MOV EAX,DWORD PTR DS:[407E44]
  MOV DWORD PTR DS:[407E48],1   ;Set file already decompressed flag
  ADD DWORD PTR DS:[407E00],EAX ;Add loaded file base to OEP RVA
  PUSH DWORD PTR DS:[407E00]	;Entry point jump
  RET

So, once again we solve the entry point location before the other pieces of the puzzle. Normally the first of our points of interest is import table processing. We find that part of the file by scrolling through the code and looking for the API call combination that utilizes GetProcAddress and LoadLibrary/GetModuleHandle. Since functions can be imported by name or ordinal number instruction TEST which does a logical compare with 0x80000000 is also a big clue on where this code is located. Most packers check for ordinal imports this way and this code part usually stands out on its own identifying the import processing part.

  MOV EBX,DWORD PTR DS:[EDI+C] ;Part I - Loading new library
  ADD EBX,DWORD PTR DS:[407E44]
  PUSH EBX
  CALL LoadLibraryA
...
  TEST EAX,80000000            ;Part II - Is API ordinal?
  JE SHORT 004074A0
  AND EAX,0FFFF
  MOV DWORD PTR SS:[ESP+18],EAX
  MOVZX EAX,AX
  PUSH EAX
  PUSH DWORD PTR SS:[ESP+18]
  CALL GetProcAddress          ;Find function via ordinal
  TEST EAX,EAX
...
  MOV ECX,DWORD PTR DS:[407E44]
  ADD EAX,ECX
  ADD EAX,2
  PUSH EAX
  MOV DWORD PTR SS:[ESP+1C],EAX
  PUSH DWORD PTR SS:[ESP+18]
  CALL GetProcAddress          ;Find function via name
...
  MOV DWORD PTR DS:[ESI],EAX   ;Part III - Write function pointer
  ADD ESI,4
  JMP SHORT 00407469

As we can see this code is segmented inside the function that processes imports. All three parts of this code have their role. First part load the necessary libraries, second one finds the functions inside the loaded libraries and the third writes the found API pointers to the import address table. Three breakpoints are needed in order to collect this data. One at the library loading part and two at function finding part. We need two breakpoints at the function finding part because only one of two GetProcAddress calls gets executed depending on whether the function is imported by ordinal or not. Similarly to this we have the following relocation code:

L000:
  MOVZX EAX,WORD PTR DS:[EBX]
  MOV EBP,EAX
  AND BP,0F000
  CMP EBP,3000
  JNZ L010
  AND EAX,EDI
  ADD EAX,DWORD PTR DS:[ECX]
  ADD EAX,EDX
  ADD DWORD PTR DS:[EAX],ESI
  MOV EDX,DWORD PTR DS:[407E44]
L010:
  MOVZX EAX,WORD PTR DS:[EBX]
  MOV EBP,EAX
  AND BP,0F000
  CMP EBP,1000
  JNZ L022
  AND EAX,EDI
  ADD EAX,DWORD PTR DS:[ECX]
  ADD EAX,EDX
  MOV EDX,ESI
  SHR EDX,10
  ADD WORD PTR DS:[EAX],DX
  MOV EDX,DWORD PTR DS:[407E44]
L022:
  MOVZX EAX,WORD PTR DS:[EBX]
  MOV EBP,EAX
  AND BP,0F000
  CMP EBP,2000
  JNZ L032
  AND EAX,EDI
  ADD EAX,DWORD PTR DS:[ECX]
  ADD EAX,EDX
  ADD WORD PTR DS:[EAX],SI
  MOV EDX,DWORD PTR DS:[407E44]
L032:
  INC EBX
  INC EBX
  DEC DWORD PTR SS:[ESP+10]
  JNZ L000

Yet again this is only the part of a really long code which can be easily identified. Test or compares with value 0x3000 indicates a 32 bit relocation is always a good clue, and if such test is a part of a loop there is a good chance that that code is a part of a relocation to new base function. We make two snapshots that fix relocations with ease. One at the beginning of this function and the other at the end of the same function. Memory to be snapshot is always the entire memory minus the packer section, which is in all cases from virtual address of the first section to virtual address of the last one. Since we already know where the entry point jump is this is the last piece of the puzzle needed to complete our unpacker.

Writing an unpacker for nPack should be an easy task since there are just a few things to look out for. If you had no trouble writing an unpacker for PackMan you shouldn't have a problem with this one. As always unpacker, source code and the samples are included with the blog. Until next week...

TitanEngine

ReversingLabs Corporation

RL!denPack
(package contains unpacker binary, source and samples used)

VN:F [1.9.13_1145]
Rating: +1 (from 1 vote)
Share
  1. We noticed a small problem on files which have TLS table and updated our unpacker to support such cases. If you downloaded the archive before this comment please re-download the package.

    VN:F [1.9.13_1145]
    Rating: 0 (from 0 votes)
  2. I really enjoyed reading this article, keep up creating such interesting stuff!

    VA:F [1.9.13_1145]
    Rating: 0 (from 0 votes)