分享
 
 
 

win32调试API学习心得(三)

王朝delphi·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

要学习如何修改被调试进程,先让我们来了解几个与此有关的函数.

一.读指定进程内存: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);

exit;

end;

while WaitForDebugEvent(DebugD, INFINITE) do

begin {根据事件代码进行相应处理}

case DebugD.dwDebugEventCode of

EXIT_PROCESS_DEBUG_EVENT:

begin

MessageBox(Application.Handle, '被调试进程中止', '!!!', MB_OK or MB_ICONERROR);

Break;

end;

CREATE_PROCESS_DEBUG_EVENT:

begin

ThreadHandle := DebugD.CreateProcessInfo.hThread;

MessageBox(Application.Handle, '被调试进程建立', '!!!', MB_OK or MB_ICONERROR);

end;

EXCEPTION_DEBUG_EVENT:

begin

if (DebugD.Exception.ExceptionRecord.ExceptionCode <> EXCEPTION_SINGLE_STEP) and

(DebugD.Exception.ExceptionRecord.ExceptionCode <> EXCEPTION_BREAKPOINT) then

Rc := False {如果被调试程序产生了异常,让它自己处理}

else

begin

GetThreadContext(ThreadHandle, Context);

{将标志寄存器的陷井标志设为TRUE,这样CPU将会处于单步模式}

Context.EFlags := Context.EFlags or $100;

Inc(CodeCount);

Form1.Label1.Caption := IntToStr(CodeCount);

SetThreadContext(ThreadHandle, Context);

Rc := True;

end;

end;

end;

if Rc then

ContinueDebugEvent(DebugD.dwProcessId, DebugD.dwThreadId,

DBG_CONTINUE)

else

ContinueDebugEvent(DebugD.dwProcessId, DebugD.dwThreadId,

DBG_EXCEPTION_NOT_HANDLED);

end;

CloseHandle(pi.hProcess);

Closehandle(pi.hThread);

end;

procedure TForm1.Button1Click(Sender: TObject);

var

ThreadHandle, ThreadID: THandle;

begin

ThreadHandle := CreateThread(nil, 0, @DebugPro, nil, 0, ThreadID);

end;

end.

最后附上其它的调试API.

一. procedure DebugBreak; stdcall;

该函数在当前进程中产生断点,以便调用的线程能够向调试器发信号.

二. procedure FatalExit(ExitCode: Integer); stdcall;

该函数把执行控制交给调试器,调试器的行为随后被指定为所用调试器的类型.

三. function FlushInstructionCache(hProcess: THandle; const lpBaseAddress: Pointer; dwSize: DWORD): BOOL; stdcall;

该函数为指定进程刷新指令高速缓存器,此函数仅在多进程计算机上是有效的.

hProcess:要刷新的高速缓存器的进程句柄.

lpBaseAddress:要刷新区域的基地址指针,可以为0

dwSize:要刷新区域的长度.

四. function isDebuggerPresent; BOOL; stdcall;

该函数表明调用的进程是否在调试器描述表下运行,此函数从KERNEL32.DLL输出.

五. procedure OutputDebugString(lpOutputString: PChar); stdcall;

该函数为当前的应用程序发送一个字符串到调试器中,lpOutputString为要发送的字符串.

在DELPHI中可以通用View->Debug Windows->Event Log打开Event Log窗口查看被调试程序发送的字符串.

六. procedure SetDebugErrorLevel(dwLevel: DWORD); stdcall;

该函数设置最小错误级别,在该错误级别中系统中将产生调试事件并把它传递给调试器.

dwLevel:指定调试事件的最小错误调试程序,如果错误相等于或大于此程序,系统产生一个调试事件,此参数必须是下列值中的一个.

0: 不记录任何错误. SLE_ERROR:仅记录ERROR级别的调试事件.

SLE_MINORERROR:仅记录MINORERROR级别和ERROR级别的调试事件.

SLE_WARNING:记录WARNING级别,MINORERROR和ERROR级别的调试事件.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有