24小时热门版块排行榜    

查看: 413  |  回复: 0
当前主题已经存档。

sdlj8051

金虫 (著名写手)

[交流] [转贴]应用内存断点的Loader

program Loader;
(***********************************)
(*                                 *)
(* Memory Access Breakpoint Loader *)
(*            by tt.t              *)
(*                                 *)
(*                                 *)
(***********************************)

{
目标:拦截对指定地址的写操作,nop掉写地址的指令。
难点:在要补丁地址设Memory Write Breakpoint,查了半天没找到现成的东西,只好自己写。
思路:将要补丁的地址设为不可写,当写时会发生AV错误,然后进行Patch。但VirtualProtectEx会将整

个Page设为不可写属性,所有写Page的操作都会产生Access violation,无法直接找到要Patch的代码。
解决方法:要保存地址所在Page的地址范围,发生AV错误时判断是否因为VirtualProtect产生的Exceptio

n,如不是将Page的属性设为可写,同时设置单步标志,写操作完成后恢复Page为不可写属性,继续执

行,直至找到需Patch代码。
不知道有没有更好的方法。
}

uses
  JwaWinType,
  JwaWinNt,
  JwaWinBase,
  JwaWinUser;

{$R *.res}

var
  si: STARTUPINFO;
  pi: PROCESS_INFORMATION;

function MyExtractFilePath(f: String): String;
var
  i, l: integer;
begin
  l := Length(f);
  for i := l downto 1 do
    if f = '' then Break;
  result := copy(f, 0, i);
end;

procedure CreateVictimProcess(Path: String);
const
  Nop: PChar = Chr($90) + Chr($90) + Chr($90);
var
  DbgEvent: TDebugEvent;
  DbgParam: DWORD;
  OldPrt, NewPrt: DWORD;
  pPatch: PByte;
  PgMin, PgMax: DWORD;
  MemInfo: TMemoryBasicInformation;
  WExpAddr: DWORD;
  DbgContext: TContext;
  rm: Boolean;
  hThread: DWORD;
begin   
  ZeroMemory(@si, sizeof(STARTUPINFO));
  si.cb := sizeof(STARTUPINFO);
  if not CreateProcess(PChar(Path), nil, nil, nil, False, CREATE_SUSPENDED or

CREATE_DEFAULT_ERROR_MODE, nil,
                       PChar(MyExtractFilePath(Path)), si, pi) then
  begin
    MessageBox(0, 'CreateProcess failed! ', 'Error!', 0);
    exit;
  end;
  ResumeThread(pi.hThread);
  if WaitForInputIdle(pi.hProcess, INFINITE) <> 0 then
  begin
    MessageBox(0, 'WaitForInputIdle failed! ', 'Error!', 0);
    exit;
  end;
  if not DebugActiveProcess(pi.dwProcessId) then
  begin
    MessageBox(0, 'DebugActiveProcess failed! ', 'Error!', 0);
    exit;
  end;
  if VirtualQueryEx(pi.hProcess, Pointer($9c66BC), MemInfo, SizeOf(MemInfo)) = 0 then
  begin
    MessageBox(0, 'VirtualQueryEx failed! ', 'Error!', 0);
    exit;
  end;
  PgMin := DWORD(MemInfo.BaseAddress);
  pgMax := PgMin + MemInfo.RegionSize;
  {VirtualProtect会将整个Page设为不可写属性,所有写Page的操作都会产生Access violation,要保存

整个Page的地址范围,
   后面可以判断是否因为VirtualProtect产生的Exception}      
  if not VirtualProtectEx(pi.hProcess, Pointer($9c66BC), 1, PAGE_EXECUTE_READ, @OldPrt) then
  begin
    MessageBox(0, 'VirtualProtectEx failed! ', 'Error!', 0);
    exit;
  end;
  {改写Page属性为不可写}
  rm := false;
  while WaitForDebugEvent(DbgEvent, INFINITE) do
  begin
    DbgParam := DBG_CONTINUE;
    case DbgEvent.dwDebugEventCode of
      EXCEPTION_DEBUG_EVENT:
      begin
        if DbgEvent.Exception.ExceptionRecord.ExceptionCode <> EXCEPTION_BREAKPOINT then
          case DbgEvent.Exception.ExceptionRecord.ExceptionCode of
            EXCEPTION_SINGLE_STEP: {单步中断}
            begin
              if rm then {由于EXCEPTION_ACCESS_VIOLATION产生的单步中断,恢复Page为不可写属性}
              begin
                rm := false;
                VirtualProtectEx(pi.hProcess, Pointer($9c66BC), 1, PAGE_EXECUTE_READ, @NewPrt);
              end;
            end;
            EXCEPTION_ACCESS_VIOLATION: {AV中断}
            begin
              DbgParam := DBG_EXCEPTION_NOT_HANDLED;
              if DbgEvent.Exception.ExceptionRecord.ExceptionInformation[0] = 1 then {写操作}
              begin
                WExpAddr := DbgEvent.Exception.ExceptionRecord.ExceptionInformation[1]; {写操作的目标地

址}
                if (WExpAddr >= PgMin) and (WExpAddr <= PgMax) then {目标地址在Page范围}
                begin
                  DbgParam := DBG_CONTINUE;
                  if(WExpAddr <> $9c66BC) then {不是写指定地址}
                  begin
                    VirtualProtectEx(pi.hProcess, Pointer($9c66BC), 1, OldPrt, @NewPrt);
                    DbgContext.ContextFlags := CONTEXT_CONTROL;
                    hThread := OpenThread(THREAD_ALL_ACCESS, false, DbgEvent.dwThreadId);
                    GetThreadContext(hThread, DbgContext);
                    DbgContext.EFlags := DbgContext.EFlags or $100;

{设单步标志,会触发EXCEPTION_SINGLE_STEP}
                    SetThreadContext(hThread, DbgContext);
                    rm := true; {标志,表明是EXCEPTION_ACCESS_VIOLATION产生的单步中断}
                  end
                  else
                  begin {Patch}
                    pPatch := DbgEvent.Exception.ExceptionRecord.ExceptionAddress;
                    VirtualProtectEx(pi.hProcess, pPatch, 3, PAGE_READWRITE, @NewPrt);
                    WriteProcessMemory(pi.hProcess, pPatch, Nop, 3, nil);
                    VirtualProtectEx(pi.hProcess, pPatch, 3, NewPrt, @NewPrt);
                  end;
                end;
              end;
            end;
            else
              DbgParam := DBG_EXCEPTION_NOT_HANDLED;
          end;
      end;
      EXIT_PROCESS_DEBUG_EVENT:
      begin
        ContinueDebugEvent(DbgEvent.dwProcessId, DbgEvent.dwThreadId, DbgParam);
        Break;
      end;
    end;
    ContinueDebugEvent(DbgEvent.dwProcessId, DbgEvent.dwThreadId, DbgParam);
  end;
end;

var
  Victim: string;
begin
  Victim := MyExtractFilePath(ParamStr(0)) + 'Wise***.exe'; //受害程序
  CreateVictimProcess(Victim);
  halt;
end.


--------------------------------------------------------------------------------

标 题: 答复
发帖人:ttui
时 间: 2005-10-28 10:09
详细信息:


ft,既然看不懂那就那就详细注释下。
记得要大体了解下deubg api的用法和使用流程先.

procedure CreateVictimProcess(Path: String);
const
  Nop: PChar = Chr($90) + Chr($90) + Chr($90);
var
  DbgEvent: TDebugEvent;
  DbgParam: DWORD;  //ContinueDebugEvent用,标志如何处理调试消息
  OldPrt, NewPrt: DWORD;
  pPatch: PByte;
  PgMin, PgMax: DWORD;
  MemInfo: TMemoryBasicInformation;
  WExpAddr: DWORD;
  DbgContext: TContext;
  rm: Boolean;
  hThread: DWORD;
begin   
  ZeroMemory(@si, sizeof(STARTUPINFO));
  si.cb := sizeof(STARTUPINFO);
  if not CreateProcess(PChar(Path), nil, nil, nil, False, CREATE_SUSPENDED or CREATE_DEFAULT_ERROR_MODE, nil,
                       PChar(MyExtractFilePath(Path)), si, pi) then
  begin
    MessageBox(0, 'CreateProcess failed! ', 'Error!', 0);
    exit;
  end;
  //建立目标进程
  ResumeThread(pi.hThread);
  //恢复进程执行.其实CreateProcess时不加CREATE_SUSPENDED标志就可以省掉这句
  if WaitForInputIdle(pi.hProcess, INFINITE) <> 0 then
  begin
    MessageBox(0, 'WaitForInputIdle failed! ', 'Error!', 0);
    exit;
  end;
  //等待目标进程完全运行至其开始等待用户输入.
  //因为目标程序是加过壳的,壳的部分会检查调试器,所以等到壳运行结束在去debug它.
  if not DebugActiveProcess(pi.dwProcessId) then
  begin
    MessageBox(0, 'DebugActiveProcess failed! ', 'Error!', 0);
    exit;
  end;
  //挂上目标程序
  if VirtualQueryEx(pi.hProcess, Pointer($9c66BC), MemInfo, SizeOf(MemInfo)) = 0 then
  begin
    MessageBox(0, 'VirtualQueryEx failed! ', 'Error!', 0);
    exit;
  end;
  //查询需要设内存断点的地址($9c66BC,也就是0x9c66BC,后面简称addr)所在page的情况
  PgMin := DWORD(MemInfo.BaseAddress);
  pgMax := PgMin + MemInfo.RegionSize;
  //得到addr所在的page的始末地址.
  {因为VirtualProtect会将addr所在整个Page设为不可写属性(见VirtualProtect的API说明),所有写Page的操作,即使不是写我们感兴趣(但位于那个page上)的地址的操作,都会因为将addr设为不可写属性而产生Access violation.所以这里要保存整个Page的地址范围,以便后面判断AV是不是因写addr而产生的}  
  if not VirtualProtectEx(pi.hProcess, Pointer($9c66BC), 1, PAGE_EXECUTE_READ, @OldPrt) then
  begin
    MessageBox(0, 'VirtualProtectEx failed! ', 'Error!', 0);
    exit;
  end;
  {改写addr属性为不可写.这里会将addr所在的整个Page设为不可写属性}
  rm := false;  {标志是不是因处理AV设置的单步中断产生的中断}
  while WaitForDebugEvent(DbgEvent, INFINITE) do  //等待调试消息
  begin
    DbgParam := DBG_CONTINUE;  //默认处理调试消息
    case DbgEvent.dwDebugEventCode of
      EXCEPTION_DEBUG_EVENT:
      begin
        if DbgEvent.Exception.ExceptionRecord.ExceptionCode <> EXCEPTION_BREAKPOINT then
        //判断是不是int 3产生的中断.因为DebugActiveProcess挂上程序后会向debuger发出一个EXCEPTION_BREAKPOINT调试消息,这里忽略它
          case DbgEvent.Exception.ExceptionRecord.ExceptionCode of  //判断异常类型
            EXCEPTION_ACCESS_VIOLATION: {AV异常}
            begin
              DbgParam := DBG_EXCEPTION_NOT_HANDLED; //表示由目标程序的SEH处理异常
              if DbgEvent.Exception.ExceptionRecord.ExceptionInformation[0] = 1 then
              begin  {是写操作产生的AV}
                WExpAddr := DbgEvent.Exception.ExceptionRecord.ExceptionInformation[1]; {WExpAddr =写操作的目标地址}
                if (WExpAddr >= PgMin) and (WExpAddr <= PgMax) then {WExpAddr在addr所在Page上}
                begin
                  DbgParam := DBG_CONTINUE;//表示我们来处理异常,让目标程序从发生异常处继续执行.
                  if(WExpAddr <> $9c66BC) then {WExpAddr不是写addr,要允许目标程序写操作}
                  begin
                    VirtualProtectEx(pi.hProcess, Pointer($9c66BC), 1, OldPrt, @NewPrt);//恢复addr所在page为原来的属性(可写)

                    DbgContext.ContextFlags := CONTEXT_CONTROL;
                    hThread := OpenThread(THREAD_ALL_ACCESS, false, DbgEvent.dwThreadId);
                    //由Thread Id得到Thread handle
                    GetThreadContext(hThread, DbgContext);
                    //得到thread的Context,设置单步执行标志,让写操作完成后发生单步中断,以便将addr重新设为不可写,拦截下一次写操作.
                    DbgContext.EFlags := DbgContext.EFlags or $100; {设单步标志}
                    SetThreadContext(hThread, DbgContext);
                    //设置thread的Context

                    rm := true; {标志,表明是我们处理AV而产生的单步中断}
                  end
                  else  //{WExpAddr是写addr,进行Patch}
                  begin
                    pPatch := DbgEvent.Exception.ExceptionRecord.ExceptionAddress;
                    //写地址操作语句地址
                    VirtualProtectEx(pi.hProcess, pPatch, 3, PAGE_READWRITE, @NewPrt);
                    //设操作语句地址为可写
                    WriteProcessMemory(pi.hProcess, pPatch, Nop, 3, nil);
                    //nop掉写地址语句
                    VirtualProtectEx(pi.hProcess, pPatch, 3, NewPrt, @NewPrt);
                    //恢复语句地址属性
                  end;
                end;
              end;
            end;{AV异常处理完毕}
            EXCEPTION_SINGLE_STEP: {发生单步中断}
            begin
              if rm then {由于处理AV产生的单步中断.}
              begin
                rm := false;
               //重置标志
                VirtualProtectEx(pi.hProcess, Pointer($9c66BC), 1, PAGE_EXECUTE_READ, @NewPrt);
               //恢复Page为不可写属性,以便拦截下次写操作
              end;
            end;
            else//不是AV中断或单步中断,由目标程序的SEH处理异常
              DbgParam := DBG_EXCEPTION_NOT_HANDLED;
          end;
      end;
      EXIT_PROCESS_DEBUG_EVENT:
      begin
        ContinueDebugEvent(DbgEvent.dwProcessId, DbgEvent.dwThreadId, DbgParam);
        //目标程序推出,loader使命结束,退出
        Break;
      end;
    end;
    ContinueDebugEvent(DbgEvent.dwProcessId, DbgEvent.dwThreadId, DbgParam);
  end;
end;

[ Last edited by sdlj8051 on 2006-10-6 at 11:31 ]
回复此楼
已阅   回复此楼   关注TA 给TA发消息 送TA红花 TA的回帖
相关版块跳转 我要订阅楼主 sdlj8051 的主题更新
普通表情 高级回复 (可上传附件)
最具人气热帖推荐 [查看全部] 作者 回/看 最后发表
[论文投稿] 售SCI文章,我:8O5.5.1.O.54,科目齐全,可+急 +3 G6APbkg8SA6w 2026-08-29 4/200 2026-08-30 07:39 by ZPa0EcMwuECS
[硕博家园] 售SCI一区T0P文章,我:8O.55.1.O.54,科目全,可伽急 +3 G6APbkg8SA6w 2026-08-29 4/200 2026-08-30 07:17 by ZPa0EcMwuECS
[考博] 售SCI-T0P文章,我:8O.5.5.1.O.54,科目齐全,可+急 +3 gy1nBQXYQJqL 2026-08-29 5/250 2026-08-30 07:05 by ZPa0EcMwuECS
[找工作] 售SCI文章,我:8O5.5.1.O.54,科目齐全,可+急 +4 gy1nBQXYQJqL 2026-08-29 7/350 2026-08-30 06:46 by ZPa0EcMwuECS
[教师之家] 售SCI一区T0P文章,我:8.O.55.1.O54,科目全,可伽急 +5 ASdOkHsho7FD 2026-08-28 7/350 2026-08-30 06:10 by ZPa0EcMwuECS
[考博] 售SCI一区文章,我:8.O.55.1.O.54,科目齐全,可伽急 +3 jCd0dEvKHShX 2026-08-29 4/200 2026-08-30 03:26 by ZPa0EcMwuECS
[论文投稿] 售SCI一区T0P文章,我:8O.55.1.O.5.4,科目齐全,可+急 +5 gy1nBQXYQJqL 2026-08-29 6/300 2026-08-30 01:58 by ZPa0EcMwuECS
[论文投稿] 售SCI一区文章,我:8.O.551.O.5.4,科目全,可伽急 +4 ASdOkHsho7FD 2026-08-28 5/250 2026-08-30 01:03 by ZPa0EcMwuECS
[公派出国] 售SCI一区T0P文章,我:8.O.55.1.O.5.4,科目全,可+急 +4 ASdOkHsho7FD 2026-08-28 5/250 2026-08-30 00:52 by ZPa0EcMwuECS
[论文投稿] 售SCI一区T0P文章,我:8.O55.1.O.54,科目全,可十急 +4 ASdOkHsho7FD 2026-08-28 5/250 2026-08-30 00:41 by ZPa0EcMwuECS
[基金申请] 中青基了要发朋友圈吗? +4 349506619 2026-08-28 4/200 2026-08-29 22:41 by alongwaytogo
[教师之家] 导师吐槽:我怎么摊上了这么个极品研究生! +9 苏东坡二世 2026-08-23 9/450 2026-08-29 14:41 by hustersqt
[基金申请] 申请删除本帖 +6 lyz123lyz 2026-08-27 7/350 2026-08-27 17:31 by 宁静致远sy
[基金申请] 2026年的国家社科基金项目通讯评审的新规则与新动向、新挑战 +7 process2012 2026-08-23 10/500 2026-08-26 19:23 by hmhminy
[基金申请] 2026年8月25日国自然放榜前突然收到列入评审专家邮件,有关系吗? +25 木水思豆 2026-08-25 28/1400 2026-08-26 14:53 by draco1987
[基金申请] 项目信息和经费信息在系统里都可以看到了 +6 wittyboy 2026-08-26 14/700 2026-08-26 10:55 by wittyboy
[基金申请] 国合可查了 +3 paperzjh 2026-08-26 3/150 2026-08-26 10:41 by LemmonTr
[基金申请] 牛来!米来!面来! +8 beefly 2026-08-26 8/400 2026-08-26 08:37 by xuzhipiao
[基金申请] 如果此刻你正在为国基感到焦虑,不妨来听听这首《基金之外》 +8 scalable 2026-08-24 8/400 2026-08-25 12:52 by jnhyjjm
[基金申请] 没有任何消息-是不是就凉了 +9 图啦图啦 2026-08-24 10/500 2026-08-25 11:59 by 南海小哥
信息提示
请填处理意见