2009
11.16

After a few weeks we return to building unpackers with an interesting packer called Packman. Even though this is a pretty straight forward packer there are a few details that make us learn a trick or two while working on this unpacker. Most interesting detail about how one could find a loaded base for the module with just some simple math waits for us in the first few instructions, here:

  PUSHAD
  CALL L002
L002:
  POP EBX
  LEA EBX,DWORD PTR DS:[EBX-3A]
  ADD DWORD PTR DS:[EBX],EBX

That's is? This code can find on which base loaded module was loaded? It can and here is why. Once the first call and POP EBX execute EBX will be equal to address on which EBX is located. That doesn't get us closer to our loaded base address but next two instructions do. Once LEA executes EBX will be pointing to data inside the same section where the entry point resides. Data at that pointer is 0xFFFF8A30 which is equal to 0x004075D0 - 0x00400000 which is the EBX data minus the default image base. Since math for this is EBX - ImageBase = Delta reversing this would be EBX + Delta = ImageBase. And from this it is simple to figure out that as long as the EBX changes and delta remains the same with this simple math formula we can always calculate the loaded base of the file. Quite a neat trick.

  MOV BYTE PTR DS:[ESI],0E9
  MOV EAX,DWORD PTR DS:[EBX+C] ;0xFFFF9CB7
  MOV DWORD PTR DS:[ESI+1],EAX

What comes next is also interesting and very simple. Code above is a dynamic entry point jump generation. Since at that point ESI always point to the address of the packed entry point data that will be written to that address changes its code content. New instruction to be written instead of fist PUSHAD is a jump to the entry point. Since the relative distance between packed and original entry point will never change there is no need to recalculate this jump and we can always write the same data for that jump regardless of the file loaded base. This works of course because jumps are relative to their location. However in orderĀ  to get this location we can do several things:

  • Set a breakpoint on the JUMP to packed entry point right after POPAD and single step twice to get to the entry point
  • Place a breakpoint at the packed entry point at some point and wait for it to hit and then single step to get to entry point
  • Read newly created entry point jump (or data used to write it) and recalculate the entry point jump placing a hardware breakpoint there

Any of the solutions above is a good choice and you can use any of those to place the final entry point breakpoint. But we are getting ahead of ourselves since we still need to work out relocations and imports before we even get to the entry point.

First thing is first, imports. And so this code blob does everything we need to know about import handling in PackMan.

L000:
  ADD EAX,DWORD PTR DS:[EBX]
  PUSH EAX
  CALL NEAR DWORD PTR SS:[EBP] ;GetModuleHandleA
  MOV EDI,DWORD PTR DS:[ESI]
  ADD EDI,DWORD PTR DS:[EBX]
  JMP L017
L006:
  BTR ECX,1F
  JB L011
  ADD ECX,DWORD PTR DS:[EBX]
  INC ECX
  INC ECX
L011:
  PUSH EAX
  PUSH ECX
  PUSH EAX
  CALL NEAR DWORD PTR SS:[EBP+4] ;GetProcAddress
  STOS DWORD PTR ES:[EDI]
  POP EAX
L017:
  MOV ECX,DWORD PTR DS:[EDI]
  TEST ECX,ECX
  JNZ L006
  ADD ESI,10
  LODS DWORD PTR DS:[ESI]
  TEST EAX,EAX
  JNZ L000

One thing to notice is that PackMan uses GetModuleHandleA to get the base of the loaded DLL file. This is because it doesn't load libraries by itself, instead it lets Windows do the loading part and it just fills in the import address table with the correct API pointers. Its easy to place two breakpoints on these function calls and grab the data we need to fill in the imports correctly. Moving on to relocations.

  MOV ECX,DWORD PTR DS:[EBX+2C] ;First snapshot
  OR ECX,ECX
  JE SHORT L015
  MOV EDI,DWORD PTR DS:[EBX+24]
  JMP L014
L005:
  XOR EAX,EAX
  LODS WORD PTR DS:[ESI]
  OR EAX,EAX
  JE L012
  AND AH,0F
  ADD EAX,DWORD PTR DS:[EBX]
  ADD DWORD PTR DS:[EDX+EAX],ECX
L012:
  CMP ESI,EDI
  JNZ L005
L014:
  MOV EDX,DWORD PTR DS:[EDI]
  LEA ESI,DWORD PTR DS:[EDI+8]
  ADD EDI,DWORD PTR DS:[EDI+4]
  TEST EDX,EDX
  JNZ L012
L015:
  POPAD ;Second snapshot

This code blob relocates the file to newly loaded base. As always we can make two snapshots that fix relocations automatically. Question is which memory segment do we snapshot? Since Packman has two memory forms we apply a solution that works for both. Packman can have two or more PE sections with the packer code in the last section. So the memory to 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. Comparing those two snapshots fixes the relocation table with ease.

Writing an unpacker for PackMan 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 UPX 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!dePackMan 1.x
(package contains unpacker binary, source and samples used)

VN:F [1.9.3_1094]
Rating: +2 (from 2 votes)
  • Share/Bookmark
  1. wrong link

    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  2. Link has been fixed, it was only working from the main blog page. Thank you for letting us know.

    VN:F [1.9.3_1094]
    Rating: 0 (from 0 votes)