尝试使用 OllyDbg v201 解压 hello world 程序

逆向工程 视窗 ollydbg 工具
2021-06-22 19:31:47

我正在尝试学习如何解压缩一个用crinkler压缩的简单可执行文件,让这里的 nasm 列表用 crinkler 压缩。

示例 1.asm

global start
; kernel32.lib Exports
extern _ExitProcess@4
extern _GetStdHandle@4
extern _WriteFile@20

section .text

start:
    ; DWORD  bytes;
    mov     ebp, esp
    sub     esp, 4

    ; hStdOut = GetstdHandle( STD_OUTPUT_HANDLE)
    push    -11
    call    _GetStdHandle@4
    mov     ebx, eax

    ; WriteFile( hstdOut, message, length(message), &bytes, 0);
    push    0
    lea     eax, [ebp-4]
    push    eax
    push    (message_end - message)
    push    message
    push    ebx
    call    _WriteFile@20

    ; ExitProcess(0)
    push    0
    call    _ExitProcess@4

    ; never here
    hlt
message:
    db      'Hello, World', 10
message_end:

要生成 exe,我正在使用最新版本的nasm & crinkler像这样nasm -f win32 example1.asm && crinkler example1.obj kernel32.lib user32.lib opengl32.lib winmm.lib gdi32.lib legacy_stdio_definitions.lib oldnames.lib ucrt.lib /out:example1_crinkler.exe /CRINKLER /HASHTRIES:300 /COMPMODE:SLOW /ORDERTRIES:4000 /entry:start /subsystem:console

要解压 exe,我使用的是OllyDbg v201和最新版本的OllyDumpEx v1.50这里的问题是,OllyDebugEx 的网站只有这些部分{概述、功能、屏幕截图、支持的调试器、下载、更改日志},根本没有文档,这意味着该插件假设您已经体验过所有选项/术语。

现在我已经到了我已经想出如何解压缩我的测试可执行文件并找到 OEP 的地步,当前状态如下:

在此处输入图片说明

我现在想知道的是了解 OllyDumpEx 提供的整套可用选项:

  • 模块 {PE Base, List Section, Dump Mode, PE Source}
  • 搜索 { 搜索区域,搜索模式}
  • PE {图像基础,部分对齐,入口点}
  • 选项
  • 部分

一旦我知道如何正确转储它,我还想知道如何修复它,以便获得最终的未压缩 exe。

3个回答

出于好奇,我下载了 crinkler,用 Visual C++ 编译了这段代码

#include <windows.h>
int WINAPI WinMain(__in HINSTANCE,__in_opt HINSTANCE,__in LPSTR,__in int) {
    MessageBox(NULL, "Testing Crinky", "CRINKLE", MB_OK);
    ExitProcess(NULL);
}

并将目标文件与 crinkler20.exe 链接

cl -c /nologo /W4 /O1 /Zi /analyze *.cpp

crinkler.exe /ENTRY:WinMain crinky.obj kernel32.lib user32.lib /CRINKLER /HASHTRIES:300 /COMPMODE:SLOW /ORDERTRIES:4000 /SUBSYSTEM:windows /out:crinky.exe

它创建了一个运行良好的 459 字节 exe

ls -la crinky.exe
-rwxrwxrwx  1 HP 0 459 2016-11-15 15:25 crinky.exe

在此处输入图片说明

在十六进制编辑器中打开它,看看里面有什么,crinkler 采用了 pe header 破坏技术(它似乎使用 peheader 来填充代码)

这是一个 hexedit 视图(来自 hxd)

Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000  4D 5A 32 30 50 45 00 00 4C 01 00 00 01 DB 61 7F  MZ20PE..L....Ûa.
00000010  10 D0 17 73 75 47 EB F9 08 00 02 00 0B 01 11 C9  .Ð.suGëù.......É
00000020  45 85 C0 79 1F 01 D3 50 F7 E2 90 3D 5C 00 00 00  E…Ày..ÓP÷â.=\...
00000030  F7 F3 39 C1 19 DB EB 48 00 00 40 00 04 00 00 00  ÷ó9Á.ÛëH..@.....
00000040  04 00 00 00 0F A3 2D 29 01 40 00 8D 04 00 EB CE  .....£-).@....ëÎ
00000050  00 00 00 00 EB B6 42 06 40 00 00 00 53 31 ED BB  ....ë¶B.@...S1í»
00000060  02 00 00 00 90 BE 14 01 40 00 6A 01 58 BF 00 00  .....¾..@.j.X¿..
00000070  42 00 B1 00 90 57 EB 12 00 00 00 00 00 00 00 00  B.±..Wë.........
00000080  5A 72 07 92 29 D1 04 00 29 D0 60 AD 01 F8 74 2C  Zr.’)Ñ..)Ð`..øt,
00000090  6A 0A 5A 89 14 54 89 54 24 10 AD 31 ED 4D 45 01  j.Z‰.T‰T$..1íME.
000000A0  C0 72 FB 74 AF 60 AC 88 C2 32 07 6B C0 6F 02 87  Àrût¯`¬ˆÂ2.kÀo.‡
000000B0  00 00 00 00 48 4F 00 D2 72 EF 75 F9 BF CE 00 42  ....HO.Òrïuù¿Î.B
000000C0  00 B9 F5 E9 1F 03 73 0C F3 66 AB 0A 06 61 8D 76  .¹õé..s.óf«..a.v
000000D0  0C 7B B7 C3 F7 F1 8D 3C 57 89 E9 31 C0 AE 74 04  .{·Ã÷ñ.<W‰é1À®t.
000000E0  00 07 75 02 41 41 0F B6 14 07 D3 E2 01 54 84 34  ..u.AA.¶..Óâ.T„4
000000F0  48 7A F3 85 DB 7F 0D D0 2C 1F 75 03 D0 14 1F F7  Hzó…Û..Ð,.u.Ð..÷
00000100  D3 FE 04 1F 61 46 EB 95 E8 6A A6 38 C6 6F 82 E9  Óþ..aFë•èj¦8Æo‚é
00000110  1B 13 09 4F 55 FF BD FF FE FF FF 9A 00 20 80 90  ...OUÿ½ÿþÿÿš. €.
00000120  32 FF BD FF FF FF FF DF 00 44 BE 63 EE 28 9E 30  2ÿ½ÿÿÿÿß.D¾cî(ž0
00000130  07 9C 4B 16 0D 6B A8 05 A4 65 86 46 50 14 FE B3  .œK..k¨.¤e†FP.þ³
00000140  42 F6 F7 88 0C 1A 94 68 7D 22 BF 3C 30 2F DD C7  Bö÷ˆ..”h}"¿<0/ÝÇ
00000150  6B F9 26 24 83 12 7D A2 3A 05 85 FE B3 D2 B1 CF  kù&$ƒ.}¢:.…þ³Ò±Ï
00000160  0F B2 80 7D 9A 49 1C 61 97 36 9F 22 F6 54 CD 3F  .²€}šI.a—6Ÿ"öTÍ?
00000170  3B 0E 4B 81 F7 A3 6D 3C 54 89 06 C3 37 51 99 E0  ;.K.÷£m<T‰.Ã7Q™à
00000180  E6 1E D8 B1 E4 7C 73 D4 9C 36 78 DA 27 8C 39 F3  æ.رä|sÔœ6xÚ'Œ9ó
00000190  BD BF 42 6B F3 1C D9 B1 E7 A5 E7 60 1F C6 5B DF  ½¿Bkó.Ù±ç¥ç`.Æ[ß
000001A0  8F A6 47 E0 B5 A7 DD 3D D6 4A CA 91 3C 35 B3 74  .¦G൧Ý=ÖJÊ‘<5³t
000001B0  CB A1 81 90 90 32 39 E4 B0 0F 4D EA E4 EA 2F 0A  Ë¡...29ä°.Mêäê/.
000001C0  6C 06 7C 8F E8 9B B1 50 6E 55 0B                 l.|.è›±PnU.

linux 文件实用程序说它是一个 ms dos exe

dumpbin 抱怨没有 COFF 标头

dumpbin /nologo crinky.exe

Dump of file crinky.exe

File Type: EXECUTABLE IMAGE
LINK : fatal error LNK1235: corrupt or invalid COFF symbol table

file crinky.exe
crinky.exe; MS-DOS executable, MZ for MS-DOS

将其加载到 windbg 并查看标题似乎已完全损坏

cdb -c ".foreach /pS 4 /ps 100 (place { lm }) { !dh place };q" crinky.exe | grep quit: -B 53

File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
     14C machine (i386)
       0 number of sections
7F61DB01 time date stamp Mon Sep 21 09:48:09 2037

7317D010 file pointer to symbol table
F9EB4775 number of symbols
       8 size of optional header
       2 characteristics
            Executable

OPTIONAL HEADER VALUES
     10B magic #
  17.201 linker version
79C08545 size of code
50D3011F size of initialized data
3D90E2F7 size of uninitialized data
      5C address of entry point
C139F3F7 base of code
         ----- new -----
00400000 image base
       4 section alignment
       4 file alignment
       2 subsystem (Windows GUI)
41743.10541 operating system version
16385.36096 image version
 4.52971 subsystem version
 642B6EB size of image
      40 size of headers
BBED3153 checksum
0114be90 size of stack reserve
016a0040 size of stack commit
0000bf58 size of heap reserve
00b10042 size of heap commit
       0  DLL characteristics
       0 [9207725A] address [size] of Export Directory
   4D129 [AD60D029] address [size] of Import Directory
2C74F801 [895A0A6A] address [size] of Resource Directory
54895414 [31AD1024] address [size] of Exception Directory
 1454DED [74FB72C0] address [size] of Security Directory
88AC60AF [6B0732C2] address [size] of Base Relocation Directory
87026FC0 [       0] address [size] of Debug Directory
D2004F48 [F975EF72] address [size] of Description Directory
4200CEBF [E9F5B900] address [size] of Special Directory
 C73031F [ AAB66F3] address [size] of Thread Storage Directory
768D6106 [C3B77B0C] address [size] of Load Configuration Directory
3C8DF1F7 [31E98957] address [size] of Bound Import Directory
 474AEC0 [ 2750700] address [size] of Import Address Table Directory
B60F4141 [E2D30714] address [size] of Delay Import Directory
34845401 [85F37A48] address [size] of COR20 Header Directory
D00D7FDB [ 3751F2C] address [size] of Reserved Directory

quit:

测试入口点函数是否是使用 cdb 单退出的函数,没有间接调用/jmps 并且它只包含一个退出单返回

cdb -c "g @$exentry;uf @eip;q" crinky.exe | grep -iE "call|ret"
004000d3 c3              ret

所以在那里设置一个 bp 并执行并转储解密区域

cdb -c "g 4000d3;t \"dc @eip l38;q\"" crinky.exe  | grep quit: -B 17

0:000> cdb: Reading initial command 'g 4000d3;t "dc @eip l38;q"'
*** WARNING: Unable to verify timestamp for image00400000
*** ERROR: Module load completed but symbols could not be loaded for image00400000
00420000  400108bb 00abbe00 00bf0042 58004300  ...@....B....C.X
00420010  8b0c408b 008b0c40 688b008b 75ed8518  .@..@......h...u
00420020  6a006a0e 006a5200 000815ff 31c30043  .j.j.Rj.....C..1
00420030  6091acc0 013c458b 78508be8 4a8bea01  ...`.E<...Px...J
00420040  20428b18 748be801 ee01fc88 c7c1ff31  ..B ...t....1...
00420050  acc03106 7d48c731 e03b3bf5 24428be4  .1..1.H}.;;...B$
00420060  8b66e801 428b480c 8be8011c 44898804  ..f..H.B.......D
00420070  01611c24 c383abe8 56b8e204 000415ff  $.a........V....
00420080  89950043 c8feacf2 c0fefb79 006a8f74  C.......y...t.j.
00420090  4200b868 00c06800 006a0042 000815ff  h..B.h..B.j.....
004200a0  006a0043 000015ff 02cc0043 72657375  C.j.....C...user
004200b0  01003233 000000ff 4e495243 00454c4b  32......CRINKLE.
004200c0  74736554 20676e69 6e697243 0000796b  Testing Crinky..
004200d0  00000000 00000000 00000000 00000000  ................
quit:

数据似乎已被解密,但导入已在此处解决

我们可以通过任何方式在三个调用中访问我们的代码

cdb -c "g 4000d3;rm 0;pct 3"  crinky.exe

0:000> cdb: Reading initial command 'g 4000d3;rm 0;pct 3'
0042007c ff1504004300    call  dword ptr [(00430004)] ={kernel32!LoadLibraryA (7676395c)}
0042007c ff1504004300    call  dword ptr [(00430004)] ={kernel32!LoadLibraryA (7676395c)}
0042009c ff1508004300    call  dword ptr [(00430008)] ={user32!MessageBoxA (75e6ea11)}
0:000> ub @eip
image00400000+0x20086:
00420086 fec8            dec     al
00420088 79fb            jns     image00400000+0x20085 (00420085)
0042008a fec0            inc     al
0042008c 748f            je      image00400000+0x2001d (0042001d)
0042008e 6a00            push    0
00420090 68b8004200      push    offset image00400000+0x200b8 (004200b8)
00420095 68c0004200      push    offset image00400000+0x200c0 (004200c0)
0042009a 6a00            push    0
0:000> da 4200b8
004200b8  "CRINKLE"
0:000> da 4200c0
004200c0  "Testing Crinky"
0:000>

基于这些,如果没有严肃的代码,未压缩的可执行文件可能无法转储(它​​没有任何导入表/也没有 pe 头可以使用)

您可能需要预先添加一个 pe 标头并将导入部分附加到原始内存中

至于 ollydumpex 它是一个有很多选择的工具

pebase 您可以选择任何模块,内存范围,地址范围列表部分将根据 pebase 选择列出找到的部分,例如,如果您选择内存列表部分将列出从 0x10000 到 0x7fffffff(32 位 2 gb va)的整个虚拟地址范围range ) 当您重新扫描 search pe 在范围内搜索 pe 标头时

如果可能,转储模式重建会尝试重建导入(可能是可运行的 exe)

原始转储内存中的内容(用于取证而不是运行 exe)

但是您可以看到 ollydumpex 抱怨状态栏中的标头损坏,因此目前使用 ollydumpex 进行转储不是可行的选择

好吧,如果您开始编写特殊的转储程序,填充 pe 头文件导入等,您可以转储内容,这可能是一项严肃的工作

小开始

#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
int dumpdos (char * filepath){
  IMAGE_DOS_HEADER imdos = {0};
  FILE * fp = fopen(filepath,"r");
  if(fp)  { fseek(fp,0,SEEK_SET);
    fread(&imdos,sizeof(BYTE),sizeof(IMAGE_DOS_HEADER),fp); fclose(fp);
  }
  printf("%08x\n",imdos.e_magic);
  printf("%08x\n",imdos.e_lfanew);
  return imdos.e_lfanew;
}
void dumpnt (char * filepath , int off_pehead){
  IMAGE_NT_HEADERS imnt = {0};
  FILE * fp = fopen(filepath,"r");
  if(fp)  { fseek(fp,off_pehead,SEEK_SET);
    fread(&imnt,sizeof(BYTE),sizeof(IMAGE_NT_HEADERS),fp); fclose(fp);
  }
  printf("%08x\n",imnt.Signature);
  printf("%08x\n",imnt.FileHeader.Machine);
  printf("%08x\n",imnt.FileHeader.NumberOfSections);
  printf("%08x\n",imnt.FileHeader.SizeOfOptionalHeader);
  printf("%08x\n",imnt.OptionalHeader.Magic);
  printf("%08x\n",imnt.OptionalHeader.AddressOfEntryPoint); 
  printf("%08x\n",imnt.OptionalHeader.ImageBase);
}
void main (int argc,char* argv[]){
  if(argc !=2){     printf("usage %s <path to binary>",argv[0] ); exit(-1); }
  printf ("dumping e_magic and e_lfanew from header of %s\n" , argv[1]);
  int pehead = dumpdos(argv[1]);
  printf("dumping NtSig , M/c, sect_nos, sizeopt_hdr, opthdrmagic, &EP, imgbase\n");
  dumpnt(argv[1],pehead);
}

执行它

dumpcrink.exe ..\crinky.exe

dumping e_magic and e_lfanew from header of ..\crinky.exe
00005a4d
00000004
dumping NtSig , M/c, sect_nos, sizeopt_hdr, opthdrmagic, &EP, imgbase
00004550
0000014c
00000000
00000008
0000010b
0000005c
00400000

自定义导入解析器很可爱

CPU Disasm
Address   Command                                  Comments
00420000  MOV     EBX, OFFSET 00400108             ; hashtable
00420005  MOV     ESI, OFFSET 004200AB             ; no of imports to resolve in k32
0042000A  MOV     EDI, OFFSET 00430000             ; destination for resolved imports
0042000F  POP     EAX                              ; peb
00420010  MOV     EAX, DWORD PTR DS:[EAX+0C]       ; Peb.Ldr
00420013  MOV     EAX, DWORD PTR DS:[EAX+0C]       ; inLoadorderModuleList
00420016  MOV     EAX, DWORD PTR DS:[EAX]          ; flink ntdll
00420018  MOV     EAX, DWORD PTR DS:[EAX]          ; flink k32
0042001A  MOV     EBP, DWORD PTR DS:[EAX+18]       ; kernel32.dllbaseAddr
0042001D  TEST    EBP, EBP                         
;  dx ((ntdll!_LDR_DATA_TABLE_ENTRY *)((int )((ntdll!_PEB *) @$peb)->Ldr->
;InLoadOrderModuleList.Flink->Flink->Flink))->DllBase
0042001F  JNE     SHORT 0042002F                   ; if this is not k32base exit
00420021  PUSH    0
00420023  PUSH    0
00420025  PUSH    EDX
00420026  PUSH    0
00420028  CALL    NEAR DWORD PTR DS:[430008]       ; will never reach here 
;if reaches here in case of no k32 it will crash with null dereference
0042002E  RETN
0042002F  XOR     EAX, EAX
00420031  LODS    BYTE PTR DS:[ESI]                ; no of imports from k32
00420032  XCHG    EAX, ECX                         ; save no of imports from k32
00420033  PUSHAD
00420034  MOV     EAX, DWORD PTR SS:[EBP+3C]       ; k32peheaderptr (e_lfanew)
00420037  ADD     EAX, EBP                         ; k32peheaderptr PE
00420039  MOV     EDX, DWORD PTR DS:[EAX+78]       ; exporttable of k32
0042003C  ADD     EDX, EBP                         ; k32_imageexportdir
0042003E  MOV     ECX, DWORD PTR DS:[EDX+18]       ; noofname
00420041  MOV     EAX, DWORD PTR DS:[EDX+20]       ; addressofnamerva
00420044  ADD     EAX, EBP                         ; virtualaddrof export names
00420046  MOV     ESI, DWORD PTR DS:[ECX*4+EAX-4]  ; last export rva
0042004A  ADD     ESI, EBP                         ; last export addr name
0042004C  XOR     EDI, EDI                         ; 0
0042004E  ROL     EDI, 6                           ; creating hash
00420051  XOR     EAX, EAX
00420053  LODS    BYTE PTR DS:[ESI]                ; first/nth letter of export name
00420054  XOR     EDI, EAX
00420056  DEC     EAX
00420057  JGE     SHORT 0042004E                   ; null termin seek
00420059  CMP     EDI, DWORD PTR DS:[EBX]          ; comparehash with [400108]
0042005B  LOOPNZ  SHORT 00420041                   ; reloop
0042005D  MOV     EAX, DWORD PTR DS:[EDX+24]       ; matched hash end of iit
00420060  ADD     EAX, EBP
00420062  MOV     CX, WORD PTR DS:[ECX*2+EAX]
00420066  MOV     EAX, DWORD PTR DS:[EDX+1C]       ; addroffunctions
00420069  ADD     EAX, EBP
0042006B  MOV     EAX, DWORD PTR DS:[ECX*4+EAX]
0042006E  MOV     DWORD PTR SS:[ESP+1C], EAX       
; modify register eax so popad will hold the rva of k32 export
00420072  POPAD                                    ; eax stays same as addr of 
;functions
00420073  ADD     EAX, EBP                         ; rva+ base = addr of proc
00420075  STOS    DWORD PTR ES:[EDI]               ; getprocaddr achieved
00420076  ADD     EBX, 4                           ; store at 430000
00420079  LOOP    SHORT 00420033                   
  ; resolves all exports from k32 + loadlibrary
0042007B  PUSH    ESI                              ; ASCII "user32"
0042007C  CALL    NEAR DWORD PTR DS:[430004]       ; loadlibs user32 or whatever dll
00420082  XCHG    EAX, EBP
00420083  MOV     EDX, ESI                         ; ASCII "user32"
00420085  LODS    BYTE PTR DS:[ESI]                ; again null term for dll name
00420086  DEC     AL
00420088  JNS     SHORT 00420085
0042008A  INC     AL
0042008C  JE      SHORT 0042001D                   ;
; so this is standard sized custom import resolver

所以我创建了另一个带有更多 api 调用的 exe 并检查它并
在 42008e 和 f9nned 上设置了硬件 exec bp 我登陆了实际代码

CPU Disasm
Address   Hex dump               Command                                                  Comments
004000BC    BF 3A014200          MOV     EDI, OFFSET 0042013A <---------
004000C1    B9 23E91F03          MOV     ECX, OFFSET 031FE923
004000C6    73 0C                JAE     SHORT 004000D4
004000C8    F3:66:AB             REP STOS WORD PTR ES:[EDI]

所以 42008e 是实际代码的开始并跨越硬编码地址
430000 是解析导入

CPU Dump
Address   Hex dump                                         ASCII
0042008E  53 56 8B 35|08 00 43 00|33 DB 57 53|BF E4 00 42| SV‹5 C 3ÛWS¿ä B
0042009E  00 57 68 1C|01 42 00 53|FF D6 53 57|68 0C 01 42|  WhB SÿÖSWhB
004200AE  00 53 FF D6|53 57 68 2C|01 42 00 53|FF D6 53 57|  SÿÖSWh,B SÿÖSW
004200BE  68 FC 00 42|00 53 FF D6|53 57 68 EC|00 42 00 53| hü B SÿÖSWhì B S
004200CE  FF D6 53 FF|15 00 00 43|00 5F 5E 5B|02 75 73 65| ÿÖSÿ  C _^[use
004200DE  72 33 32 00|01 FF 43 52|49 4E 4B 4C|45 00 54 65| r32 ÿCRINKLE Te
004200EE  73 74 69 6E|67 20 43 72|6F 6E 6B 79|00 00 54 65| sting Cronky  Te
004200FE  73 74 69 6E|67 20 43 72|65 6E 6B 79|00 00 54 65| sting Crenky  Te
0042010E  73 74 69 6E|67 20 43 72|61 6E 6B 79|00 00 54 65| sting Cranky  Te
0042011E  73 74 69 6E|67 20 43 72|69 6E 6B 79|00 00 54 65| sting Crinky  Te
0042012E  73 74 69 6E|67 20 43 72|75 6E 6B 79|00 00 00 00| sting Crunky

如果我将其二进制复制并将其转储到 bin 文件中,我可以通过重新设置 0x40008e 创建一个 xtern 段 a、d 等轻松地使用 ida 对其进行分析

josh 在下面写了一篇带有修补说明的长篇文章,一个不需要去修补说明的人只需要 hw bp 0x42008e -> f9 _> 将入口点的 addr 更改为 2008e 而不是原始 pe 标头中的 5c 创建备份并保存备份

这个内存转储可以在 idapro 中按原样加载,它会像原始 exe 一样漂亮

ollydbg crinkledexe -> ctrl+g 42008e -> right click-> exec hw bpt -> f9 _> patch ADDROF ENTRYPOINT TO 0X2008E INSTEAD OF 0X5C ->right click_> createbackup _> right click _> save backup-> load in ida demo 6.9 bingo it is as nice as it get 

由于您拥有原始程序并且可以检查 OEP 的外观,也许只需尝试调试解包器,直到找到看起来像 OEP 的代码。然后您可以尝试将其推广到其他可执行文件。

如果我正确理解了问题,那么如果可以生成未压缩(并且正在运行!)的 exe(已与 crinkler 链接),则该任务将得到解决。在下文中,我将展示如何实现这一目标的步骤。但是,所有步骤都是在 Ida 中完成的,而不是在 Olly 中完成的,因为我对 Olly 不熟悉。程序应该非常相似。下面是步骤:

总结:让它运行直到压缩器的“ret”之后,转储到一个bin文件,将下面解释的单个字节修补成绕过解压缩器的“ret”,然后完成。这种技术的好处是绝对不需要摆弄 PE 标头。所有带有已被 crinkler 损坏的 PE 标头的神奇加载保持完好无损。PE 标头不需要以任何方式触及。现在,更详细的描述如下:

Step1:生成exe。我按照已经在此线程中提供的 WinMain 和消息框进行了操作。这按预期正常工作(Win10,编译器 VS2013)。

第二步:加载到 Ida 中,起始地址为 0x40005c。一些警告,但最终启动功能出现了。可以在“开始”位置设置断点。值得注意且有趣的是,此初始代码的第一行不得更改,否则 Ida 会产生奇怪的错误消息,例如不接受 Win32 本地调试器或询问有关 %1 对象的问题。我认为这是因为此启动代码必须为加载程序生成 NOP,因为它可能驻留在 PE 标头中的某处。

第 3 步:让解压器一直运行,直到本线程中已经讨论过“ret”语句(在我的示例中,地址为 0x4000d3)。这个“ret”表示解压器结束,代码返回到应用程序部分,但要注意:应用程序代码尚未与 Kernel32.lib 链接。使用了一种典型的技术,例如 shellcode 编程中使用的技术。发现 kernel32.dll 地址类似于通常(且未记录)的方式,多年前由 skape 出色地编写。一旦知道 kernel32.dll 地址,就可以动态收集所有其他操作系统 dll 和 API 调用。在此之前,显然由于压缩程序,诸如“LoadLibraryA”、“user32.dll”或“MessageBoxA”之类的必要名称已以散列形式存储。所有 windows 库加载和访问都是在运行时动态完成的,也是应用程序定义的字符串的生成。所有这一切的目的都是为了提高压缩率,并且(IMO!)与核心压缩算法无关。与shellcode编程的相似之处显而易见:空闲RAM空间是非常宝贵的资源,因此shellcode必须尽可能紧凑。每个字节都很重要。

第 4 步:在应用程序部分的开头,即在讨论的“ret”语句(在我的示例中为地址 0x420000)之后,必须进行内存转储,RVA 0x400000 成为文件中的偏移量零。我的转储从 0x00400000 开始,长度为 0x50000。解压缩器使用的最高地址在示例中约为 0x430000。转储在 Ida 中是微不足道的,有一个合适的小脚本。

第 5 步:绕过解压缩器。正如已经提到的,这不能在“开始”点完成,因为它不会成功。但幸运的是,稍后的一些语句(地址 0x400076)堆栈已被解压缩器加载,并具有正确的应用程序地址 0x420000。所有需要修补的是地址 400076 处的“ret”语句,将代码引入未压缩部分。单字节补丁就足够了!开始部分现在看起来像这样:

HEADER:0040005C                 public start
HEADER:0040005C start           proc near
HEADER:0040005C                 push    ebx
HEADER:0040005D                 xor     ebp, ebp
HEADER:0040005F                 mov     ebx, 2
HEADER:00400064                 nop
HEADER:00400065                 mov     esi, 400114h
HEADER:0040006A                 push    1
HEADER:0040006C                 pop     eax
HEADER:0040006D                 mov     edi, 420000h
HEADER:00400072                 mov     cl, 0
HEADER:00400074                 nop
HEADER:00400075                 push    edi
HEADER:00400076                 retn

第 6 步:就是这样,缺少的是将文件重命名为 something.exe 并尝试它是否有效。它起作用了,Windows 加载程序没有任何抱怨,并且可以在调试器中使用静态断点进行调查。

当然,如果需要,可以提供生成的未压缩的exe。