win32调试API学习心得(三)

2008-04-10 02:57:27来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

要学习如何修改被调试进程,先让我们来了解几个与此有关的函数.
一.读指定进程内存:ReadProcessMemory
此函数的定义为:function ReadProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesRead: DWORD): BOOL; stdcall;
hProcess指向被读取内存的进程的句柄,此句柄必须有PROCESS_VM_READ权限.
lpBaseAddress:指向被读取的内存在进程中基地址的指针.
lpBuffer:指向用于保存读出数据的缓冲区的指针.
nSize:指定从指定进程中要读取的字节数.
lpNumberOfBytesRead:指向读出数据的实际字节数.

二.写指定进程内存:WriteProcessMemory
此函数的定义为:function WriteProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesWritten: DWORD): BOOL; stdcall;
参数含义同ReadProcessMemory,其中hProcess句柄要有对进程的PROCESS_VM_WRITE和PROCESS_VM_OPERATION权限.lpBuffer为要写到指定进程的数据的指针.

注意,如果要修改的内存所在的页面的存取保护属性为只读,如代码段,要修改页面的存取保护才能够正常修改.可使用VirtualProtectEx函数,请参考下面的代码片段:
VirtualProtectEx(hPHandle, Address, SizeOf(BYTE), PAGE_READWRITE, OldFlg);
WriteProcessMemory(hPHandle, Address, @BreakCode, SizeOf(BYTE), Read);
VirtualProtectEx(hPhandle, Address, SizeOf(BYTE), OldFlg, OldFlg); // 恢复页码保护属性
hPHandle为目标进程句柄,Address为要修改的内存的基址,SizeOf(BYTE)表示要修改的区域长度,如果这个长度跨过一个或几个页面边界时,将修改跨过的所有页面的存取保护属性,OldFlg用来存放原来的存取保护属性,以便调用WriteProcessMemory后恢复页面保护属性.

三.得到指定线程的上下文结构:GetThreadContext
此函数的定义为:function GetThreadContext(hThread: THandle; var lpContext: TContext): BOOL; stdcall;
hThread:要取得上下文结构的线程的句柄,可以在发生CREATE_THEAD_DEBUG_EVENT调试事件时保存线程ID和线程句柄的关联以便调用GetThreadContext时得到线程句柄.
lpContext:用来保存指定线程上下文件信息的结构.
在象Windows这样的多任务操作系统中,同一时间里可能运行着几个程序.Windows分配给每个线程一个时间片,当时间片结束后,Windows将冻结当前线程并切换到下一具有最高优先级的线程.在切换之前,Windows将保存当前进程的寄存器的内容,这样当在该线程再次恢复运行时,Windows可以恢复最近一次线程运行的环境.保存的寄存器内容总称为进程上下文.上下文件的结构取决于CPU的类型.
在调用GetThreadContext之前,要先设置TContext的ContextFlags标志来指明要检索的寄存器.例如只想得到CPU的段寄存器的值,可以设置ContextFlags标志为CONTEXT_SEGMENTS.其它可能的标志如下:
CONTEXT_CONTROL:包含CPU的控制寄存器,比如指今指针,堆栈指针,标志和函数返回地址.
CONTEXT_INTEGER:用于标识CPU的整数寄存器.
CONTEXT_FLOATING_POINT:用于标识CPU的浮点寄存器.
CONTEXT_SEGMENTS:用于标识CPU的段寄存器.
CONTEXT_DEBUG_REGISTER:用于标识CPU的调试寄存器.
CONTEXT_EXTENDED_REGISTERS:用于标识CPU的扩展寄存器.
CONTEXT_FULL:相当于CONTEXT_CONTROL or CONTEXT_INTEGER or CONTEXT_SEGMENTS,即这三个标志的组合.

四.设置指定线程的上下文结构:SetThreadContext
此函数的定义为:function SetThreadContext(hThread: THandle; const lpContext: TContext): BOOL; stdcall;
参数同GetThreadContext.
有了这二个函数可以实现很多功能,比如用WriteProcessMemory在被调试进程的某个函数入口处写一个调试中断(int 3,即$cc),然后在运行到此调试中断时会产生中断,再用GetThreadContext得到当前线程的上下文,就可以根据Esp的值得到函数的参数等信息.你甚至可以修改Eip的值来让被调试程序跳到任何地址来执行,或是修改标志寄存器的值来修改进程的执行方式.

了解了以上函数后我们就可以用来修改被调试进程了,具体能实现怎样的功能只局限于自己的想像力了,但运用不得当被调试程序通常会当得很惨。当然这几个函数不止可以用于被调试进程,用于其它进程一样适用(可用OpenProcess根据进程标识符得到进程句柄),例如用它们来做出你自己的游戏修改器等等.

下面的例子演示了如何其得线程的上下文并将CPU置为单步模式来运行程序,注意由于单步模式比较慢,运行一个大的被调试程序时可能会等很久时间.
unit Unit1;

interface

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

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

var
Form1: TForm1;

implementation

{$R *.dfm}

{调试信息处理过程}
procedure DebugPro;
var
si: _STARTUPINFOA; (进程启动信息}
pi: _PROCESS_INFORMATION; {进程信息}
Flage: DWORD;
DebugD: DEBUG_EVENT; {调试事件}
Rc: Boolean;
CodeCount: DWORD; {运行的指令数}
ThreadHandle: Thandle; {主线程句柄}
Context: TContext;
begin
{建立调试进程}
CodeCount := 0;
ConText.ContextFlags := CONTEXT_CONTROL;
Flage := DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS;
GetStartupInfo(si); {初始化si结构,不然无法正常建立进程}
if not CreateProcess(nil, Pchar(''''C:\winnt\NOTEPAD.EXE C:\Boot.ini''''), nil, nil,
False, Flage, nil, nil, si, pi) then
begin
MessageBox(Application.Handle, ''''建立被调试进程失败'''', ''''!!!'''', MB_OK or MB_ICONERROR);

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:在Delphi中自己建立交叉表

下一篇:NeHe的opengl教程delphi版(9)----星星