CnPack Forum


 
Subject: 从 except ... end 跳回 try ... 的例子
skyjacker
版主
Rank: 7Rank: 7Rank: 7
茶农


UID 2239
Digest Posts 9
Credits 617
Posts 269
点点分 617
Reading Access 100
Registered 2006-6-8
Status Offline
Post at 2007-5-17 15:27  Profile | Blog | P.M.  | QQ
从 except ... end 跳回 try ... 的例子

//=====================================================================
// MicroTip#7 从 except ... end 跳回 try ... 的例子
// Http://www.cnpack.org
// Written by SkyJacker 2007.05.17
// QQ Discuss Group: 130970
// 欢迎讨论 Win/Delphi SEH, QQ: 6705517  MSN&EMail: HeMiaoYu@gmail.com
//=====================================================================

函数流程:
try
  ProcA  <----+
except        |
  ProcB   ----+
end;
流程描述: ProcA 发生异常后,进入异常处理函数 ProcB,然后再从 ProcB 跳回 ProcA。

下面函数演示:从异常处理函数返回到异常发生处的下一条指令。

procedure ExceptToTry;
var
  MyAddr: Cardinal;
  sTitle: string;
  sCaption: string;
begin
  sTitle := 'Test';
  sCaption := 'Info';
  try
    asm
      call @CurrAddr;
      @CurrAddr:
      pop MyAddr // 获得本条指令的地址
      xor ecx, ecx
      idiv ecx
      push 0
      push sCaption
      push sTitle
      push 0
      Call MessageBox
    end;
  except
    asm
      mov eax, [MyAddr]
      add eax, 7
      jmp eax
    end;
  end;
end;




一壶清茶煮青春.
Top
Passion (LiuXiao)
管理员
Rank: 9Rank: 9Rank: 9


UID 359
Digest Posts 19
Credits 6760
Posts 3556
点点分 6760
Reading Access 102
Registered 2004-3-28
Status Offline
Post at 2007-5-17 16:21  Profile | Blog | P.M. 
解释一把?

这种情况下如果不用asm的话,用goto貌似也行?我没试过。
Top
skyjacker
版主
Rank: 7Rank: 7Rank: 7
茶农


UID 2239
Digest Posts 9
Credits 617
Posts 269
点点分 617
Reading Access 100
Registered 2006-6-8
Status Offline
Post at 2007-5-17 16:46  Profile | Blog | P.M.  | QQ
try
    asm
      call @CurrAddr; // Call 将下一条指令 pop MyAddr 的地址入栈
      @CurrAddr:
      pop MyAddr // 出栈。获得本条指令(pop MyAddr)的地址,存入 MyAddr
      xor ecx, ecx
      idiv ecx  // 除零错误
      push 0
      push sCaption
      push sTitle
      push 0
      Call MessageBox // 信息提示对话框
    end;
  except
    asm
      mov eax, [MyAddr] // 取 pop MyAddr 指令地址
      add eax, 7 // pop MyAddr 指令为 3 个字节, xor ecx, ecx 为 2 个字节, idiv ecx 为 2 个字节。
      jmp eax    // 跳到 MessageBox 形参入栈起始处 push 0
    end;
  end;
end;


试了一下 goto,编译通不过。
procedure TestGoTo;
var
  I, J: Integer;
label MyL;
begin
  J := 0;
  try
    I := I div J;
    MyL: MessageBox(0, 'Except', 'Info', MB_OK);
  except
    goto MyL;
  end;
end;

提示编译错误:'GOTO MyL' leads into or out of TRY statement




一壶清茶煮青春.
Top
Passion (LiuXiao)
管理员
Rank: 9Rank: 9Rank: 9


UID 359
Digest Posts 19
Credits 6760
Posts 3556
点点分 6760
Reading Access 102
Registered 2004-3-28
Status Offline
Post at 2007-5-17 20:30  Profile | Blog | P.M. 
关键就是pop MyAddr,解释一下就通了。
很少用pop到某个地址的寄存器指令,都不熟悉了。
Top
zzzl (早安的空气)
版主
Rank: 7Rank: 7Rank: 7



UID 590
Digest Posts 0
Credits 399
Posts 199
点点分 399
Reading Access 100
Registered 2004-11-29
Status Offline
Post at 2007-5-22 19:59  Profile | Blog | P.M.  | QQ
再从 ProcB 跳回 ProcA

是指从ProcA的异常处开始执行还是从新?
Top
Passion (LiuXiao)
管理员
Rank: 9Rank: 9Rank: 9


UID 359
Digest Posts 19
Credits 6760
Posts 3556
点点分 6760
Reading Access 102
Registered 2004-3-28
Status Offline
Post at 2007-5-22 20:09  Profile | Blog | P.M. 
这个例子应该是跳过异常部分,从下面的MessageBox处执行。
因为写了
      mov eax, [MyAddr] // 取 pop MyAddr 指令地址
      add eax, 7 // pop MyAddr 指令为 3 个字节, xor ecx, ecx 为 2 个字节, idiv ecx 为 2 个字节。

所以这个7只在这儿有用,没通用的特性。换几个语句就得换个值。
真正的跳回原处执行,得用到SEH的高级点的特性,而不是用asm规定跳回地址。用try等来使用SEH的高级特性的功能貌似VC里头就提供,而Delphi这方面省略了。
Top
skyjacker
版主
Rank: 7Rank: 7Rank: 7
茶农


UID 2239
Digest Posts 9
Credits 617
Posts 269
点点分 617
Reading Access 100
Registered 2006-6-8
Status Offline
Post at 2007-5-23 10:11  Profile | Blog | P.M.  | QQ
如果不利用 SEH 的特性跳回异常发生处, 通过 call,pop 来定位异常发生点还是比较方便的.

SEH 是操作系统的一个特性,
Delphi, VC 编译器级别的异常处理应该是差不多的.
VC 用 __except_handler3 来接管用户的异常处理函数,
Delphi 用 _HandleAnyException 来统一处理.
两者都是映射到由操作系统产生的异常处理结构.

再次执行异常发生处, 是操作系统给应用程序一次修复异常的机会.


//------------------------------------------------------------------------------
// 2.再次执行异常发生点的例子
// Written by SkyJacker
//------------------------------------------------------------------------------

var
  iEcx: Integer;

// 异常处理函数
function MyExceptHandle(): Integer;
begin
  MessageBox(0, 'MyExceptHandle', 'Info', MB_OK);
  iEcx := 1; // 修复异常,操作系统给程序一次修复的机会
  Result := 0; // 返回异常发生处,再次执行 idiv iEcx
end;

procedure JmpExceptionAddr;
begin
  iEcx := 0;
  asm
    lea eax, MyExceptHandle
    push eax
    push fs:[0] // 构造新 SEH 节点
    mov fs:[0], esp
    cdq
    idiv iEcx
  end;
  MessageBox(0, 'OK', 'Hello', 0);
  asm
    mov eax, [esp]
    mov eax, [eax]
    mov fs:[0], eax // 恢复原 SEH
    add esp, 8 // 堆栈平衡
  end;
end;

[ 本帖最后由 skyjacker 于 2007-5-23 10:16 编辑 ]




一壶清茶煮青春.
Top
zzzl (早安的空气)
版主
Rank: 7Rank: 7Rank: 7



UID 590
Digest Posts 0
Credits 399
Posts 199
点点分 399
Reading Access 100
Registered 2004-11-29
Status Offline
Post at 2007-6-3 00:52  Profile | Blog | P.M.  | QQ
好强,有没有从任意位置跳到任意位置的万能方法
Top
skyjacker
版主
Rank: 7Rank: 7Rank: 7
茶农


UID 2239
Digest Posts 9
Credits 617
Posts 269
点点分 617
Reading Access 100
Registered 2006-6-8
Status Offline
Post at 2007-6-6 12:40  Profile | Blog | P.M.  | QQ
jmp 0~2^32-1

但会有执行权限的问题.
随便跳过去也不知道执行什么代码阿.

比较有意义的是,能控制到能够预期执行的代码地址.
比如执行到自定义的 ShellCode.

[ 本帖最后由 skyjacker 于 2007-6-6 12:42 编辑 ]




一壶清茶煮青春.
Top
Passion (LiuXiao)
管理员
Rank: 9Rank: 9Rank: 9


UID 359
Digest Posts 19
Credits 6760
Posts 3556
点点分 6760
Reading Access 102
Registered 2004-3-28
Status Offline
Post at 2007-6-6 19:14  Profile | Blog | P.M. 
跳转指令容易实现,关键是跳转的地址不好确定。

CnWizards的源码中有个MethodHook,就是在Method函数体地址里头写入Jmp指令,然后后面接的偏移量就是计算出来的相对偏移值,这样跳转才能正确地从旧的Method跳到新的Method里头。
Top
 




All times are GMT++8, the time now is 2024-4-29 07:02

    本论坛支付平台由支付宝提供
携手打造安全诚信的交易社区 Powered by Discuz! 5.0.0  © 2001-2006 Comsenz Inc.
Processed in 0.008574 second(s), 8 queries , Gzip enabled

Clear Cookies - Contact Us - CnPack Website - Archiver - WAP