使用汇编语言和 Delphi 映射文件获取函数的地址
Get the address of a function using assembly language and Delphi map file
我试图在 "Detailed" 映射文件中查找单元名称和函数名称,该映射文件是通过在 Delphi 中构建项目生成的 5. 我找到了一些代码网上声称可以这样做,但我做不到。
代码要求:
- Delphi 5
- 一个 TForm
- 一个 TButton
- 一个 TEdit
- 一个 TLabel
目标:
函数:'Log' 应该是 return 调用过程的地址。一旦确定了地址,就可以在映射文件中查找单元和函数名称以及行号。
用途:
岂不是很好......如果一个函数的名称可以通过从程序中的任何地方调用 'Log' 来获得。
现实:
我真的很想了解 'Log' 函数中发生的事情,以及它为什么或为什么不工作,其次是 returning 单元和函数名称的另一种方式作为调用过程的行号。
问题:
我从 TForm1.Button1Click > TForm1.Log > TForm1.LogAddress > TForm1.ShowInfo 获得的地址与我正在寻找的地图文件中的相应项目不一致,即 M=Unit1, TForm1.Button1Click等...
网站:
http://www.haydenr.com/delphi/articles/article002.htm , http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22596248.html 最后一个网站可能无法从 link 访问 -- 我将 Google 搜索字符串放在 'Log' 函数中。
Delphi代码:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
Procedure Log;
Procedure ShowInfo(hexAddress : Integer);
Procedure LogAddress(ptr: pointer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Implementation
{$R *.DFM}
Procedure TForm1.Log;
// Google Search: capturing a procedure or function's name for logging purposes
// http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22596248.html
Begin
ASM
pop EAX
push EAX
Call LogAddress
End;//ASM
End;
Procedure TForm1.ShowInfo(hexAddress: Integer);
// http://www.haydenr.com/delphi/articles/article002.htm
Var
iMapFileAddress : Integer;
sMapFileAddress : String;
ImageBase : Integer;
SubOffset : Integer;
Offset : Integer;
Begin
ImageBase := [=11=]400000; // Project > Options... > Linker Tab > Memory sizes group box > Image Base
SubOffset := 00;
Offset := ImageBase + SubOffset;
iMapFileAddress := hexAddress - Offset;
sMapFileAddress := IntToHex(iMapFileAddress,8);
Edit1.Text := sMapFileAddress; //This is the value I get: sMapFileAddress = 00542214
{
Here are some excerpts from: Project1.map
|Detailed map of segments
| 0001:00040498 000002D4 C=CODE S=.text G=(none) M=Unit1 ACBP=A9
| 0001:0004076C 000001A5 C=CODE S=.text G=(none) M=Project1 ACBP=A9
| :
| :
| :
| Address Publics by Name
|
| 0001:0004071C TForm1.Button1Click
| 0001:00040668 TForm1.Log
| 0001:00040700 TForm1.LogAddress
| 0001:0004067C TForm1.ShowInfo
}
End;
procedure TForm1.LogAddress(ptr: pointer);
begin
ShowInfo(Integer(ptr));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Log;
end;
end.
您没有正确调用 LogAddress
。作为TForm1
的方法,它实际上是一个双参数函数。这两个参数依次是 Self
和 ptr
,这意味着寄存器 EAX 是 Self
而 EDX 是 ptr
。但是,您在 EAX 中传递了地址参数,而您对 EDX 没有做任何事情。这意味着您正在查找谁知道什么的名称,这可能不是您程序中任何代码段的地址。最重要的是,Self
值不再有效,因此对成员变量(例如 Edit1
)的任何引用都将不起作用;你会遇到访问冲突或某种形式的内存损坏。
将 Log
方法更改为:
procedure TForm1.Log;
asm
// fetch return address from top of stack
mov edx, [esp+4]
call LogAddress
end;
请记住,您在 ptr
参数中传递的地址是 Log
调用的 return 地址 。实际上,这通常是 在 之后用于进入 Log
方法的 CALL
指令的地址。因此,您不应期望在映射文件的函数列表中找到该确切地址。相反,您需要查找地址最接近但不超过您拥有的地址的函数。此外,如果您将该地址转换为行号,则它可能是实际调用站点之后的行。
the JCL 中的 JclDebug 单元已经具有可以为您执行此操作的函数。您可以使用 ProcByLevel
函数获取呼叫者的姓名,并使用 LiveByLevel
获取行号。
根据 Rob Kennedy 的建议进行代码替换并调整“esp 偏移量”后,工作代码如下...
为了制作“地图文件”;在 Delphi IDE 中执行以下步骤:
项目 > 选项... > 编译器选项卡 > 代码生成窗格:
优化 = FALSE
项目选项 Window > 链接器选项卡 > 映射文件窗格:Select“详细”
项目 > 构建所有项目
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1 : TButton;
Label1 : TLabel;
Edit1 : TEdit;
procedure Button1Click(Sender: TObject);
Procedure Log;
Procedure ShowInfo(hexAddress : Integer);
Procedure LogAddress(ptr: pointer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Implementation
{$R *.DFM}
Procedure TForm1.Log;
// Google Search: capturing a procedure or function's name for logging purposes
// http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22596248.html
Begin
ASM
// fetch return address from top of stack
mov edx,[esp+8]
call LogAddress
End;//ASM
End;
Procedure TForm1.ShowInfo(hexAddress: Integer);
// http://www.haydenr.com/delphi/articles/article002.htm
Var
iMapFileAddress : Integer;
sMapFileAddress : String;
ImageBase : Integer;
SubOffset : Integer;
Offset : Integer;
Begin
ImageBase := [=10=]400000; // Project > Options... > Linker Tab > Memory sizes group box > Image Base
SubOffset := 00;
Offset := ImageBase + SubOffset;
iMapFileAddress := hexAddress - Offset;
sMapFileAddress := IntToHex(iMapFileAddress,8);
Edit1.Text := sMapFileAddress; //This is the value I get: sMapFileAddress = 00040730
{
|Here are some excerpts from: Project1.map
|*******************************************************************************************************************
|Detailed map of segments
| 0001:00040498 000002D4 C=CODE S=.text G=(none) M=Unit1 ACBP=A9
| 0001:0004076C 000001A5 C=CODE S=.text G=(none) M=Project1 ACBP=A9
| :
| :
| V
| Address Publics by Name
|
| 0001:0004071C TForm1.Button1Click
| 0001:00040668 TForm1.Log
| 0001:00040700 TForm1.LogAddress
| 0001:0004067C TForm1.ShowInfo
| :
| :
| V
|Line numbers for Unit1(Unit1.pas) segment .text
|
| 82 0001:00040728 83 0001:00040730 85 0001:00040764 85 0001:0004076B
|*******************************************************************************************************************
|
| Returned Hex (H) = 00040730
| Returned Integer (I) = 263984
|-------------------------------------------------------------------------------------------------------------------
|Address Of Min Hex Max Hex Min Integer Max Integer | Evaluation
|------------------------------------------------------------------------------|------------------------------------
|Unit1 00040498 000406E5 263320 263909 | Hmin < H < Hmax AND Imin < I < Imax
|Function/Procedure 0004071C 263964 | H < Hmax AND I < Imax
|Line Number 00040730 263984 | H = Hmax AND I = Imax
|-------------------------------------------------------------------------------------------------------------------
|
|NOTE: The actual line number where 'Log' is called is: Returned Line Number - 1 (83 - 1 = 82)
}
End;
procedure TForm1.LogAddress(ptr: pointer);
begin
ShowInfo(Integer(ptr));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Log;
end;
end.
我试图在 "Detailed" 映射文件中查找单元名称和函数名称,该映射文件是通过在 Delphi 中构建项目生成的 5. 我找到了一些代码网上声称可以这样做,但我做不到。
代码要求:
- Delphi 5
- 一个 TForm
- 一个 TButton
- 一个 TEdit
- 一个 TLabel
目标: 函数:'Log' 应该是 return 调用过程的地址。一旦确定了地址,就可以在映射文件中查找单元和函数名称以及行号。
用途: 岂不是很好......如果一个函数的名称可以通过从程序中的任何地方调用 'Log' 来获得。
现实: 我真的很想了解 'Log' 函数中发生的事情,以及它为什么或为什么不工作,其次是 returning 单元和函数名称的另一种方式作为调用过程的行号。
问题: 我从 TForm1.Button1Click > TForm1.Log > TForm1.LogAddress > TForm1.ShowInfo 获得的地址与我正在寻找的地图文件中的相应项目不一致,即 M=Unit1, TForm1.Button1Click等...
网站: http://www.haydenr.com/delphi/articles/article002.htm , http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22596248.html 最后一个网站可能无法从 link 访问 -- 我将 Google 搜索字符串放在 'Log' 函数中。
Delphi代码:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
Procedure Log;
Procedure ShowInfo(hexAddress : Integer);
Procedure LogAddress(ptr: pointer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Implementation
{$R *.DFM}
Procedure TForm1.Log;
// Google Search: capturing a procedure or function's name for logging purposes
// http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22596248.html
Begin
ASM
pop EAX
push EAX
Call LogAddress
End;//ASM
End;
Procedure TForm1.ShowInfo(hexAddress: Integer);
// http://www.haydenr.com/delphi/articles/article002.htm
Var
iMapFileAddress : Integer;
sMapFileAddress : String;
ImageBase : Integer;
SubOffset : Integer;
Offset : Integer;
Begin
ImageBase := [=11=]400000; // Project > Options... > Linker Tab > Memory sizes group box > Image Base
SubOffset := 00;
Offset := ImageBase + SubOffset;
iMapFileAddress := hexAddress - Offset;
sMapFileAddress := IntToHex(iMapFileAddress,8);
Edit1.Text := sMapFileAddress; //This is the value I get: sMapFileAddress = 00542214
{
Here are some excerpts from: Project1.map
|Detailed map of segments
| 0001:00040498 000002D4 C=CODE S=.text G=(none) M=Unit1 ACBP=A9
| 0001:0004076C 000001A5 C=CODE S=.text G=(none) M=Project1 ACBP=A9
| :
| :
| :
| Address Publics by Name
|
| 0001:0004071C TForm1.Button1Click
| 0001:00040668 TForm1.Log
| 0001:00040700 TForm1.LogAddress
| 0001:0004067C TForm1.ShowInfo
}
End;
procedure TForm1.LogAddress(ptr: pointer);
begin
ShowInfo(Integer(ptr));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Log;
end;
end.
您没有正确调用 LogAddress
。作为TForm1
的方法,它实际上是一个双参数函数。这两个参数依次是 Self
和 ptr
,这意味着寄存器 EAX 是 Self
而 EDX 是 ptr
。但是,您在 EAX 中传递了地址参数,而您对 EDX 没有做任何事情。这意味着您正在查找谁知道什么的名称,这可能不是您程序中任何代码段的地址。最重要的是,Self
值不再有效,因此对成员变量(例如 Edit1
)的任何引用都将不起作用;你会遇到访问冲突或某种形式的内存损坏。
将 Log
方法更改为:
procedure TForm1.Log;
asm
// fetch return address from top of stack
mov edx, [esp+4]
call LogAddress
end;
请记住,您在 ptr
参数中传递的地址是 Log
调用的 return 地址 。实际上,这通常是 在 之后用于进入 Log
方法的 CALL
指令的地址。因此,您不应期望在映射文件的函数列表中找到该确切地址。相反,您需要查找地址最接近但不超过您拥有的地址的函数。此外,如果您将该地址转换为行号,则它可能是实际调用站点之后的行。
the JCL 中的 JclDebug 单元已经具有可以为您执行此操作的函数。您可以使用 ProcByLevel
函数获取呼叫者的姓名,并使用 LiveByLevel
获取行号。
根据 Rob Kennedy 的建议进行代码替换并调整“esp 偏移量”后,工作代码如下...
为了制作“地图文件”;在 Delphi IDE 中执行以下步骤:
项目 > 选项... > 编译器选项卡 > 代码生成窗格: 优化 = FALSE
项目选项 Window > 链接器选项卡 > 映射文件窗格:Select“详细”
项目 > 构建所有项目
unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1 : TButton; Label1 : TLabel; Edit1 : TEdit; procedure Button1Click(Sender: TObject); Procedure Log; Procedure ShowInfo(hexAddress : Integer); Procedure LogAddress(ptr: pointer); private { Private declarations } public { Public declarations } end; var Form1: TForm1; Implementation {$R *.DFM} Procedure TForm1.Log; // Google Search: capturing a procedure or function's name for logging purposes // http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22596248.html Begin ASM // fetch return address from top of stack mov edx,[esp+8] call LogAddress End;//ASM End; Procedure TForm1.ShowInfo(hexAddress: Integer); // http://www.haydenr.com/delphi/articles/article002.htm Var iMapFileAddress : Integer; sMapFileAddress : String; ImageBase : Integer; SubOffset : Integer; Offset : Integer; Begin ImageBase := [=10=]400000; // Project > Options... > Linker Tab > Memory sizes group box > Image Base SubOffset := 00; Offset := ImageBase + SubOffset; iMapFileAddress := hexAddress - Offset; sMapFileAddress := IntToHex(iMapFileAddress,8); Edit1.Text := sMapFileAddress; //This is the value I get: sMapFileAddress = 00040730 { |Here are some excerpts from: Project1.map |******************************************************************************************************************* |Detailed map of segments | 0001:00040498 000002D4 C=CODE S=.text G=(none) M=Unit1 ACBP=A9 | 0001:0004076C 000001A5 C=CODE S=.text G=(none) M=Project1 ACBP=A9 | : | : | V | Address Publics by Name | | 0001:0004071C TForm1.Button1Click | 0001:00040668 TForm1.Log | 0001:00040700 TForm1.LogAddress | 0001:0004067C TForm1.ShowInfo | : | : | V |Line numbers for Unit1(Unit1.pas) segment .text | | 82 0001:00040728 83 0001:00040730 85 0001:00040764 85 0001:0004076B |******************************************************************************************************************* | | Returned Hex (H) = 00040730 | Returned Integer (I) = 263984 |------------------------------------------------------------------------------------------------------------------- |Address Of Min Hex Max Hex Min Integer Max Integer | Evaluation |------------------------------------------------------------------------------|------------------------------------ |Unit1 00040498 000406E5 263320 263909 | Hmin < H < Hmax AND Imin < I < Imax |Function/Procedure 0004071C 263964 | H < Hmax AND I < Imax |Line Number 00040730 263984 | H = Hmax AND I = Imax |------------------------------------------------------------------------------------------------------------------- | |NOTE: The actual line number where 'Log' is called is: Returned Line Number - 1 (83 - 1 = 82) } End; procedure TForm1.LogAddress(ptr: pointer); begin ShowInfo(Integer(ptr)); end; procedure TForm1.Button1Click(Sender: TObject); begin Log; end; end.