win32调试API学习心得(三)

要学习如何修改被调试进程,先让我们来了解几个与此有关的函数.
一.读指定进程内存: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级别的调试事件.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在这个系列课程中,来自微软的权威技术专家将向您解释Windows操作系统的内部工作原理,从系统架构的大局观出发,逐步展示进程、线程、安全机制、内存管理和存储管理等子系统的工作方式。通过对底层原理的揭示,使您更进一步的理解Windows上各类程序的工作方式和如何进行错误诊断及性能优化。 本次课程的内容编排得到了国内知名技术作家,《Windows Internals》一书的中文译者,潘爱民先生的大力支持,同时TechNet也邀请到了众多微软一线技术专家进行讲解。这是一个为IT专业人员量身定做的Windows内部知识课程,在介绍原理的同时,也紧密地围绕实际案例和常见的故障进行分析点评。这是一个系统的学习Windows底层工作机制的好机会,课程内容深入浅出,精彩纷呈,绝对不容错过。 深入研究Windows内部原理系列之一:Windows的昨天、今天和明天 讲师信息:潘爱民 2007年01月25日 14:00-15:30 Level: 300 著名技术作家、微软亚洲研究院研究员潘爱民老师将在这次课程中跟听众分享Windows的发展历程和技术精萃,描绘操作系统的体系架构、Vista的内核变更以及今后版本Windows的发展趋势。 深入研究Windows内部原理系列之二:Windows体系结构-从操作系统的角度 讲师信息:张银奎 2007年01月26日 14:00-15:30 Level: 400 操作系统是计算机系统的灵魂和管理中心,也是软件系统中最复杂的部分。本讲座将以生动的讲解和丰富的演示带您领略Windows操作系统的核心架构和主要组件,包括HAL、内核、执行体、系统进程(IDLE、SMSS.EXE、WinLogon.EXE)和Windows子系统(CSRSS.EXE、WIN32K.SYS以及子系统DLL)等。并讨论中断管理、对象管理、和异常分发等系统机制和实现这些机制的基本数据结构。 深入研究Windows内部原理系列之Windows体系结构-从应用程序的角度 讲师信息:曾震宇 2007年01月29日 14:00-15:30 Level: 400 从服务器软件到Office办公应用,从联网游戏到即时消息,不管这些应用的复杂程度如何,他们都是一个个在操作系统控制和管理之下的可执行程序。本次课程邀请微软全球技术中心专家级工程师,为各位讲解一个程序是如何经历从启动、分配资源、运行、结束这一连串的过程,并且介绍其中的重要概念和排错诊断技巧。 深入研究Windows内部原理系列之四:Windows操作系统中的重要基本概念 讲师信息:高宇 2007年01月30日 14:00-15:30 Level: 400 进程、线程、资源分配、内存管理、Win32 API、服务、安全,这些是工作中常常提及但是又无法深入理解的神秘概念。在这次课程中,讲师将介绍Windows中最常见与最重要的一些基本概念. 使大家能够顺利地参与到本系列之后的讨论中去。 深入研究Windows内部原理系列之五:Windows Sysinternals工具集介绍 讲师信息:彭爱华 2007年01月31日 14:00-15:30 Level: 400 Sysinternals Suite(Windows Sysinternals工具集)包含一系列免费的系统工具,其中有大名鼎鼎的Process Explorer、FileMon、RegMon等(在Windows Vista下,FileMon和RegMon则被Process Monitor所代替),如果把系统管理员比喻成战士的话,那么Sysinternals Suite就是我们手中的良兵利器。熟悉和掌握这些工具,并且对Windows的体系有一定的了解,将大幅度的提高日常的诊断和排错能力。本课程将以任务驱动的模式,介绍几个经典的应用案例,来介绍Sysinternals Suite的强大功能。 深入研究Windows内部原理系列之六:Vista新特性底层揭秘 讲师信息:彭爱华 2007年02月01日 14:00-15:30 Level: 400 Windows Vista绝非仅仅是具有诸如3D切换、毛玻璃等炫目的界面效果,花钱购买了Windows Vista,而仅仅为了使用其界面效果,难免有点“买椟还珠”的感觉。实际上Windows Vista值得称道的是它具有很多全新的安全特性,例如用户帐户控制、IE保护模式、服务隔离和Windows资源保护等等。有了这些全新的安全特性,我们就可以在相当的程度上摆脱恶意软件的滋扰。Windows之父Jim Allchin曾经说过不要满足于只知道How-to、小技巧之类的知识,而是应该深入底层了解其内部原理。只有了解了这些安全特性的内在原理,才

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值