破解之PE文件译文(八)

社区服务
高级搜索
猴岛论坛电脑百科破解之PE文件译文(八)
发帖 回复
倒序阅读 最近浏览的帖子最近浏览的版块
4个回复

[网络问题]破解之PE文件译文(八)

楼层直达
不懂也不知道

ZxID:10586231

等级: 禁止发言

举报 只看楼主 使用道具 楼主   发表于: 2010-07-10 0
破解之PE文件译文(八)
在这个附录中我将给大家展示一下怎样手工建立一个程序。因为我不会DEC Alpha,本例将使用Intel汇编语言。

本程序相当于

  #include <stdio.h>
  int main(void)
  {
    puts(hello,world);
    return 0;
  }

首先,我使用Win32函数来翻译它以取代C运行时库:

  #define STD_OUTPUT_HANDLE -11UL
  #define hello "hello, world\n"

  __declspec(dllimport) unsigned long __stdcall
  GetStdHandle(unsigned long hdl);

  __declspec(dllimport) unsigned long __stdcall
  WriteConsoleA(unsigned long hConsoleOutput,
            const void *buffer,
            unsigned long chrs,
            unsigned long *written,
            unsigned long unused
            );

  static unsigned long written;

  void startup(void)
  {
    WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE),hello,sizeof(hello)-1,&written,0);
    return;
  }

现在我将笨拙的将它汇编出来:
  startup:
          ; parameters for WriteConsole(), backwards
  6A 00              push    0x00000000
  68 ?? ?? ?? ??        push    offset _written
  6A 0D              push    0x0000000d
  68 ?? ?? ?? ??        push    offset hello
          ; parameter for GetStdHandle()
  6A F5              push    0xfffffff5
  2E FF 15 ?? ?? ?? ??    call    dword ptr cs:__imp__GetStdHandle@4
          ; result is last parameter for WriteConsole()
  50                push    eax
  2E FF 15 ?? ?? ?? ??    call    dword ptr cs:__imp__WriteConsoleA@20
  C3                ret   

  hello:
  68 65 6C 6C 6F 2C 20 77 6F 72 6C 64 0A  "hello, world\n"
  _written:
  00 00 00 00

以上就是编译的部分。任何人都能做到这点。从现在起让我们扮演起链接器的角色,这会非常有趣 :-)

我需要先找出函数WriteConsoleA()和GetStdHandle()。碰巧它们都在“kernel32.dll”中。(这是“输入库”部分。)

现在我开始做可执行文件。问号代表待定的值;它们将在以后被修正。

首先是DOS-根,开始于0x0,有0x40字节长:
  00 | 4d 5a 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  10 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  30 | 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00
正如你所见到的,这不是真正的MS-DOS程序。它只是一个开始部分有“MZ”签名的头和紧跟在头后面的e_lfanew指针,没有任何代码。这是因为它并非打算运行于MS-DOS之上;它之所以在这里只是因为规范的需要。

然后是PE签名,开始于0x40,有0x4字节长:
    50 45 00 00

现在到了文件头,开始于0x44,有0x14字节长:
  Machine              4c 01    ; i386
  NumberOfSections        02 00    ; 代码段和数据段
  TimeDateStamp          00 00 00 00 ; 谁管它?
  PointerToSymbolTable    00 00 00 00 ; 未用
  NumberOfSymbols        00 00 00 00 ; 未用
  SizeOfOptionalHeader    e0 00    ; 常量
  Characteristics        02 01    ; 32位机器上的可执行文件

接着是可选头,开始于0x58,有0x60字节长:
  Magic              0b 01    ; 常量
  MajorLinkerVersion      00      ; 我是 0.0 版:-)
  MinorLinkerVersion      00      ;
  SizeOfCode            20 00 00 00 ; 32字节代码
  SizeOfInitializedData    ?? ?? ?? ?? ; 待找出
  SizeOfUninitializedData  00 00 00 00 ; 我们没有BSS节
  AddressOfEntryPoint      ?? ?? ?? ?? ; 待定
  BaseOfCode            ?? ?? ?? ?? ; 待定
  BaseOfData            ?? ?? ?? ?? ; 待定
  ImageBase            00 00 10 00 ; 1 MB, 随意选
  SectionAlignment        20 00 00 00 ; 32字节对齐
  FileAlignment          20 00 00 00 ; 32字节对齐
  MajorOperatingSystemVersion 04 00    ; NT 4.0



MinorOperatingSystemVersion 00 00    ;
  MajorImageVersion      00 00    ;0.0版
  MinorImageVersion      00 00    ;
  MajorSubsystemVersion    04 00    ; Win32 4.0
  MinorSubsystemVersion    00 00    ;
  Win32VersionValue      00 00 00 00 ; 未使用?
  SizeOfImage          ?? ?? ?? ?? ; 待定
  SizeOfHeaders          ?? ?? ?? ?? ; 待定
  CheckSum            00 00 00 00 ; 非驱动不用
  Subsystem            03 00    ; Win32控制台
  DllCharacteristics      00 00    ; 未用 (不是一个DLL)
  SizeOfStackReserve      00 00 10 00 ; 1 MB栈
  SizeOfStackCommit      00 10 00 00 ; 开始时4 KB
  SizeOfHeapReserve      00 00 10 00 ; 1 MB堆
  SizeOfHeapCommit        00 10 00 00 ; 开始时4 KB
  LoaderFlags          00 00 00 00 ; 未知
  NumberOfRvaAndSizes      10 00 00 00 ; 常量

正如你所见,我计划只用2个节,一个用于代码,一个用于所有剩余的东西(数据、常量和输入目录等)。没有重定位和象资源之类其它东西。我也不用BSS节并将变量“written”放入已初始化数据。文件和RAM中的节对齐都是一样的(32字节);这将有助于使任务简单,否则我就得来回地计算RVA很多次。

现在我们设置数据目录,开始于0xb8字节,有 0x80字节长:
    地址      大小
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_EXPORT (0)
  ?? ?? ?? ??  ?? ?? ?? ??      ; IMAGE_DIRECTORY_ENTRY_IMPORT (1)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_RESOURCE (2)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_SECURITY (4)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_BASERELOC (5)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_DEBUG (6)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_TLS (9)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_IAT (12)
  00 00 00 00  00 00 00 00      ; 13
  00 00 00 00  00 00 00 00      ; 14
  00 00 00 00  00 00 00 00      ; 15
仅使用输入目录。

下一个使节头。首先我们做代码节的,代码节将包含前面所编的汇编语句。它有32字节长,所以代码节也就是这么长。节头从0x138处开始,有0x28字节长:

  Name        2e 63 6f 64 65 00 00 00  ; ".code"的ASCII码值
  VirtualSize      00 00 00 00        ; 未用
  VirtualAddress    ?? ?? ?? ??        ; 待定
  SizeOfRawData    20 00 00 00        ; 代码的大小
  PointerToRawData  ?? ?? ?? ??        ; 待定
  PointerToRelocations 00 00 00 00        ; 未用
  PointerToLinenumbers 00 00 00 00        ; 未用
  NumberOfRelocations 00 00            ; 未用
  NumberOfLinenumbers 00 00            ; 未用
  Characteristics  20 00 00 60        ; 代码节,可执行,可读

第二节将包含数据。节头开始于0x160处,有0x28字节长:

  Name        2e 64 61 74 61 00 00 00  ; ".data"的ASCII码值
  VirtualSize      00 00 00 00        ; 未用
  VirtualAddress    ?? ?? ?? ??        ; 待定
  SizeOfRawData    ?? ?? ?? ??        ; 待定
  PointerToRawData  ?? ?? ?? ??        ; 待定
  PointerToRelocations 00 00 00 00        ; 未用
  PointerToLinenumbers 00 00 00 00        ; 未用
  NumberOfRelocations 00 00            ; 未用
  NumberOfLinenumbers 00 00            ; 未用
  Characteristics  40 00 00 c0        ; 已初始化的,可读,可写

下一个字节位于0x188处,但节需要按32字节(的倍数)对齐(因为我是这样选择的),所以我们需要添一些(0)字节直到0x1a0处:

  00 00 00 00 00 00    ; 填充的
  00 00 00 00 00 00
  00 00 00 00 00 00
  00 00 00 00 00 00

现在第一节,就是上面所汇编的代码节,“到”了。它开始于0x1a0处,有0x20字节长:
  6A 00            ; push    0x00000000
  68 ?? ?? ?? ??      ; push    offset _written
  6A 0D            ; push    0x0000000d
  68 ?? ?? ?? ??      ; push    offset hello_string
  6A F5            ; push    0xfffffff5
  2E FF 15 ?? ?? ?? ??  ; call    dword ptr cs:__imp__GetStdHandle@4
  50              ; push    eax
  2E FF 15 ?? ?? ?? ??  ; call    dword ptr cs:__imp__WriteConsoleA@20
  C3              ; ret   

因为这一节的长度(刚好32字节),在下一节(数据节)前我们不需要填充任何字节。下一节到了,从0x1c0处开始:

  68 65 6C 6C 6F 2C 20 77 6F 72 6C 64 0A ; "hello, world\n"的ASCII码值
  00 00 00                    ; 填充几个0以和_written对齐
  00 00 00 00                  ; _written

现在剩下的只有输入目录了。本文件将从"kernel32.dll"库中输入2个函数,输入目录将从本节的变量后面立即开始。首先我们先将上面的数据按32字节对齐:

  00 00 00 00 00 00 00 00 00 00 00 00  ; 填充的

在0x1e0处开始输入描述(IMAGE_IMPORT_DESCRIPTOR):
  OriginalFirstThunk    ?? ?? ?? ??  ; 待定
  TimeDateStamp      00 00 00 00  ; 未绑定
  ForwarderChain      ff ff ff ff  ; 无中转
  Name            ?? ?? ?? ??  ; 待定
  FirstThunk        ?? ?? ?? ??  ; 待定

我们需要用一个0字节项来结束输入目录(我们现在位于0x1f4):
  OriginalFirstThunk    00 00 00 00  ; 结束符号
  TimeDateStamp      00 00 00 00  ;
  ForwarderChain      00 00 00 00  ;
  Name            00 00 00 00  ;
  FirstThunk        00 00 00 00  ;

现在只剩下DLL名字,还有2个换长,以及换长数据和函数名字了。但现在我们真的很快就要完成了。

DLL名字,以0结尾,开始于0x208处:
  6b 65 72 6e 65 6c 33 32 2e 64 6c 6c 00 ; "kernel32.dll"的ASCII码值
  00 00 00                    ; 填充到32位边界

原始第一个换长,开始于0x218处:
  AddressOfData  ?? ?? ?? ??        ; "WriteConsoleA"函数名的RVA
  AddressOfData  ?? ?? ?? ??        ; "GetStdHandle"函数名的RVA
            00 00 00 00        ; 结束符号

第一个换长就是同样的列表,开始于0x224处:
(__imp__WriteConsoleA@20, at 0x224)
  AddressOfData  ?? ?? ?? ??        ; "WriteConsoleA"函数名的RVA
(__imp__GetStdHandle@4, at 0x228)
  AddressOfData  ?? ?? ?? ??        ; "GetStdHandle"函数名的RVA
            00 00 00 00        ; 结束符号

现在剩下的只有输入名字(IMAGE_IMPORT_BY_NAME)形式的两个函数名了。我们现处于0x230字节。
  01 00                        ; 序数,不需要正确
  57 72 69 74 65 43 6f 6e 73 6f 6c 65 41 00 ; "WriteConsoleA"的ASCII码值
  02 00                        ; 序数,不需要正确
  47 65 74 53 74 64 48 61 6e 64 6c 65 00  ; "GetStdHandle"的ASCII码值

Ok, 这就全部结束了。下一个字节,我们并不真正需要,是0x24f。我们必须将节填充到0x260处:
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; 填充的
  00

------------

我们已经完成了。因为我们已经知道了所有的字节偏移量,我们可以应用我们的修正到所有原先被用“??”符号标为“未知”的地址和大小了。
我将不强迫你一步一步地去读它(很好懂的),只直接给出结果来:

------------

DOS-头, 开始于0x0:
  00 | 4d 5a 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  10 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  30 | 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00

签名, 开始于0x40:
    50 45 00 00

文件头, 开始于0x44:
  Machine              4c 01    ; i386
  NumberOfSections        02 00    ; 代码和数据
  TimeDateStamp          00 00 00 00 ; 谁管它?
  PointerToSymbolTable    00 00 00 00 ; 未用
  NumberOfSymbols        00 00 00 00 ; 未用
  SizeOfOptionalHeader    e0 00    ; 常量
  Characteristics        02 01    ; 可执行于32位机器上

可选头, 开始于0x58:
  Magic              0b 01    ; 常量
  MajorLinkerVersion      00      ; 我是 0.0版 :-)
  MinorLinkerVersion      00      ;
  SizeOfCode            20 00 00 00 ; 32字节代码
  SizeOfInitializedData    a0 00 00 00 ; 数据节大小
  SizeOfUninitializedData  00 00 00 00 ; 我们没有 BSS节
  AddressOfEntryPoint      a0 01 00 00 ; 代码节的开始处
  BaseOfCode            a0 01 00 00 ; 代码节的RVA
  BaseOfData            c0 01 00 00 ; 数据节的RVA
  ImageBase            00 00 10 00 ; 1 MB, 任意选择
  SectionAlignment        20 00 00 00 ; 32字节对齐
  FileAlignment          20 00 00 00 ; 32字节对齐
  MajorOperatingSystemVersion 04 00    ; NT 4.0
  MinorOperatingSystemVersion 00 00    ;
  MajorImageVersion      00 00    ; 0.0版本
  MinorImageVersion      00 00    ;
  MajorSubsystemVersion    04 00    ; Win32 4.0
  MinorSubsystemVersion    00 00    ;
  Win32VersionValue      00 00 00 00 ; 未用?
  SizeOfImage          c0 00 00 00 ; 所有节大小的总数
  SizeOfHeaders          a0 01 00 00 ; 第一节的偏移量
  CheckSum            00 00 00 00 ; 非驱动程序不须用
  Subsystem            03 00    ; Win32控制台程序
  DllCharacteristics      00 00    ; 未用(不是一个DLL)
  SizeOfStackReserve      00 00 10 00 ; 1 MB 栈
  SizeOfStackCommit      00 10 00 00 ; 开始时4 KB
  SizeOfHeapReserve      00 00 10 00 ; 1 MB 堆
  SizeOfHeapCommit        00 10 00 00 ; 开始时4 KB
  LoaderFlags          00 00 00 00 ; 未知
  NumberOfRvaAndSizes      10 00 00 00 ; 常量

数据目录,开始于 0xb8:
    地址        大小
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_EXPORT (0)
  e0 01 00 00  6f 00 00 00      ; IMAGE_DIRECTORY_ENTRY_IMPORT (1)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_RESOURCE (2)
  00 00 00 00  00 00 00 00      ; IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)
  00 00
本帖de评分: 1 条评分 DB +6
DB+6

感谢分享

┌.淫穆”

ZxID:10737016

等级: 上将
..

举报 只看该作者 沙发   发表于: 2010-07-10 0
不错!
BY小鬼

ZxID:9091214

等级: 少将
我是一只好猴子咿呀呦~~BY小鬼

举报 只看该作者 板凳   发表于: 2010-07-10 0
要学这个  需要从基本开始学!
溪空鹿隐林

ZxID:12131846

等级: 元老

举报 只看该作者 地板   发表于: 2010-07-10 0
不错
  阿兽,

ZxID:10483277

等级: 元帅
V型:994896297

举报 只看该作者 4楼  发表于: 2010-07-10 0
我不懂呀。一哄而散
« 返回列表
发帖 回复