Logo
RSS Feed

🎁 Packed Executables And Unpacking


Created: 09.10.2020

There are several indicators that the executable is packed. Here they are (this list will be growing as I encounter new indicators):

  • Few imports and among these imports are functions like GetProcessAddress or LoadLibrary
  • Weird names for PE sections
  • Empty Physical addresses for sections (while VA is not empty). More about

Unpacking techniques

A piece of software cannot just run packed (what a challenging world that would be! 😳 ). The thing is that whatever the packing algorithm is, however complex 🧶 it might be, the real code needs to be unpacked into memory first. So, even with the most sophisticated protection techniques, it all really comes down to waiting for the executable to get unpacked into memory, from where it can be gently and dearly dumped like a 🐝 hive from a tree 🌳 full of hungry and pretty aggressive wild bees with a big and heavy bat 🏃‍♀️. We might want to do some minor other stuff afterwards but let’s keep it for now.

What do I mean by “waiting”? Start a debugger, attach it to this packed 📦 piece of treasure 💎, inspect the code and try to find a point in execution, when it’s done unpacking and hands control over to the original code. The original code’s entry point is called OEP (original entry point - 😳) and that’s the point when we make a memory dump.

The problem is - how to actually find this OEP? Well, there are lots of ways to do so. They vary from packer to packer, or from analyst to analyst. Here is the list of techniques that I’ve compiled.

OllyDbg’s built-in option

There is a functionality that OllyDbg debugger has, which can be reached out with Plugins -> OllyDump -> Find OEP that can get you something unpacked. Didn’t work for me, but might be a low-hanging fruit 🍌 for you. Give it a try before getting dragged into the swamp of hardcore reversing.

Below is what I got when trying to run it against a slightly complicated packed malware that was a part of my job interview once:

0048DF34 CALL unpackme.0048DF39
0048DF39 POP EAX
0048DF3A PUSH EBP
0048DF3B MOV EDI, EAX...
0048DF47 ...JMP SHORT unpackme.0048DF4E

It was my first executable to unpack (well, I didn’t touch anything harder than the labs from Practical Malware Analysis), so I couldn’t say whether it was it. Now my guts tell me that it’s not. Dumping the code from memory and trying to run it proved this, but I would like to explain it later. Just to be sure I understand why this is not OEP and how to tell just by looking at this code. Let’s move to the next one.

hr esp-4

This is another technique - setting a hardware breakpoint on the point when the stack is being squeezed. I got these instructions:

00490B50 POP ESI 
00490B51 POP EBP
00490B52 JMP EDI

When I clicked Follow Dump for EAX registry I saw a MZ ... PE-header and the registry’s value at the moment is 00400000. All I needed to do then was dumping the code and fixing the import table with ImportRec. PEView showed that the file was unpacked. Reopen the file with OllyDbg and the program stops at 0x0043A586.

JMP EDI

Before the unpacking code handes the control over to the original code, it needs to allocate some memory, but setting a BP on VirtualAlloc didn’t work. However, setting it on ZwAllocateVirtualMemory… Several times did the code stumble on this function. Upon return to the user-code, I saw clearly that memory was allocated for libraries.

При возврате в user-code было видно, что память выделяется под библиотеки. Сделала первый раз dump на том участке, где выделяется память под comctl_1 (адрес 7CA27320). При этом, ImportRec увидел всего 4 dlls, но OEP найти не вышло таким способом (ставила Hardware Breakpoint on write byte в EAX follow dump, а также после возврата в user-code пыталась с помощью F8 отследить, происходят ли какие-то изменения в dump - никаких результатов).

POPAD

Quite close to esp-4 technique.

Также пробовала найти OEP более стандартным способом, найдя POPAD и поставив bp на эту инструкцию. Однако после этой инструкции не было unconditional JMP. Кроме того, после запуска возникала ошибка и код останавливался до bp: ”Access violation when reading"

GetProcAddress

Deemed no results.

Unsorted

Код распаковывается в какой-либо регион памяти и уже в распакованном виде выполняется оттуда. Но для этого нужно выделить память и изменить атрибуты доступа к ней, чтобы иметь возможность выполнять код. Функция VirtualProtect как раз изменяет атрибуты доступа к региону памяти, поэтому она меня так заинтересовала. BOOL WINAPI VirtualProtect( In LPVOID lpAddress, In SIZE_T dwSize, In DWORD flNewProtect, Out PDWORD lpflOldProtect ); Нас интересует третий параметр этой функции — flNewProtect.Видим, что передается значение 0x40, а документация MSDN показывает нам, что это права на чтение, запись и выполнение.