Unpacking paso a paso: PECompact, OEP, dump y reconstrucción de la IAT
Autor: Diego J D Arias · Plataforma: Windows · Ejecutable: UnPackme01.exe (32 bits) Herramientas: x64dbg (en su versión x32dbg) · Detect It Easy v3.21 · PE-bear v0.7.2 · Scylla
En esta oportunidad desempacamos un ejecutable de 32 bits protegido con PECompact. Vamos a hacer cuatro cosas: entender por qué el packer se comporta como se comporta, llegar al OEP real, reconstruir la IAT que el packer había redirigido y volcar el ejecutable ya desempacado.
Importante: Trabajá siempre en una máquina virtual aislada (red host-only o desconectada) y con un snapshot que incluya la memoria.
Primero que nada mirar el ejecutable antes de depurarlo
Antes de abrir el debugger, miramos el archivo con dos herramientas: Detect It Easy (DIE) y PE-bear. No ejecutamos nada, pero estas dos capturas ya cuentan muchísimo. Vamos a leerlas con calma, porque de acá sale casi todo el plan del desempacado.
Detect It Easy: quién es el packer y qué hay debajo
Detect It Easy: mostrando PECompact, Borland Delphi y las heurísticas de empaquetado

Línea por línea:
- PE32, Windows 32 bits, GUI. Es un ejecutable de 32 bits con interfaz gráfica. Por eso usamos x32dbg (no la versión de 64 bits) y, como es una GUI, más adelante esperamos encontrar una
DialogProco unaWndProc. - Packer: PECompact (3.02.2 o 3.03.21 beta). DIE reconoce el packer por firma y hasta nos da una versión aproximada. Es la información más valiosa de la captura: ya sabemos con qué nos enfrentamos. PECompact no virtualiza código: un stub descomprime el programa en memoria y termina con un salto al código original. Entonces sabemos qué buscar: el stub, la descompresión y ese salto final. El
unknown magices un valor de la cabecera que DIE no sabe asociar a una versión concreta, por eso da dos posibilidades. No cambia el método. - Linker: Turbo Linker 2.25 / Compiler: Borland Delphi / Language: Object Pascal. Ojo, esto habla del programa original, no del packer. DIE lo deduce de datos de la cabecera que el packer dejó intactos (por ejemplo, la versión del linker). Es un dato muy útil: ya sabemos que debajo hay un programa Delphi sin haber desempacado nada. Eso significa que conocemos cómo suele verse su arranque (prólogo con
push ebp / mov ebp,esp / add esp,-N, convenciónregister), y nos va a servir para reconocer el OEP cuando lleguemos. También encaja con la marcaMZPque vimos en el offset 0, típica de los ejecutables Borland. - (Heur) Protection: Unreadable resources. DIE intenta leer los recursos del ejecutable y no puede. En un programa normal,
.rsrctiene íconos, diálogos y cadenas. Acá los recursos reales no están a la vista: lo más probable es que el packer los comprima y los restaure después, y que esa sección esté ocupada por otra cosa (lo comprobamos enseguida). - (Heur) Protection: Section #0 (“CODE”) has RWX. La sección de código es, a la vez, legible, escribible y ejecutable. El código normal es solo lectura y ejecución. Que sea escribible indica que ese código se va a reescribir a sí mismo, que es justo lo que hace un packer al descomprimirse.
- (Heur) Packer: Generic [EntryPoint + Imports like PECompact + Section #0 (“CODE”) compressed + PE in resources + High entropy]. DIE llega a la misma conclusión por otro camino, sin usar la firma: mira el entry point, los imports, la compresión de CODE, la entropía y datos con forma de ejecutable dentro de los recursos. Es una segunda opinión independiente. Y ese último punto encaja con lo que vamos a ver: el stub y los datos comprimidos del packer viven en
.rsrc.
Tip: cuando la firma y la heurística coinciden, podés confiar en el diagnóstico. Si DIE solo mostrara una heurística y ninguna firma, habría que dudar más: puede ser un packer poco común o una versión modificada.
PE-bear: las secciones, donde se ve el packer a simple vista
PE-bear: mostrando las dos secciones del ejecutable: CODE y .rsrc

La tabla de secciones es lo más elocuente. Fijate en estos puntos:
1. Solo hay dos secciones. Un ejecutable de Delphi normal suele traer varias más (DATA, BSS, .idata, .reloc…). Acá el packer fusionó todo y no hay una .idata a la vista: los imports reales no están donde esperaríamos, y eso nos anticipa que habrá que reconstruir la IAT.
2. CODE ocupa mucho más en memoria que en disco.
| Sección | VA | Virtual Size | Raw Size | Entropía |
|---|---|---|---|---|
| CODE | 0x1000 | 0x5B000 | 0x29400 | 8 |
| .rsrc | 0x5C000 | 0x3000 | 0x2800 | 5.62 |
Lo que nos dice esta tabla:
- Solo hay dos secciones. No hay
.data,.idatani.reloc. Eso es típico de un packer: los imports verdaderos no están a la vista. - CODE tiene entropía 8, es decir, está comprimida por completo. Además ocupa mucho más en memoria
0x5B000que en disco0x29400: se descomprime sobre sí misma al ejecutarse. - .rsrc figura como ejecutable
ERWy tiene entropía 5.62. Ahí vive parte del stub del packer, sin comprimir. - El entry point cae dentro de CODE, la sección comprimida. Es decir, el arranque no puede ser código normal.
- En el offset 0 PE-bear muestra
MZPen lugar deMZ. Lo anotamos como curiosidad: los ejecutables de Delphi/Borland también suelen llevar esa marca.
Tip: un entry point dentro de una sección de entropía 8 es casi una confirmación de que hay un stub descomprimiendo algo. La pregunta pasa a ser dónde termina ese stub.
El loader de Windows reserva 364 KB para CODE, pero en el archivo solo hay 165 KB. Esa diferencia es el espacio donde va a aparecer el programa real cuando el packer lo descomprima. Sumado a la entropía de 8 que mide DIE en esa sección, la conclusión es directa: el código verdadero no está en el archivo, se genera en memoria.
3. Las dos secciones tienen Characteristics = E0000020. Decodificado:
| Bit | Significado |
|---|---|
0x00000020 | Contiene código |
0x20000000 | Ejecutable |
0x40000000 | Legible |
0x80000000 | Escribible |
Para CODE ya lo esperábamos: tiene que ser escribible para poder descomprimirse sobre sí misma. Pero el dato raro es .rsrc. Una sección de recursos normal es solo datos legibles (40000040). Que acá sea ejecutable y escribible delata que contiene código: es el stub del packer. Con su entropía de 5.62, intermedia, se ve que mezcla código sin comprimir y datos.
4. El campo Ptr to Reloc. de CODE vale 32434550. Esa columna debería ser un puntero a relocaciones, y no tiene sentido acá: Num. of Reloc. es 0. Es un valor de texto disfrazado. Si lo leemos en hexadecimal, 0x32434550 en little-endian son los bytes 50 45 43 32, que en ASCII dicen PEC2. Es la marca que PECompact deja en la cabecera, en un campo que el loader ignora. Es otra confirmación del packer, esta vez a mano.
5. El entry point cae dentro de CODE. Este dato sale de la cabecera opcional (no se ve en la captura): el entry point es 0x40F844 como veremos un poco mas adelante, es decir RVA 0xF844, que está dentro del rango de CODE (0x1000 a 0x5C000). Si el arranque estuviera en una sección normal de código, no sería raro. Pero ahí la sección está comprimida, así que el arranque no puede ser el programa original: tiene que ser el stub.
En busca del OEP
El método de manual para empezar es colocar un breakpoint de ejecución en memoria sobre 401000 y esperar a que el programa salte a ella.
Acá no sirve. Como el entry point está dentro de CODE, el breakpoint salta en la primera instrucción, sin distinguir entre el stub del packer y el código original.
El entry point y el truco del SEH
Abrimos la muestra en x32dbg y miramos las primeras instrucciones del entry point:
0040F844 | B8 ECE64500 | mov eax,UnPackme01.45E6EC |
0040F849 | 50 | push eax |
0040F84A | 64:FF35 00000000 | push dword ptr fs:[0] |
0040F851 | 64:8925 00000000 | mov dword ptr fs:[0],esp | instala un SEH propio
0040F858 | 33C0 | xor eax,eax |
0040F85A | 8908 | mov dword ptr ds:[eax],ecx | escritura en la dirección 0 -> access violation
0040F85C | 50 | push eax |
0040F85D | 45 | inc ebp |
0040F85E | 43 | inc ebx |
0040F85F | 6F | outsd |
0040F860 | 6D | insd |Leamos esto con calma, porque es el corazón del packer:
- Las tres primeras instrucciones arman un manejador de excepciones (SEH) propio: empujan la dirección del handler
45E6ECy lo enganchan enfs:[0]. xor eax,eaxdejaeax = 0ymov [eax],ecxintenta escribir en la dirección 0. Eso provoca una access violation a propósito.- Todo lo que viene después (
outsd,insd,arpl…) es basura que nunca se ejecuta linealmente. Está ahí para romper el análisis estático: IDA no sigue el flujo real y el listado parece código sin sentido. El handler vive en0x45E6EC, dentro de .rsrc. Si lo leemos, vemos que:
- sobrescribe la instrucción que falló
40F85Acon unjmp rel32hacia0x45E70F, - y devuelve
ExceptionContinueExecution. Windows reanuda la ejecución en la misma dirección,40F85A, que ahora ya no es una escritura sino un salto al stub. Es código automodificable disparado por una excepción intencional.
Tip: cuando veas un
fs:[0]modificado al principio de un programa, desconfiá. En un entry point, un SEH propio más una instrucción que falla seguro suele ser el mecanismo de transferencia de control del packer.
Dejar pasar la excepción sin perder el control
Queremos atravesar la excepción y seguir el flujo del packer, no el de ntdll. Para eso hacemos lo siguiente:
- Ponemos un hardware breakpoint de ejecución en el handler:
bphws 45E6EC,x. - Corremos con F9. El debugger se detiene en la access violation de
40F85A. - Pasamos la excepción al programa con Shift+F9. El breakpoint de hardware salta en
45E6EC. - Ponemos un breakpoint normal en
45E70Fel inicio real del stub y seguimos con F9. Con esto nos saltamos toda la mecánica de ntdll y llegamos justo al comienzo del stub.
Tip: usá hardware breakpoints siempre que puedas. No modifican la memoria, así que un packer que escanee su propio código buscando bytes
0xCCno los detecta.
Como llegar al OEP
Si bajamos un poco podemos ver como el stub restaura los registros originales con una serie de pop y termina con un salto calculado: jmp eax en 0x45E7AE. Es el tail jump, el salto final al código original, y casi todo packer termina con uno.
0045E79A | 03CA | add ecx,edx | ecx:EntryPoint, edx:EntryPoint
0045E79C | 68 00800000 | push 8000 |
0045E7A1 | 6A 00 | push 0 |
0045E7A3 | 57 | push edi | edi:EntryPoint
0045E7A4 | FF11 | call dword ptr ds:[ecx] | ecx:EntryPoint
0045E7A6 | 8BC6 | mov eax,esi | eax:EntryPoint, esi:EntryPoint
0045E7A8 | 5A | pop edx | edx:EntryPoint
0045E7A9 | 5E | pop esi | esi:EntryPoint
0045E7AA | 5F | pop edi | edi:EntryPoint
0045E7AB | 59 | pop ecx | ecx:EntryPoint
0045E7AC | 5B | pop ebx |
0045E7AD | 5D | pop ebp |
0045E7AE | FFE0 | jmp eax | eax:EntryPointPonemos un hardware breakpoint ahí bphws 45E7AE,x, corremos y miramos eax:
eax = 0x40F844
Es la misma dirección donde arrancó el packer. Lo que paso, restauró en 40F844 los bytes originales del programa, sobrescribiendo el arranque falso. Damos un paso (F7) y estamos en el OEP.
Para confirmarlo, miramos el código que apareció:
0040F844 | 55 | push ebp |
0040F845 | 8BEC | mov ebp,esp |
0040F847 | 83C4 F0 | add esp,FFFFFFF0 |
0040F84A | 53 | push ebx |
0040F84B | B8 74F74000 | mov eax,UnPackme01.40F774 |
0040F850 | E8 A35AFFFF | call UnPackme01.4052F8 |Ahora sí hay un prólogo coherente (push ebp / mov ebp,esp / add esp,-10h / push ebx), típico de un programa hecho en Delphi/Borland, y strings claros. Ya estamos en el código real.
Tip: una forma rápida de confirmar un OEP es mirar si el código tiene sentido y si en el dump de memoria aparecen strings legibles del programa. Un prólogo estándar del compilador es una muy buena señal.
Reconstruir la IAT
Llegar al OEP no alcanza. Si volcamos el proceso ahora, el ejecutable resultante no va a funcionar, porque su tabla de imports (IAT) no apunta a las APIs reales.
Veamos el problema en Scylla
Con el proceso detenido en el OEP, abrimos Scylla desde x32dbg, comprobamos que el OEP sea 0x40F844 he ingresamos los datos manualmente, el VA y el Size, para conocer los datos, miremos la IAT en el dump de x32dbg.
0043B190 00000000 ....
0043B194 76A7C7F0 ðǧv user32.GetKeyboardType
0043B198 76A11870 p.¡v user32.LoadStringA
0043B19C 76A6D010 .Цv user32.MessageBoxA
0043B1A0 76A0FFF0 ðÿ v user32.CharNextA
0043B1A4 00000000 ....
0043B1A8 7674F1A0 ñtv advapi32.RegQueryValueExA
0043B1AC 7674F360 `ótv advapi32.RegOpenKeyExA
0043B1B0 7674F270 pòtv advapi32.RegCloseKey
0043B1B4 00000000 ....
0043B1B8 772BF450 Pô+w oleaut32.SysFreeString
0043B1BC 772C3D10 .=,w oleaut32.SysReAllocStringLen
0043B1C0 772C0200 ..,w oleaut32.SysAllocStringLen
0043B1C4 00000000 ....
0043B1C8 022A0118 ..*.
0043B1CC 022A011F ..*.
0043B1D0 022A0126 &.*.
0043B1D4 022A012D -.*.
0043B1D8 00000000 ....
0043B1DC 022A0134 4.*.
0043B1E0 022A013B ;.*.
0043B1E4 022A0142 B.*.
0043B1E8 022A0149 I.*.
0043B1EC 022A0150 P.*.
0043B1F0 022A0157 W.*.
0043B1F4 022A015E ^.*.
0043B1F8 022A0165 e.*.
0043B1FC 022A016C l.*.
0043B200 022A0173 s.*.
0043B204 022A017A z.*.
0043B208 022A0181 ..*.
0043B20C 022A0188 ..*.
0043B210 022A018F ..*.
0043B214 022A0196 ..*.
0043B218 022A019D ..*.
0043B21C 022A01A4 ¤.*.
0043B220 022A01AB «.*.
0043B224 022A01B2 ².*.
0043B228 022A01B9 ¹.*.
0043B22C 022A01C0 À.*.
0043B230 022A01C7 Ç.*.
0043B234 022A01CE Î.*.
0043B238 022A01D5 Õ.*.
0043B23C 022A01DC Ü.*.
0043B240 022A01E3 ã.*.
0043B244 022A01EA ê.*.
0043B248 022A01F1 ñ.*.
0043B24C 022A01F8 ø.*.
0043B250 022A01FF ÿ.*.
0043B254 022A0206 ..*.
0043B258 022A020D ..*.
0043B25C 022A0214 ..*.
0043B260 022A021B ..*.
0043B264 022A0222 ".*.
0043B268 022A0229 ).*.
0043B26C 022A0230 0.*.
0043B270 022A0237 7.*.
0043B274 00000000 ....
0043B278 76F35170 pQóv gdi32.TextOutA
0043B27C 76F33E10 .>óv gdi32.StretchBlt
0043B280 76F36E90 .nóv gdi32.SetTextColor
0043B284 76F33F50 P?óv gdi32.SetMapMode
0043B288 76F36FC0 Àoóv gdi32.SetBkMode
0043B28C 76F36F00 .oóv gdi32.SetBkColor
0043B290 76F36CA0 lóv gdi32.SelectObject
0043B294 76F346D0 ÐFóv gdi32.Polygon
0043B298 76F33DD0 Ð=óv gdi32.PatBlt
0043B29C 76F35230 0Róv gdi32.GetTextExtentPoint32A
0043B2A0 76F35F50 P_óv gdi32.GetStockObject
0043B2A4 76F33EA0 >óv gdi32.GetObjectA
0043B2A8 76F35880 .Xóv gdi32.DeleteObject
0043B2AC 76F36890 .hóv gdi32.DeleteDC
0043B2B0 76F37440 @tóv gdi32.CreateSolidBrush
0043B2B4 76F352A0 Róv gdi32.CreateFontA
0043B2B8 76F35E80 .^óv gdi32.CreateDIBSection
0043B2BC 76F36D90 .móv gdi32.CreateCompatibleDC
0043B2C0 76F37070 ppóv gdi32.CreateCompatibleBitmap
0043B2C4 76F37400 .tóv gdi32.CreateBitmap
0043B2C8 76F36DE0 àmóv gdi32.BitBlt
0043B2CC 00000000 ....
0043B2D0 6E4C1390 ..Ln msimg32.AlphaBlend
0043B2D4 00000000 ....
0043B2D8 76A14C20 L¡v user32.CreateWindowExA
0043B2DC 76A20F90 ..¢v user32.UpdateWindow
0043B2E0 76A2F0B0 °ð¢v user32.ShowWindow
0043B2E4 76A13720 7¡v user32.SetWindowTextA
0043B2E8 76A2F050 Pð¢v user32.SetWindowPos
0043B2EC 76A147C0 ÀG¡v user32.SetWindowLongA
0043B2F0 76A2B590 .µ¢v user32.SetTimer
0043B2F4 76A136F0 ð6¡v user32.SetDlgItemTextA
0043B2F8 76A2D93C <Ù¢v user32.SetCursor
0043B2FC 76A10970 p.¡v user32.SendMessageA
0043B300 76A10930 0.¡v user32.SendDlgItemMessageA
0043B304 76A2AFB0 °¯¢v user32.ReleaseDC
0043B308 76A15820 X¡v user32.ReleaseCapture
0043B30C 76A2D2A0 Ò¢v user32.PostQuitMessage
0043B310 76A6D010 .Цv user32.MessageBoxA
0043B314 76A11870 p.¡v user32.LoadStringA
0043B318 76A16350 Pc¡v user32.LoadIconA
0043B31C 76A2CD60 `Í¢v user32.LoadCursorA
0043B320 76A180F0 ð.¡v user32.LoadBitmapA
0043B324 76A2E860 `è¢v user32.KillTimer
0043B328 76A2E7A0 ç¢v user32.InvalidateRect
0043B32C 76A1A410 .¤¡v user32.InflateRect
0043B330 76A214F0 ð.¢v user32.GetWindowRect
0043B334 76A213D0 Ð.¢v user32.GetSystemMetrics
0043B338 76A80900 ..¨v user32.GetDlgItemTextA
0043B33C 76A25CE0 à\¢v user32.GetDlgItem
0043B340 76A2B610 .¶¢v user32.GetDC
0043B344 76A11E40 @.¡v user32.GetPhysicalCursorPos
0043B348 76A21770 p.¢v user32.GetClientRect
0043B34C 76A13A60 `:¡v user32.FrameRect
0043B350 76A0F990 .ù v user32.FillRect
0043B354 76A2E110 .á¢v user32.EndPaint
0043B358 76A17140 @q¡v user32.EndDialog
0043B35C 76A0EDE0 àí v user32.DrawTextA
0043B360 76A11D80 ..¡v user32.DrawIconEx
0043B364 76A0EB10 .ë v user32.DrawEdge
0043B368 76A4B900 .¹¤v user32.DialogBoxParamA
0043B36C 774E8870 p.Nw ntdll.NtdllDialogWndProc_A
0043B370 76A10820 .¡v user32.CallWindowProcA
0043B374 76A2DD10 .Ý¢v user32.BeginPaint
0043B378 76A0FFF0 ðÿ v user32.CharNextA
0043B37C 76A6F5F0 ðõ¦v user32.CharToOemA
0043B380 00000000 ....
0043B384 022A023E >.*.
0043B388 00000000 ....
0043B38C 76A14C20 L¡v user32.CreateWindowExA
0043B390 00000000 ....Vemos que la IAT comienza en VA = 0043B194 y termina en 0043B390, el Size se obtiene haciendo la resta (0043B390 - 0043B194) lo que da Size = 0x1FC
Un detalle: los ceros que aparecen en la tabla son solo separadores entre las DLLs, no entradas perdidas.
Bien, ahora con los datos que tenemos abrimos Scylla y completamos VA 0043B194 y Size 1FC luego Get Imports, y no muestra el resultado:

- 75 APIs válidas.
- Dos grupos inválidos, de 42 y 1 entradas, y que estan apuntando a
0x022Axxxx. Esas direcciones están en memoria alocada por el packer, no en una DLL. Son redirecciones de imports.
Aqui se puede ver la IAT completas las buenas y las que estan redireccionadas.
0040540B | 90 | nop |
0040540C | FF25 64B24300 | jmp dword ptr ds:[43B264] |
00405412 | 8BC0 | mov eax,eax |
00405414 | FF25 60B24300 | jmp dword ptr ds:[43B260] |
0040541A | 8BC0 | mov eax,eax |
0040541C | FF25 5CB24300 | jmp dword ptr ds:[43B25C] |
00405422 | 8BC0 | mov eax,eax |
00405424 | FF25 58B24300 | jmp dword ptr ds:[43B258] |
0040542A | 8BC0 | mov eax,eax |
0040542C | FF25 54B24300 | jmp dword ptr ds:[43B254] |
00405432 | 8BC0 | mov eax,eax |
00405434 | FF25 50B24300 | jmp dword ptr ds:[43B250] |
0040543A | 8BC0 | mov eax,eax |
0040543C | FF25 4CB24300 | jmp dword ptr ds:[43B24C] |
00405442 | 8BC0 | mov eax,eax |
00405444 | FF25 48B24300 | jmp dword ptr ds:[43B248] |
0040544A | 8BC0 | mov eax,eax |
0040544C | FF25 44B24300 | jmp dword ptr ds:[43B244] |
00405452 | 8BC0 | mov eax,eax |
00405454 | FF25 40B24300 | jmp dword ptr ds:[43B240] |
0040545A | 8BC0 | mov eax,eax |
0040545C | FF25 3CB24300 | jmp dword ptr ds:[43B23C] |
00405462 | 8BC0 | mov eax,eax |
00405464 | FF25 38B24300 | jmp dword ptr ds:[43B238] |
0040546A | 8BC0 | mov eax,eax |
0040546C | FF25 34B24300 | jmp dword ptr ds:[43B234] |
00405472 | 8BC0 | mov eax,eax |
00405474 | FF25 30B24300 | jmp dword ptr ds:[43B230] |
0040547A | 8BC0 | mov eax,eax |
0040547C | FF25 2CB24300 | jmp dword ptr ds:[43B22C] |
00405482 | 8BC0 | mov eax,eax |
00405484 | FF25 28B24300 | jmp dword ptr ds:[43B228] |
0040548A | 8BC0 | mov eax,eax |
0040548C | FF25 24B24300 | jmp dword ptr ds:[43B224] |
00405492 | 8BC0 | mov eax,eax |
00405494 | FF25 20B24300 | jmp dword ptr ds:[43B220] |
0040549A | 8BC0 | mov eax,eax |
0040549C | FF25 1CB24300 | jmp dword ptr ds:[43B21C] |
004054A2 | 8BC0 | mov eax,eax |
004054A4 | FF25 18B24300 | jmp dword ptr ds:[43B218] |
004054AA | 8BC0 | mov eax,eax |
004054AC | FF25 14B24300 | jmp dword ptr ds:[43B214] |
004054B2 | 8BC0 | mov eax,eax |
004054B4 | FF25 10B24300 | jmp dword ptr ds:[43B210] |
004054BA | 8BC0 | mov eax,eax |
004054BC | FF25 0CB24300 | jmp dword ptr ds:[43B20C] |
004054C2 | 8BC0 | mov eax,eax |
004054C4 | FF25 08B24300 | jmp dword ptr ds:[43B208] |
004054CA | 8BC0 | mov eax,eax |
004054CC | FF25 04B24300 | jmp dword ptr ds:[43B204] |
004054D2 | 8BC0 | mov eax,eax |
004054D4 | FF25 00B24300 | jmp dword ptr ds:[43B200] |
004054DA | 8BC0 | mov eax,eax |
004054DC | FF25 FCB14300 | jmp dword ptr ds:[43B1FC] |
004054E2 | 8BC0 | mov eax,eax |
004054E4 | FF25 F8B14300 | jmp dword ptr ds:[43B1F8] |
004054EA | 8BC0 | mov eax,eax |
004054EC | FF25 F4B14300 | jmp dword ptr ds:[43B1F4] |
004054F2 | 8BC0 | mov eax,eax |
004054F4 | FF25 F0B14300 | jmp dword ptr ds:[43B1F0] |
004054FA | 8BC0 | mov eax,eax |
004054FC | FF25 ECB14300 | jmp dword ptr ds:[43B1EC] |
00405502 | 8BC0 | mov eax,eax |
00405504 | FF25 E8B14300 | jmp dword ptr ds:[43B1E8] |
0040550A | 8BC0 | mov eax,eax |
0040550C | FF25 E4B14300 | jmp dword ptr ds:[43B1E4] |
00405512 | 8BC0 | mov eax,eax |
00405514 | FF25 E0B14300 | jmp dword ptr ds:[43B1E0] |
0040551A | 8BC0 | mov eax,eax |
0040551C | FF25 DCB14300 | jmp dword ptr ds:[43B1DC] |
00405522 | 8BC0 | mov eax,eax |
00405524 | FF25 D0B24300 | jmp dword ptr ds:[<&AlphaBlend>] |
0040552A | 8BC0 | mov eax,eax |
0040552C | FF25 C8B24300 | jmp dword ptr ds:[<&BitBlt>] |
00405532 | 8BC0 | mov eax,eax |
00405534 | FF25 C4B24300 | jmp dword ptr ds:[<&CreateBitmap>] |
0040553A | 8BC0 | mov eax,eax |
0040553C | FF25 C0B24300 | jmp dword ptr ds:[<&CreateCompatibleBitmap>] |
00405542 | 8BC0 | mov eax,eax |
00405544 | FF25 BCB24300 | jmp dword ptr ds:[<&CreateCompatibleDC>] |
0040554A | 8BC0 | mov eax,eax |
0040554C | FF25 B8B24300 | jmp dword ptr ds:[<&CreateDIBSection>] |
00405552 | 8BC0 | mov eax,eax |
00405554 | FF25 B4B24300 | jmp dword ptr ds:[<&CreateFontA>] |
0040555A | 8BC0 | mov eax,eax |
0040555C | FF25 B0B24300 | jmp dword ptr ds:[<&CreateSolidBrush>] |
00405562 | 8BC0 | mov eax,eax |
00405564 | FF25 ACB24300 | jmp dword ptr ds:[<&DeleteDC>] |
0040556A | 8BC0 | mov eax,eax |
0040556C | FF25 A8B24300 | jmp dword ptr ds:[<&DeleteObject>] |
00405572 | 8BC0 | mov eax,eax |
00405574 | FF25 A4B24300 | jmp dword ptr ds:[<&GetObjectA>] |
0040557A | 8BC0 | mov eax,eax |
0040557C | FF25 A0B24300 | jmp dword ptr ds:[<&GetStockObject>] |
00405582 | 8BC0 | mov eax,eax |
00405584 | FF25 9CB24300 | jmp dword ptr ds:[<&GetTextExtentPoint32A>] |
0040558A | 8BC0 | mov eax,eax |
0040558C | FF25 98B24300 | jmp dword ptr ds:[<&PatBlt>] |
00405592 | 8BC0 | mov eax,eax |
00405594 | FF25 94B24300 | jmp dword ptr ds:[<&Polygon>] |
0040559A | 8BC0 | mov eax,eax |
0040559C | FF25 90B24300 | jmp dword ptr ds:[<&SelectObject>] |
004055A2 | 8BC0 | mov eax,eax |
004055A4 | FF25 8CB24300 | jmp dword ptr ds:[<&SetBkColor>] |
004055AA | 8BC0 | mov eax,eax |
004055AC | FF25 88B24300 | jmp dword ptr ds:[<&SetBkMode>] |
004055B2 | 8BC0 | mov eax,eax |
004055B4 | FF25 84B24300 | jmp dword ptr ds:[<&SetMapMode>] |
004055BA | 8BC0 | mov eax,eax |
004055BC | FF25 80B24300 | jmp dword ptr ds:[<&SetTextColor>] |
004055C2 | 8BC0 | mov eax,eax |
004055C4 | FF25 7CB24300 | jmp dword ptr ds:[<&StretchBlt>] |
004055CA | 8BC0 | mov eax,eax |
004055CC | FF25 78B24300 | jmp dword ptr ds:[<&TextOutA>] |
004055D2 | 8BC0 | mov eax,eax |
004055D4 | FF25 74B34300 | jmp dword ptr ds:[<&BeginPaint>] |
004055DA | 8BC0 | mov eax,eax |
004055DC | FF25 70B34300 | jmp dword ptr ds:[<&CallWindowProcA>] |
004055E2 | 8BC0 | mov eax,eax |
004055E4 | FF25 78B34300 | jmp dword ptr ds:[<&CharNextA>] |
004055EA | 8BC0 | mov eax,eax |
004055EC | FF25 7CB34300 | jmp dword ptr ds:[<&CharToOemA>] |
004055F2 | 8BC0 | mov eax,eax |
004055F4 | FF25 6CB34300 | jmp dword ptr ds:[<&NtdllDialogWndProc_A>] |
004055FA | 8BC0 | mov eax,eax |
004055FC | FF25 68B34300 | jmp dword ptr ds:[<&DialogBoxParamA>] |
00405602 | 8BC0 | mov eax,eax |
00405604 | FF25 64B34300 | jmp dword ptr ds:[<&DrawEdge>] |
0040560A | 8BC0 | mov eax,eax |
0040560C | FF25 60B34300 | jmp dword ptr ds:[<&DrawIconEx>] |
00405612 | 8BC0 | mov eax,eax |
00405614 | FF25 5CB34300 | jmp dword ptr ds:[<&DrawTextA>] |
0040561A | 8BC0 | mov eax,eax |
0040561C | FF25 58B34300 | jmp dword ptr ds:[<&EndDialog>] |
00405622 | 8BC0 | mov eax,eax |
00405624 | FF25 54B34300 | jmp dword ptr ds:[<&EndPaint>] |
0040562A | 8BC0 | mov eax,eax |
0040562C | FF25 50B34300 | jmp dword ptr ds:[<&FillRect>] |
00405632 | 8BC0 | mov eax,eax |
00405634 | FF25 4CB34300 | jmp dword ptr ds:[<&FrameRect>] |
0040563A | 8BC0 | mov eax,eax |
0040563C | FF25 48B34300 | jmp dword ptr ds:[<&GetClientRect>] |
00405642 | 8BC0 | mov eax,eax |
00405644 | FF25 44B34300 | jmp dword ptr ds:[<&GetPhysicalCursorPos>] |
0040564A | 8BC0 | mov eax,eax |
0040564C | FF25 40B34300 | jmp dword ptr ds:[<&GetDC>] |
00405652 | 8BC0 | mov eax,eax |
00405654 | FF25 3CB34300 | jmp dword ptr ds:[<&GetDlgItem>] |
0040565A | 8BC0 | mov eax,eax |
0040565C | FF25 38B34300 | jmp dword ptr ds:[<&GetDlgItemTextA>] |
00405662 | 8BC0 | mov eax,eax |
00405664 | FF25 34B34300 | jmp dword ptr ds:[<&GetSystemMetrics>] |
0040566A | 8BC0 | mov eax,eax |
0040566C | FF25 30B34300 | jmp dword ptr ds:[<&GetWindowRect>] |
00405672 | 8BC0 | mov eax,eax |
00405674 | FF25 2CB34300 | jmp dword ptr ds:[<&InflateRect>] |
0040567A | 8BC0 | mov eax,eax |
0040567C | FF25 28B34300 | jmp dword ptr ds:[<&InvalidateRect>] |
00405682 | 8BC0 | mov eax,eax |
00405684 | FF25 24B34300 | jmp dword ptr ds:[<&KillTimer>] |
0040568A | 8BC0 | mov eax,eax |
0040568C | FF25 20B34300 | jmp dword ptr ds:[<&LoadBitmapA>] |
00405692 | 8BC0 | mov eax,eax |
00405694 | FF25 1CB34300 | jmp dword ptr ds:[<&LoadCursorA>] |
0040569A | 8BC0 | mov eax,eax |
0040569C | FF25 18B34300 | jmp dword ptr ds:[<&LoadIconA>] |
004056A2 | 8BC0 | mov eax,eax |
004056A4 | FF25 18B34300 | jmp dword ptr ds:[<&LoadIconA>] |
004056AA | 8BC0 | mov eax,eax |
004056AC | FF25 14B34300 | jmp dword ptr ds:[<&LoadStringA>] |
004056B2 | 8BC0 | mov eax,eax |
004056B4 | FF25 10B34300 | jmp dword ptr ds:[<&MessageBoxA>] |
004056BA | 8BC0 | mov eax,eax |
004056BC | FF25 0CB34300 | jmp dword ptr ds:[<&PostQuitMessage>] |
004056C2 | 8BC0 | mov eax,eax |
004056C4 | FF25 08B34300 | jmp dword ptr ds:[<&ReleaseCapture>] |
004056CA | 8BC0 | mov eax,eax |
004056CC | FF25 04B34300 | jmp dword ptr ds:[<&ReleaseDC>] |
004056D2 | 8BC0 | mov eax,eax |
004056D4 | FF25 00B34300 | jmp dword ptr ds:[<&SendDlgItemMessageA>] |
004056DA | 8BC0 | mov eax,eax |
004056DC | FF25 FCB24300 | jmp dword ptr ds:[<&SendMessageA>] |
004056E2 | 8BC0 | mov eax,eax |
004056E4 | FF25 FCB24300 | jmp dword ptr ds:[<&SendMessageA>] |
004056EA | 8BC0 | mov eax,eax |
004056EC | FF25 F8B24300 | jmp dword ptr ds:[<&SetCursor>] |
004056F2 | 8BC0 | mov eax,eax |
004056F4 | FF25 F4B24300 | jmp dword ptr ds:[<&SetDlgItemTextA>] |
004056FA | 8BC0 | mov eax,eax |
004056FC | FF25 F0B24300 | jmp dword ptr ds:[<&SetTimer>] |
00405702 | 8BC0 | mov eax,eax |
00405704 | FF25 ECB24300 | jmp dword ptr ds:[<&SetWindowLongA>] |
0040570A | 8BC0 | mov eax,eax |
0040570C | FF25 ECB24300 | jmp dword ptr ds:[<&SetWindowLongA>] |
00405712 | 8BC0 | mov eax,eax |
00405714 | FF25 E8B24300 | jmp dword ptr ds:[<&SetWindowPos>] |
0040571A | 8BC0 | mov eax,eax |
0040571C | FF25 E4B24300 | jmp dword ptr ds:[<&SetWindowTextA>] |
00405722 | 8BC0 | mov eax,eax |
00405724 | FF25 E0B24300 | jmp dword ptr ds:[<&ShowWindow>] |
0040572A | 8BC0 | mov eax,eax |
0040572C | FF25 DCB24300 | jmp dword ptr ds:[<&UpdateWindow>] |Se puede ver claramente que en vez de la dirección real de la API, la IAT tiene un desvío. Si volcáramos así, el ejecutable apuntaría a una región que solo existe en el proceso vivo y se rompería al ejecutarlo.
Para entender la redirección
Si hacemos doble click en uno de los slots inválidos, vemos que cada uno apunta a un pequeño stub de 7 bytes:
022A0000 | B8 20FD4877 | mov eax,<ntdll.RtlDeleteCriticalSection> | eax:EntryPoint
022A0005 | FFE0 | jmp eax | eax:EntryPoint
022A0007 | B8 70E94977 | mov eax,<ntdll.RtlLeaveCriticalSection> | eax:EntryPoint
022A000C | FFE0 | jmp eax | eax:EntryPoint
022A000E | B8 70014A77 | mov eax,<ntdll.RtlEnterCriticalSection> | eax:EntryPoint
022A0013 | FFE0 | jmp eax | eax:EntryPoint
022A0015 | B8 10C94B77 | mov eax,<ntdll.RtlInitializeCriticalSection> | eax:EntryPoint
022A001A | FFE0 | jmp eax | eax:EntryPoint
022A001C | B8 60F9A475 | mov eax,<kernel32.VirtualFree> | eax:EntryPoint
022A0021 | FFE0 | jmp eax | eax:EntryPoint
022A0023 | B8 60F8A475 | mov eax,<kernel32.VirtualAlloc> | eax:EntryPoint
022A0028 | FFE0 | jmp eax | eax:EntryPoint
022A002A | B8 D0F9A475 | mov eax,<kernel32.LocalFree> | eax:EntryPoint
022A002F | FFE0 | jmp eax | eax:EntryPoint
022A0031 | B8 0009A575 | mov eax,<kernel32.LocalAlloc> | eax:EntryPoint
022A0036 | FFE0 | jmp eax | eax:EntryPoint
022A0038 | B8 4028A575 | mov eax,<kernel32.GetTickCount> | eax:EntryPoint
022A003D | FFE0 | jmp eax | eax:EntryPoint
022A003F | B8 E0E3A475 | mov eax,<kernel32.QueryPerformanceCounter> | eax:EntryPoint
022A0044 | FFE0 | jmp eax | eax:EntryPoint
022A0046 | B8 101EA575 | mov eax,<kernel32.GetVersion> | eax:EntryPoint
022A004B | FFE0 | jmp eax | eax:EntryPoint
022A004D | B8 B0E3A475 | mov eax,<kernel32.GetCurrentThreadId> | eax:EntryPoint
022A0052 | FFE0 | jmp eax | eax:EntryPoint
022A0054 | B8 90E4A475 | mov eax,<kernel32.WideCharToMultiByte> | eax:EntryPoint
022A0059 | FFE0 | jmp eax | eax:EntryPoint
022A005B | B8 E008A575 | mov eax,<kernel32.lstrlenA> | eax:EntryPoint
022A0060 | FFE0 | jmp eax | eax:EntryPoint
022A0062 | B8 0095A475 | mov eax,<kernel32.lstrcpynA> | eax:EntryPoint
022A0067 | FFE0 | jmp eax | eax:EntryPoint
022A0069 | B8 C01AA575 | mov eax,<kernel32.LoadLibraryExA> | eax:EntryPoint
022A006E | FFE0 | jmp eax | eax:EntryPoint
022A0070 | B8 D08BA475 | mov eax,<kernel32.GetThreadLocale> | eax:EntryPoint
022A0075 | FFE0 | jmp eax | eax:EntryPoint
022A0077 | B8 2011A575 | mov eax,<kernel32.GetStartupInfoA> | eax:EntryPoint
022A007C | FFE0 | jmp eax | eax:EntryPoint
022A007E | B8 F0F9A475 | mov eax,<kernel32.GetProcAddress> | eax:EntryPoint
022A0083 | FFE0 | jmp eax | eax:EntryPoint
022A0085 | B8 000FA575 | mov eax,<kernel32.GetModuleHandleA> | eax:EntryPoint
022A008A | FFE0 | jmp eax | eax:EntryPoint
022A008C | B8 D012A575 | mov eax,<kernel32.GetModuleFileNameA> | eax:EntryPoint
022A0091 | FFE0 | jmp eax | eax:EntryPoint
022A0093 | B8 1086A475 | mov eax,<kernel32.GetLocaleInfoA> | eax:EntryPoint
022A0098 | FFE0 | jmp eax | eax:EntryPoint
022A009A | B8 B0E4A475 | mov eax,<kernel32.GetLastError> | eax:EntryPoint
022A009F | FFE0 | jmp eax | eax:EntryPoint
022A00A1 | B8 8023A575 | mov eax,<kernel32.GetCommandLineA> | eax:EntryPoint
022A00A6 | FFE0 | jmp eax | eax:EntryPoint
022A00A8 | B8 800FA575 | mov eax,<kernel32.FreeLibrary> | eax:EntryPoint
022A00AD | FFE0 | jmp eax | eax:EntryPoint
022A00AF | B8 5036A575 | mov eax,<kernel32.FindFirstFileA> | eax:EntryPoint
022A00B4 | FFE0 | jmp eax | eax:EntryPoint
022A00B6 | B8 1036A575 | mov eax,<kernel32.FindClose> | eax:EntryPoint
022A00BB | FFE0 | jmp eax | eax:EntryPoint
022A00BD | B8 806DA575 | mov eax,<kernel32.ExitProcess> | eax:EntryPoint
022A00C2 | FFE0 | jmp eax | eax:EntryPoint
022A00C4 | B8 203AA575 | mov eax,<kernel32.WriteFile> | eax:EntryPoint
022A00C9 | FFE0 | jmp eax | eax:EntryPoint
022A00CB | B8 2074A675 | mov eax,<kernel32.UnhandledExceptionFilter> | eax:EntryPoint
022A00D0 | FFE0 | jmp eax | eax:EntryPoint
022A00D2 | B8 C039A575 | mov eax,<kernel32.SetFilePointer> | eax:EntryPoint
022A00D7 | FFE0 | jmp eax | eax:EntryPoint
022A00D9 | B8 8039A575 | mov eax,<kernel32.SetEndOfFile> | eax:EntryPoint
022A00DE | FFE0 | jmp eax | eax:EntryPoint
022A00E0 | B8 600AA575 | mov eax,<kernel32.RtlUnwind> | eax:EntryPoint
022A00E5 | FFE0 | jmp eax | eax:EntryPoint
022A00E7 | B8 3039A575 | mov eax,<kernel32.ReadFile> | eax:EntryPoint
022A00EC | FFE0 | jmp eax | eax:EntryPoint
022A00EE | B8 500AA575 | mov eax,<kernel32.RaiseException> | eax:EntryPoint
022A00F3 | FFE0 | jmp eax | eax:EntryPoint
022A00F5 | B8 A01BA575 | mov eax,<kernel32.GetStdHandle> | eax:EntryPoint
022A00FA | FFE0 | jmp eax | eax:EntryPoint
022A00FC | B8 D037A575 | mov eax,<kernel32.GetFileSize> | eax:EntryPoint
022A0101 | FFE0 | jmp eax | eax:EntryPoint
022A0103 | B8 0038A575 | mov eax,<kernel32.GetFileType> | eax:EntryPoint
022A0108 | FFE0 | jmp eax | eax:EntryPoint
022A010A | B8 A035A575 | mov eax,<kernel32.CreateFileA> | eax:EntryPoint
022A010F | FFE0 | jmp eax | eax:EntryPoint
022A0111 | B8 5033A575 | mov eax,<kernel32.CloseHandle> | eax:EntryPoint
022A0116 | FFE0 | jmp eax | eax:EntryPoint
022A0118 | B8 50E4A475 | mov eax,<kernel32.TlsSetValue> | eax:EntryPoint
022A011D | FFE0 | jmp eax | eax:EntryPoint
022A011F | B8 C0E3A475 | mov eax,<kernel32.TlsGetValue> | eax:EntryPoint
022A0124 | FFE0 | jmp eax | eax:EntryPoint
022A0126 | B8 0009A575 | mov eax,<kernel32.LocalAlloc> | eax:EntryPoint
022A012B | FFE0 | jmp eax | eax:EntryPoint
022A012D | B8 000FA575 | mov eax,<kernel32.GetModuleHandleA> | eax:EntryPoint
022A0132 | FFE0 | jmp eax | eax:EntryPoint
022A0134 | B8 E008A575 | mov eax,<kernel32.lstrlenA> | eax:EntryPoint
022A0139 | FFE0 | jmp eax | eax:EntryPoint
022A013B | B8 608CA475 | mov eax,<kernel32.lstrcat> | eax:EntryPoint
022A0140 | FFE0 | jmp eax | eax:EntryPoint
022A0142 | B8 203AA575 | mov eax,<kernel32.WriteFile> | eax:EntryPoint
022A0147 | FFE0 | jmp eax | eax:EntryPoint
022A0149 | B8 4035A575 | mov eax,<kernel32.WaitForSingleObject> | eax:EntryPoint
022A014E | FFE0 | jmp eax | eax:EntryPoint
022A0150 | B8 10FAA475 | mov eax,<kernel32.VirtualQuery> | eax:EntryPoint
022A0155 | FFE0 | jmp eax | eax:EntryPoint
022A0157 | B8 2009A575 | mov eax,<kernel32.SizeofResource> | eax:EntryPoint
022A015C | FFE0 | jmp eax | eax:EntryPoint
022A015E | B8 600BA575 | mov eax,<kernel32.SetThreadPriority> | eax:EntryPoint
022A0163 | FFE0 | jmp eax | eax:EntryPoint
022A0165 | B8 B00AA575 | mov eax,<kernel32.SetErrorMode> | eax:EntryPoint
022A016A | FFE0 | jmp eax | eax:EntryPoint
022A016C | B8 C034A575 | mov eax,<kernel32.ReleaseMutex> | eax:EntryPoint
022A0171 | FFE0 | jmp eax | eax:EntryPoint
022A0173 | B8 E0F7A475 | mov eax,<kernel32.LockResource> | eax:EntryPoint
022A0178 | FFE0 | jmp eax | eax:EntryPoint
022A017A | B8 E0ECA475 | mov eax,<kernel32.LoadResource> | eax:EntryPoint
022A017F | FFE0 | jmp eax | eax:EntryPoint
022A0181 | B8 7010A575 | mov eax,<kernel32.LoadLibraryA> | eax:EntryPoint
022A0186 | FFE0 | jmp eax | eax:EntryPoint
022A0188 | B8 F021A575 | mov eax,<kernel32.GetWindowsDirectoryA> | eax:EntryPoint
022A018D | FFE0 | jmp eax | eax:EntryPoint
022A018F | B8 001CA575 | mov eax,<kernel32.GetVersionExA> | eax:EntryPoint
022A0194 | FFE0 | jmp eax | eax:EntryPoint
022A0196 | B8 4028A575 | mov eax,<kernel32.GetTickCount> | eax:EntryPoint
022A019B | FFE0 | jmp eax | eax:EntryPoint
022A019D | B8 D08BA475 | mov eax,<kernel32.GetThreadLocale> | eax:EntryPoint
022A01A2 | FFE0 | jmp eax | eax:EntryPoint
022A01A4 | B8 8038A575 | mov eax,<kernel32.GetTempPathA> | eax:EntryPoint
022A01A9 | FFE0 | jmp eax | eax:EntryPoint
022A01AB | B8 8086A475 | mov eax,<kernel32.GetSystemDirectoryA> | eax:EntryPoint
022A01B0 | FFE0 | jmp eax | eax:EntryPoint
022A01B2 | B8 D09AA475 | mov eax,<kernel32.GetStringTypeA> | eax:EntryPoint
022A01B7 | FFE0 | jmp eax | eax:EntryPoint
022A01B9 | B8 A01BA575 | mov eax,<kernel32.GetStdHandle> | eax:EntryPoint
022A01BE | FFE0 | jmp eax | eax:EntryPoint
022A01C0 | B8 F0F9A475 | mov eax,<kernel32.GetProcAddress> | eax:EntryPoint
022A01C5 | FFE0 | jmp eax | eax:EntryPoint
022A01C7 | B8 000FA575 | mov eax,<kernel32.GetModuleHandleA> | eax:EntryPoint
022A01CC | FFE0 | jmp eax | eax:EntryPoint
022A01CE | B8 D012A575 | mov eax,<kernel32.GetModuleFileNameA> | eax:EntryPoint
022A01D3 | FFE0 | jmp eax | eax:EntryPoint
022A01D5 | B8 1086A475 | mov eax,<kernel32.GetLocaleInfoA> | eax:EntryPoint
022A01DA | FFE0 | jmp eax | eax:EntryPoint
022A01DC | B8 B0E4A475 | mov eax,<kernel32.GetLastError> | eax:EntryPoint
022A01E1 | FFE0 | jmp eax | eax:EntryPoint
022A01E3 | B8 2037A575 | mov eax,<kernel32.GetDiskFreeSpaceA> | eax:EntryPoint
022A01E8 | FFE0 | jmp eax | eax:EntryPoint
022A01EA | B8 D019A575 | mov eax,<kernel32.GetCPInfo> | eax:EntryPoint
022A01EF | FFE0 | jmp eax | eax:EntryPoint
022A01F1 | B8 6008A575 | mov eax,<kernel32.GetACP> | eax:EntryPoint
022A01F6 | FFE0 | jmp eax | eax:EntryPoint
022A01F8 | B8 800FA575 | mov eax,<kernel32.FreeLibrary> | eax:EntryPoint
022A01FD | FFE0 | jmp eax | eax:EntryPoint
022A01FF | B8 B02EA475 | mov eax,<kernel32.FindResourceA> | eax:EntryPoint
022A0204 | FFE0 | jmp eax | eax:EntryPoint
022A0206 | B8 70BD4C77 | mov eax,<ntdll.RtlExitUserThread> | eax:EntryPoint
022A020B | FFE0 | jmp eax | eax:EntryPoint
022A020D | B8 806DA575 | mov eax,<kernel32.ExitProcess> | eax:EntryPoint
022A0212 | FFE0 | jmp eax | eax:EntryPoint
022A0214 | B8 B094A475 | mov eax,<kernel32.EnumCalendarInfoA> | eax:EntryPoint
022A0219 | FFE0 | jmp eax | eax:EntryPoint
022A021B | B8 D035A575 | mov eax,<kernel32.DeleteFileA> | eax:EntryPoint
022A0220 | FFE0 | jmp eax | eax:EntryPoint
022A0222 | B8 B013A575 | mov eax,<kernel32.CreateThread> | eax:EntryPoint
022A0227 | FFE0 | jmp eax | eax:EntryPoint
022A0229 | B8 E033A575 | mov eax,<kernel32.CreateMutexA> | eax:EntryPoint
022A022E | FFE0 | jmp eax | eax:EntryPoint
022A0230 | B8 A035A575 | mov eax,<kernel32.CreateFileA> | eax:EntryPoint
022A0235 | FFE0 | jmp eax | eax:EntryPoint
022A0237 | B8 5033A575 | mov eax,<kernel32.CloseHandle> | eax:EntryPoint
022A023C | FFE0 | jmp eax | eax:EntryPoint
022A023E | B8 A013A575 | mov eax,<kernel32.Sleep> | eax:EntryPoint
022A0243 | FFE0 | jmp eax | eax:EntryPointLa solución: haremos un script para leer los stubs en x32dbg
La buena noticia es que la dirección real de la API está dentro del stub: es el inmediato del mov eax, .... Entonces podemos recorrer la IAT, leer ese inmediato de cada stub y reescribir el slot con la API real. La IAT está en 43B194, con tamaño 0x1FC (termina en 43B390).
var $slot
var $stub
var $api
var $n
mov $slot, 43B194
mov $n, 0
loop:
cmp [$slot], 2200000
jb next
cmp [$slot], 2300000
jae next
mov $stub, [$slot]
cmp byte:[$stub], B8
jne next
mov $api, [$stub+1]
mov [$slot], $api
inc $n
next:
add $slot, 4
cmp $slot, 43B390
jb loop
log "slots parcheados: {d:$n}"
retCómo funciona, en pocas palabras:
- Recorre cada slot de la IAT de 4 en 4 bytes, desde
43B194hasta43B390. - Solo toca los slots cuyo valor cae en el rango
0x2200000–0x2300000, la zona de los stubs. Los slots sanos se dejan como están. - Verifica que el primer byte del stub sea
B8elmov eax, imm32. - Lee el inmediato
[$stub+1]y lo escribe en el slot.
Ojo: el rango de los stubs
0x0220xxxxdepende de tu ejecución. Mirá a qué direcciones apuntan tus slots inválidos y ajustá los límites del script.
Ejecutamos el script con el proceso detenido en el OEP.

Ahora volvemos a Scylla y llenamos los campos y hacemos Get Imports, y por las dudas Show Invalid:

118 APIs válidas, 0 inválidas.
Los grupos que estaban en rojo ahora resuelven a kernel32.dll (42 + 1 entradas).
Tip: antes de escribir un script de reparación, mirá qué patrón tienen los slots malos. Si los nombres de las APIs reales están en texto claro dentro del stub, identificar a dónde apunta cada slot es mucho más fácil para reconstruir la IAT.
El volcado (dump) y el fix
Con el proceso todavía detenido en el OEP y la IAT ya reparada, volcamos desde Scylla:
- Verificá que el OEP sea
0x40F844(RVA0xF844) y que la IAT quede en43B194con tamaño0x1FC. - Hacé Get Imports una última vez y confirmá que no haya entradas inválidas.
- Presioná Dump y guardá el archivo (por ejemplo,
UnPackme01_dump.exe). - Presioná Fix Dump y seleccioná ese archivo. Scylla genera una copia con los imports reconstruidos (
UnPackme01_dump_SCY.exe). Usá siempre este archivo, no el del paso anterior. - (Opcional) Si querés, abrí el resultado en un editor hexadecimal o en PE-bear y normalizá la firma del offset 0 a
MZ. Es cosmético: el loader de Windows no depende de eso. Para comprobar que el desempacado funcionó:
- Al abrí el archivo en Detect It Easy: ya no aparecer PECompact y, ahora muestra claramente el compilador (Delphi/Borland).

- Al cargarlo en x32dbg: el entry point ya es
0x40F844y el código es legible, sin stubs. - Al ejecutalo en la VM se verifica que arranca tal cual como el original.
Tip: si el dump no abre o falla al ejecutarse, revisá primero la IAT (es la causa más habitual) y después el valor del OEP en Scylla.
Resumen: cómo lo resolvimos
- Hardware breakpoint de ejecución en el handler SEH (
45E6EC) y Shift+F9 para pasar la excepción. - Breakpoint en
45E70F(inicio real del stub). - Descompresora (
call ecxen45E76D) y segunda etapa (call edien45E78A). No entramos en detalles. - Hardware breakpoint en
45E7AE(jmp eax):eaxes el OEP. - Parchear la IAT con el script (los slots
0x0220xxxxapuntan a stubsmov eax, API / jmp eax). - Scylla: Get Imports, Dump y Fix Dump.
¿Y ahora qué?
Si fuera un malware, con el programa ya desempacado, el trabajo de análisis recién empieza.
