找回密码
 开放注册

QQ登录

只需一步,快速开始

微信登录

微信扫码,快速开始

搜索
查看: 924|回复: 0

PE学习

[复制链接]

246

主题

98

回帖

675

牛毛

一级牛人

黑客帝国

积分
675
发表于 2009-7-23 19:58:28 | 显示全部楼层 |阅读模式 来自 河北省沧州市
Luevelsmeyer 的pe.txt总算是看完了,感谢ah007的翻译。
  最后译者说道精华在于附录中,我略作修改重建一个文件,但不管怎么调,都无法运行。
  一开始还以为是什么地方没有写对,可运行附的hello.exe也是报错。
  看来用0x20对齐在XP下是不行的。这篇附录中就是太老了...
  
  我来说说我改写的过程吧。
  1.先写一段简单的代码
  ;MASMPlus 代码模板 - 普通的 Windows 程序代码
  .386
  .Model Flat, StdCall
  Option Casemap :None
  
  Include user32.inc
  Include kernel32.inc
  
  IncludeLib user32.lib
  IncludeLib kernel32.lib
  
  .data
  MsgBoxText db "Hello.",0
  MsgCaption db "Test",0
  
  .CODE
  START:
  invoke MessageBox, NULL,addr MsgBoxText, addr MsgCaption, MB_OK
  invoke ExitProcess,NULL
  END START
  
  顺便提一下,我用的是MASMPlus,可以在http://www.aogosoft.com/下载。
  
  2.编译完之后用OD看一看
  004****1000 >/$  6A 00      push    0                  ; /Style = MB_OK|MB_APPLMODAL
  004****1002  |.  68 073****4000    push    004****3007              ; |Title = "Test"
  004****1007  |.  68 003****4000    push    004****3000              ; |Text = "Hello."
  00***00C  |.  6A 00      push    0                  ; |hOwner = NULL
  00***00E  |.  E8 070****0000    call    <jmp.&user32.MessageBoxA>      ; \MessageBoxA
  004****1013  |.  6A 00      push    0                  ; /ExitCode = 0
  004****1015  \.  E8 060****0000    call    <jmp.&kernel32.ExitProcess>      ; \ExitProcess
  00***01A   $- FF25 082****4000  jmp     dword ptr [<&user32.MessageBoxA>>  ;  user32.MessageBoxA
  004****1020   .- FF25 002****4000  jmp     dword ptr [<&kernel32.ExitProces>  ;  kernel32.ExitProcess
  
  不错,以后代码照这个抄就可以了。
  发现用到了两个DLL的函数,稍微复杂了一点。
  
  再看看原文中的代码
  6A 00          ; push      0x000****0000
  68 d0 01 10 00      ; push      offset _written
  6A 0D          ; push      0x00***00d
  68 c0 01 10 00      ; push      offset hello_string
  6A F5          ; push      0xfffffff5
  2E FF 15 28 02 10 00  ; call      dword ptr cs:__imp__GetStdHandle@4
  50      &n
bsp;   ; push      eax
  2E FF 15 24 02 10 00  ; call      dword ptr cs:__imp__WriteConsoleA@20
  C3          ; ret
  
  注意两个call对应的机器码:E8和2E的不同,看来E8后面跟的是一个偏移量,
  00***01A   $- FF25 082****4000  jmp     dword ptr [<&user32.MessageBoxA>>  ;  user32.MessageBoxA
  这条命令算是跳板的跳板。不想这样写,还是用2E FF 15 。
  估算一下,代码长度在0x20之内。
  
  3.建一个Hello.exe,用WinHex来改。
  a)  先大约有个思路。
  文件对齐  0x00 00 02 00;
  区段对齐  0x00 00 10 00;
  ImageBase  0x00 40 00 00;
  只要两个段,一个代码段(Raw:0x200 / Rva:0x1000),把上面的代码放进去就可以了;
  一个数据段(Raw:0x400 / Rva:0x2000),一上来先放两个字符串,然后是输入目录。
  
  这样看来最多0x600个字节就可以搞定了,打开WinHex,加这么多的0。
  然后就可以开始改了。
  
  b)Dos Stub 这部分很简单,只要有MZ,然后设一下e_lfanew就可以了。
  000****0000   4D 5A 00 00 00 00 00 00  00 00 00 00 00 00 00 00   MZ..............
  000****0010   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  000****0020   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  000****0030   00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 00   ............@...
  
  c)NT Header 部分这样看肯定一头雾水...
  000****0040   50 45 00 00 4C 01 02 00  00 00 00 00 00 00 00 00   PE..L...........
  000****0050   00 00 00 00 E0 00 02 01  0B 01 00 00 20 00 00 00   ....?...... ...
  000****0060   D0 00 00 00 00 00 00 00  00 10 00 00 00 10 00 00   ?..............
  000****0070   00 20 00 00 00 00 40 00  00 10 00 00 00 02 00 00   . ....@.........
  000****0080   04 00 00 00 00 00 00 00  04 00 00 00 00 00 00 00   ................
  000****0090   00 30 00 00 00 02 00 00  00 00 00 00 02 00 00 00   .0..............
  00***00A0   00 00 10 00 00 10 00 00  00 00 10 00 00 10 00 00   ................
  00***00B0   00 00 00 00 10 00 00 00  00 00 00 00 00 00 00 00   ................
  00***00C0   20 20 00 00 B0 00 00 00  00 00 00 00 00 00 00 00     ..?..........
  00***00D0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  00***00E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  00***00F0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  000****0100   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  000****0110   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  000****0120   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  000****0130   00 00 00 00 00 00 00 00  2E 63 6F 64 65 00 00 00   .........code...
  000****0140   00 00 00 00 00 10 00 00  20 00 00 00 00 02 00 00 &
nbsp; ........ .......
  000****0150   00 00 00 00 00 00 00 00  00 00 00 00 20 00 00 60   ............ ..`
  000****0160   2E 64 61 74 61 00 00 00  00 00 00 00 00 20 00 00   .data........ ..
  000****0170   C0 00 00 00 00 04 00 00  00 00 00 00 00 00 00 00   ?..............
  000****0180   00 00 00 00 40 00 00 C0                                                ....@..
  
  具体说一下:
  Signature          0x4550    ;直接在右边输入PE就可以了
  文件头部分:
  Machine            0x14c    ;Intel 386
  NumberOfSections      0x2      ;2个区段
  TimeDateStamp        0x0
  PointerToSymbolTable      0x0
  NumberOfSymbols      0x0
  SizeOfOptionalHeader      0xe0    ;可选头大小,永远是0xe0
  Characteristics        0x102    ;具体看pe说明
  ----------------------------------------
  可选头部分:
  Magic            0x10b    ;32-位
  MajorLinkerVersion      0x0
  MinorLinkerVersion      0x0
  SizeOfCode          0x20    ;随便写 0x0~0xFF FF FF FF都可以
  SizeOfInitializedData      0xd0    ;随便写 0x0~0xFF FF FF FF都可以
  SizeOfUninitializedData    0x0
  AddressOfEntryPoint      0x1000    ;入口,这里就是代码段开头处的Rva
  BaseOfCode          0x1000    ;代码段开头处的Rva
  BaseOfData          0x2000    ;数据段开头处的Rva
  ImageBase          0x40***00  ;写代码是要注意把Base加上
  SectionAlignment        0x1000    ;区段对齐
  FileAlignment        0x200    ;文件对齐
  MajorOperatingSystemVersion  0x4      ;NT 4.0
  MinorOperatingSystemVersion   0x0
  MajorImageVersion      0x0
  MinorImageVersion      0x0
  MajorSubsystemVersion    0x4      ;Win32 4.0
  MinorSubsystemVersion    0x0
  Reserved1          0x0
  SizeOfImage          0x3000    ;请看下面的话
  SizeOfHeaders        0x200    ;文件中第一个区段的偏移量
  /*
  这里插段话,在原文的附录中,关于这两个字段是这
描述的。
  SizeOfImage                 c0 00 00 00 ; sum of all section sizes
  SizeOfHeaders               a0 01 00 00 ; offset to 1st section
  这个描述很不好,首先all section是包括Headers这个Section的
  但下面的1st section,显然指的是Headers后的第一个区段。
  这两个section含义不一致。
  我就被误导了,一开始这里填了0x2000,就没有通过。
  
  其实原文前面的解释比较清楚:
  ...它就是所有头和节的长度的总和。
  */
  CheckSum          0x0
  Subsystem          0x2      ;Win GUI
  DllCharacteristics        0x0
  SizeOfStackReserve      0x10***00  ;就这样写吧
  SizeOfStackCommit      0x1000    ;
  SizeOfHeapReserve      0x10***00  ;
  SizeOfHeapCommit      0x1000    ;
  LoaderFlags          0x0
  NumberOfRvaAndSizes    0x10    ;总是这个值,下面有多少个目录
  ----------------------------------------
  数据目录部分
  Directory 0
  Size              0x0
  VirtualAddress        0x0
  ----------------------------------------
  Directory 1
  Size              0xb0    ;随便写 0x0~0xFF FF FF FF都可以
  VirtualAddress        0x2020    ;两个字符串0x20总够了吧
  ----------------------------------------
  .                    ;还有14个,全是0
  .
  .
  ----------------------------------------
  区段头
  Section  0
  Name            .code    ;区段的名字
  VirtualAddress        0x1000    ;内存中的位置
  SizeOfRawData        0x20    ;随便写 0x1~0x10 00都可以
  PointerToRawData      0x200    ;文件中的位置
  PointerToRelocations      0x0
  PointerToLinenumbers      0x0
  NumberOfRelocations      0x0
  NumberOfLinenumbers    0x0
  Characteristics        0x60***02  ;具体看pe说明
  ----------------------------------------
  Section  1
  Name            .data    ;区段的名字
  VirtualAddress        0x2000    ;内存中的位置
  SizeOfRawData        0xc0    ;写大一点就可以了
  PointerToRawData      0x400    ;文件中的位置
  PointerT
ToRelocations      0x0
  PointerToLinenumbers      0x0
  NumberOfRelocations      0x0
  NumberOfLinenumbers    0x0
  Characteristics        0xc00***04  ;具体看pe说明
  ----------------------------------------
  
  关于“随便写”的地方,都是size。
  看来.code段无所谓size,别超过段大小就行。
  .data段就不行,不能少,但可以多,能多多少就要看文件的大小。
  SizeOfCode          0x20    ;随便写 0x0~0xFF FF FF FF都可以
  SizeOfInitializedData      0xd0    ;随便写 0x0~0xFF FF FF FF都可以
  Directory 1
  Size              0xb0    ;随便写 0x0~0xFF FF FF FF都可以
  这3个有点夸张,但可以正常运行。
  
  c)现在不妨跳过.code段,先搞.data段
  000****0400   50 45 D1 A7 CF B0 00 00  BF B4 B5 BD C4 E3 C1 CB   PE学习..看到你了
  000****0410   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  000****0420   5C 20 00 00 00 00 00 00  FF FF FF FF 7C 20 00 00   \ ......??| ..
  000****0430   64 20 00 00 6C 20 00 00  00 00 00 00 FF FF FF FF   d ..l ......??
  000****0440   8C 20 00 00 74 20 00 00  00 00 00 00 00 00 00 00   ?..t ..........
  000****0450   00 00 00 00 00 00 00 00  00 00 00 00 AC 20 00 00   ............?..
  000****0460   00 00 00 00 AC 20 00 00  00 00 00 00 BC 20 00 00   ....?......?..
  000****0470   00 00 00 00 BC 20 00 00  00 00 00 00 75 73 65 72   ....?......user
  000****0480   33 32 2E 64 6C 6C 00 00  00 00 00 00 6B 65 72 6E   32.dll......kern
  000****0490   65 6C 33 32 2E 64 6C 6C  00 00 00 00 00 00 00 00   el32.dll........
  00***04A0   00 00 00 00 00 00 00 00  00 00 00 00 01 00 4D 65   ..............Me
  00***04B0   73 73 61 67 65 42 6F 78  41 00 00 00 02 00 45 78   ssageBoxA.....Ex
  00***04C0   69 74 50 72 6F 63 65 73  73 00 00 00 00 00 00 00   itProcess.......
  00***04D0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  00***04E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  00***04F0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  
  上来两个字符串,注意最后加'\0'。对应的Rva是0x00 40 20 00和0x00 40 20 08
  从0x420开始是输入目录
  因为有2个DLL,所以先留3*0x14=0x3c的地方,先把4个FF写好,以后找起来方便些,一一。
  OriginalFirstThunk    0x205c
  TimeDateStamp      0x0      ;
  ForwarderChain      0xffffffff    ;不中转
  Name          0x207c
  user32.dll
  FirstThunk        0x2064
  ----------------------------------------
  OriginalFirstThunk    0x206c
  TimeDateSt
amp      0x0      ;
  ForwarderChain      0xffffffff    ;不中转
  Name          0x208c
  kernel32.dll
  FirstThunk        0x2074
  ----------------------------------------
  全0
  ----------------------------------------
  之后我放的是4组Thunk,注意每组最后一个元素都是0。
  这样上面两个OriginalFirstThunk和FirstThunk都可以定下来了。
  之后是两个DLL的名字,于是Name可以定下来了。
  然后是两个IMAGE_IMPORT_BY_NAME
  WORD Hint      ;随便写一个
  BYTE Name[1]    ;函数名
  MessageBoxA是user32.dll的。
  ExitProcess是kernel32.dll的。
  这样上面的Thunk可以定了。
  
  好,数据段的大小也定了。回到上面去改一下。
  
  d)最后写代码。
  000****0200   6A 00 68 00 20 40 00 68  08 20 40 00 6A 00 2E FF   j.h. @.h. @.j..?
  000****0210   15 64 20 40 00 6A 00 2E  FF 15 74 20 40 00 00 00   .d @.j..?t @...
  000****0220   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  000****0230   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
  
  004****1000 >/$  6A 00        push    0                  ; /Style = MB_OK|MB_APPLMODAL
  004****1002  |.  68 002****4000      push    004****2000              ; |Title = "PE学?,B0,""
  004****1007  |.  68 082****4000      push    004****2008              ; |Text = "?,B4,"?,BD,"你了"
  00***00C  |.  6A 00        push    0                  ; |hOwner = NULL
  00***00E  |.  2E:FF15 642****4000>  call    dword ptr cs:[<&user32.MessageBoxA>  ; \MessageBoxA
  004****1015  |.  6A 00        push    0
  004****1017  \.  2E:FF15 742****4000>  call    dword ptr cs:[<&kernel32.ExitProcess>]  ;  kernel32.ExitProcess
  
  代码很简单,00 20 40 00也就是 00 40 20 00,是第一个字符串的地址。
  要注意“高高低低”的规则。
  64 20 40 00 是00 40 20 64是user32.dll的FirstThunk,指向的就是MessageBoxA
  
  其他:
  学习pe的时候,用c写了段读pe结构的代码,对于学习还是很有帮助的。
  winnt.h包含了pe结构的说明,但一开始include之后一排错。
  原来还要include <window.h>。
  
  终于结束了,打完收工...
您需要登录后才可以回帖 登录 | 开放注册

本版积分规则

帮助|Archiver|小黑屋|通信管理局专项备案号:[2008]238号|NB5社区 ( 皖ICP备08004151号;皖公网安备34010402700514号 )

GMT+8, 2025-4-27 14:51 , Processed in 0.167569 second(s), 33 queries .

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表