前两天想搞出一个游戏的封包解密函数,又懒怕还原成高级语言 想直接调用他EXE内部的解密函数,
本来记得PEDIY的有篇文章是增加节来导出内部函数的,但是找不到那篇文章了;所以就开始自己尝试一下,现在以自己写的一个EXE为类型

先写一个EXE程序

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
procedure DoPub;//内部函数  等待导出
begin
MessageBox(0, 'Test', 'Test', MB_OK);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  MessageBox(0, 'Test', 'Test', MB_OK);
end;

end.

上面编译成成 hook32.exe 名字为Hook没什么意思 只是个人习惯 跟钩子没关联 呵呵

然后用OD载入我们的程序 下段BP MessageBoxA 按 按钮运行 断下 执行到返回
00456498 >/$  6A 00         PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
0045649A  |.  68 AC644500   PUSH hook32.004564AC                     ; |Title = "Test"
0045649F  |.  68 AC644500   PUSH hook32.004564AC                     ; |Text = "Test"
004564A4  |.  6A 00         PUSH 0                                   ; |hOwner = NULL
004564A6  |.  E8 A509FBFF   CALL <JMP.&user32.MessageBoxA>           ; \MessageBoxA
004564AB  \.  C3            RETN

得知我们的内部函数地址为 00456498  这里我就偷懒啦  用LORDPE的文件计算器 
计算 RVA=00056498
       Offset=00055898
好了现在来修改导出表,再次偷懒 不加新节啦,WinHex打开文件 直接找个程序空白的地方
我在偏移64B90找到大片0字节区域 好就在这里开刀啦
这里我们先来熟悉一下导出表结构
  Characteristics      4字节
   TimeDateStamp    4字节
   MajorVersion        2字节
   MinorVersion        2字节
   Name                  4字节
   Base                    4字节 
   NumberOfFunctions 4字节 你要导出函数的个数
   NumberOfNames     4字节 你要导出函数名称的个数
   AddressOfFunctions 4字节 //函数地址
   AddressOfNames     4字节 //函数名地址
   AddressOfNameOrdinals 4字节 //函数名顺序地址
注意上面都是保存RVA的值哦 千万别搞错了
好了 让我们来根据上面结构来在偏移64B90 处构造导出表
我们的模块名字是 hook32.exe 到出名字就随便起个吧  我这里叫 _W
构建如下

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00064B90   00 00 00 00 00 00 00 00  00 00 00 00 C2 E7 06 00   ............络..
00064BA0   01 00 00 00 01 00 00 00  01 00 00 00 B8 E7 06 00   ............哥..
00064BB0   BC E7 06 00 C0 E7 06 00  98 64 05 00 CD E7 06 00   肩..犁....顽..
00064BC0   00 00 68 6F 6F 6B 33 32  2E 65 78 65 00 5F 57 00   ..hook32.exe._W.

也就是
->Export Table
   Characteristics:        0x00000000
   TimeDateStamp:          0x00000000  (GMT: Thu Jan 01 00:00:00 1970)
   MajorVersion:           0x0000
   MinorVersion:           0x0000  -> 0.00
   Name:                   0x0006E7C2  ("hook32.exe")
   Base:                   0x00000001
   NumberOfFunctions:      0x00000001
   NumberOfNames:          0x00000001
   AddressOfFunctions:     0x0006E7B8
   AddressOfNames:         0x0006E7BC
   AddressOfNameOrdinals:  0x0006E7C0

   Ordinal RVA        Symbol Name
   ------- ---------- ----------------------------------
   0x0001  0x00056498 "_W"

  • 标 题:答复
  • 作 者:bujin888
  • 时 间:2008-06-22 02:52:16

导出表做好后 就让我们把导出表更新到PE结构里面去吧
用OD载入程序 ALT+M打开内存 选择 hook32 PE文件头那行 进入PE文件头数据窗口
看到
0040003C    00010000    DD 00000100          ; Offset to PE signature
这句话的意思说 PE文件头在偏移100处
我们来到偏移100处
00400100    50 45 00 00>ASCII "PE"           ; PE signature (PE)
00400104    4C01        DW 014C              ; Machine = IMAGE_FILE_MACHINE_I386
00400106    0900        DW 0009              ;  NumberOfSections = 9
00400108    4E255D48    DD 485D254E          ;  TimeDateStamp = 485D254E
0040010C    00000000    DD 00000000          ;  PointerToSymbolTable = 0
00400110    00000000    DD 00000000          ;  NumberOfSymbols = 0
00400114    E000        DW 00E0              ;  SizeOfOptionalHeader = E0 (224.)
00400116    8E81        DW 818E              ;  Characteristics = EXECUTABLE_IMAGE|32BIT_MACHINE|LINE_NUMS_STRIPPED|LOCAL_SYMS_STRIPPED|BYTES_REVERSED_LO|BYTES_REVERSED_HI
00400118    0B01        DW 010B              ; MagicNumber = PE32
0040011A    02          DB 02                ;  MajorLinkerVersion = 2
0040011B    19          DB 19                ;  MinorLinkerVersion = 19 (25.)
0040011C    00600500    DD 00056000          ;  SizeOfCode = 56000 (352256.)
00400120    00E80000    DD 0000E800          ;  SizeOfInitializedData = E800 (59392.)
00400124    00000000    DD 00000000          ;  SizeOfUninitializedData = 0
00400128    1C770500    DD 0005771C          ;  AddressOfEntryPoint = 5771C
0040012C    00100000    DD 00001000          ;  BaseOfCode = 1000
00400130    00800500    DD 00058000          ;  BaseOfData = 58000
00400134    00004000    DD 00400000          ; ImageBase = 400000
00400138    00100000    DD 00001000          ;  SectionAlignment = 1000
0040013C    00020000    DD 00000200          ;  FileAlignment = 200
00400140    0400        DW 0004              ;  MajorOSVersion = 4
00400142    0000        DW 0000              ;  MinorOSVersion = 0
00400144    0000        DW 0000              ;  MajorImageVersion = 0
00400146    0000        DW 0000              ;  MinorImageVersion = 0
00400148    0400        DW 0004              ;  MajorSubsystemVersion = 4
0040014A    0000        DW 0000              ;  MinorSubsystemVersion = 0
0040014C    00000000    DD 00000000          ;  Reserved
00400150    00F00600    DD 0006F000          ;  SizeOfImage = 6F000 (454656.)
00400154    00040000    DD 00000400          ;  SizeOfHeaders = 400 (1024.)
00400158    00000000    DD 00000000          ;  CheckSum = 0
0040015C    0200        DW 0002              ;  Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI
0040015E    0000        DW 0000              ;  DLLCharacteristics = 0
00400160    00001000    DD 00100000          ;  SizeOfStackReserve = 100000 (1048576.)
00400164    00400000    DD 00004000          ;  SizeOfStackCommit = 4000 (16384.)
00400168    00001000    DD 00100000          ;  SizeOfHeapReserve = 100000 (1048576.)
0040016C    00100000    DD 00001000          ;  SizeOfHeapCommit = 1000 (4096.)
00400170    00000000    DD 00000000          ;  LoaderFlags = 0
00400174    10000000    DD 00000010          ;  NumberOfRvaAndSizes = 10 (16.)
00400178    00000000    DD 00000000          ;  Export Table address = 0
0040017C    00000000    DD 00000000          ;  Export Table size = 0
00400180    00F00500    DD 0005F000          ;  Import Table address = 5F000
00400184    94240000    DD 00002494          ;  Import Table size = 2494 (9364.)
00400188    00A00600    DD 0006A000          ;  Resource Table address = 6A000
0040018C    00480000    DD 00004800          ;  Resource Table size = 4800 (18432.)
00400190    00000000    DD 00000000          ;  Exception Table address = 0
00400194    00000000    DD 00000000          ;  Exception Table size = 0
00400198    00000000    DD 00000000          ;  Certificate File pointer = 0
0040019C    00000000    DD 00000000          ;  Certificate Table size = 0
004001A0    00400600    DD 00064000          ;  Relocation Table address = 64000
004001A4    345B0000    DD 00005B34          ;  Relocation Table size = 5B34 (23348.)
004001A8    00000000    DD 00000000          ;  Debug Data address = 0
004001AC    00000000    DD 00000000          ;  Debug Data size = 0
004001B0    00000000    DD 00000000          ;  Architecture Data address = 0
004001B4    00000000    DD 00000000          ;  Architecture Data size = 0

文件头结构有很多 我们只要 关心 这两行就可以
00400178    00000000    DD 00000000          ;  Export Table address = 0
0040017C    00000000    DD 00000000          ;  Export Table size = 0
这里是保存 导出表的地址和大小的地方
用OD相对偏移计算如下
$+178       00000000    DD 00000000          ;  Export Table address = 0
$+17C       00000000    DD 00000000          ;  Export Table size = 0
也就是说 导出表地址和长度保存在文件偏移 178 和17C处
用winhex打开文件 ALT+G 来到偏移178处  将原来我们构造导出表的地址00064B90 转换为RVA为6E790 

偏移178处4个字节改成 90 E7 06 00 数了一下我们的导出表共有64个字节16进制为0x40 
与是偏移178处4个字节改成40 00 00 00 到这里就一切OK了 保存好修改好的PE结构 然后我们写个程序来调用一下这个到处函数看看

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

  doSth:procedure;
  pfun:DWORD;
implementation

{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
 DLLModule:DWORD;
begin
  DLLModule := LoadLibrary('hook32.exe');
  doSth:= GetProcAddress(DLLModule,'_W'); //取得API地址
  doSth;
end;

end.

试验结果  HOOK32.EXE的内部函数被成功调用了!