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 |

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.
I really enjoyed reading this article, keep up creating such interesting stuff!