李沉舟 发表于 2017-3-19 11:09

VEH补丁写法,算是对JMP_4E模块的一点补充

本帖最后由 镜中神无 于 2017-3-19 11:24 编辑

昨日突发奇想,欲将某开源反汇编器移植到易语言上,奈何工作量太大,手已酸痛不已。忽忆JMP_4E模块,觉其中有反汇编引擎部分,故寻之于赵总,复辗转得之于HOOK4E模块,源码余尚未观之。见JMP_4E模块一缺陷,故前来补充之,饮水当思源。
(在下文言文渣渣,不是什么才子,开头写这一句话只是为了装X,若有文法错误还望诸位指正)

我个人的思路大致是:
1.dll劫持
2.载入我们的dll
(以上两步看参考Nisy大侠的BayMax补丁)
3.写入int3(0xCC)机器码,同时备份原字节
4.安装VEH
5.检测到中断
6.修改寄存器
7.还原原字节,设置线程EFlags寄存器里的TF标志位
8.产生单步异常,还原int3断点


该帖篇幅有限,故只讲3-8步。


在下易语言学艺不精,仅限于刚刚入门语法的水平,不敢前来献丑(连本书都没买,就是一边写程序一边百度,遇到不会的就百度),故用Delphi作为演示。
在下学艺不精,乃是菜鸟一只,如有错误,还望指正,本人不胜感激。


用我写的一个程序做演示。(请到帖子尾部下载)
.386
.model      flat,stdcall
option      casemap:none


include                windows.inc
include                user32.inc
include                kernel32.inc
includelib      user32.lib
includelib      kernel32.lib

.data
szText                db      '你能尝试把我改掉吗?',0
szCaption      db      '挑衅',0

.code

_RetStr      proc
      
      mov      eax,offset szText
      ret

_RetStr endp

start:
      call      _RetStr
      push      0
      push      offset szCaption
      push      eax
      push      0
      call      MessageBox
      invoke      ExitProcess,0
end      start
用OD载入,
00401000/$B8 00304000   MOV   EAX, 1.00403000                  ;ASCII "你能尝试把我改掉吗?"
00401005\.C3            RETN
00401006 >/$E8 F5FFFFFF   CALL    1.00401000
0040100B|.6A 00         PUSH    0x0                              ; /Style = MB_OK|MB_APPLMODAL
0040100D|.68 15304000   PUSH    1.00403015                     ; |Title = "挑衅"
00401012|.50            PUSH    EAX                              ; |Text
00401013|.6A 00         PUSH    0x0                              ; |hOwner = NULL
00401015|.E8 08000000   CALL    <JMP.&user32.MessageBoxA>      ; \MessageBoxA
0040101A|.6A 00         PUSH    0x0                              ; /ExitCode = 0x0
0040101C\.E8 07000000   CALL    <JMP.&kernel32.ExitProcess>      ; \ExitProcess


分析以上,可知,我们只需要把0x401000这个CALL的返回值,即EAX的数值改掉,指向我们自己的字符串内存地址即可。比较简单的方法是,在0x401005上面写入一个INT3断点,触发中断时,我们修改EAX。

下面上代码,为Delphi7编译,Delphi2007测试编译通过,更高版本未测试。学艺不精,如有错误,还望指正。
该例子只考虑了单线程,而未考虑多线程,实际应用中需要注意
library Project1;

uses
SysUtils,
Windows,
Classes;
type
EXCEPTION_POINTERS = record
    ExceptionRecord: ^_EXCEPTION_RECORD;
    ContextRecord: ^_CONTEXT;
end;
PEXCEPTION_POINTERS = ^EXCEPTION_POINTERS;

const
EXCEPTION_CONTINUE_EXECUTION = -1;
EXCEPTION_CONTINUE_SEARCH = 0;
pAddress: Pointer = Pointer($401005); //下断地址
var
bOld: Byte;
bInt3: Byte = $CC; //int3机器码
sMyStr: string = '在下已经修改掉了你的内容!';
iState: Boolean = False;
{$R *.res}

function VectoredHandler(ExceptionInfo: PEXCEPTION_POINTERS): Integer; stdcall
begin
case ExceptionInfo.ExceptionRecord.ExceptionCode of

    EXCEPTION_BREAKPOINT: //如果是中断异常,就判断是否为我们下了断的地址
    begin
      if ExceptionInfo.ExceptionRecord.ExceptionAddress = pAddress then
      begin
      ExceptionInfo.ContextRecord.EFlags := ExceptionInfo.ContextRecord.EFlags or $100; //设置线程EFLAGS寄存器TF标志位
      CopyMemory(pAddress, @bOld, 1); //还原原字节
      ExceptionInfo.ContextRecord.Eax := Cardinal(PAnsiChar(sMyStr)); //修改EAX寄存器指向我们的字符串地址
      iState := True;
      Result := EXCEPTION_CONTINUE_EXECUTION; //处理完毕
      Exit;
      end
      else
      begin
      Result := EXCEPTION_CONTINUE_SEARCH; //未处理
      Exit;
      end;
    end;

    EXCEPTION_SINGLE_STEP: //如果是单步异常
    begin
      if iState then
      begin
      CopyMemory(pAddress, @bInt3, 1); //重新写下int3断点,在本例子中意义不大      iState := False;
      Result := EXCEPTION_CONTINUE_EXECUTION;
      end
      else
      begin
      Result := EXCEPTION_CONTINUE_SEARCH;
      end;
    end;
   
    else
      Result := EXCEPTION_CONTINUE_SEARCH;
end;
end;

procedure main();
type
TAddVectoredExceptionHandler = procedure (First: Cardinal; pFunc: Pointer); stdcall;
var
pFunc: TAddVectoredExceptionHandler;
hModule: Cardinal;
iProtect: Cardinal;
begin
hModule := GetModuleHandle('kernel32.dll');
if hModule = 0 then
    hModule := LoadLibrary('kernel32.dll');
pFunc := GetProcAddress(hModule, 'AddVectoredExceptionHandler');
pFunc(1, @VectoredHandler); //添加1个异常处理到顶部
VirtualProtect(pAddress, 1, PAGE_EXECUTE_READWRITE, iProtect); //修改代码内存页属性为可写
CopyMemory(@bOld, pAddress, 1); //备份原1Byte机器码,还原时有用
CopyMemory(pAddress, @bInt3, 1); //写入int3断点
end;

exports
main;

begin
main();
end.

如果用LordPE把编译后的dll的main函数导入到程序输入表,打开就会发现这个。


附件下载:


无极 发表于 2017-3-19 12:09

前排 豪华座椅

风云神龙 发表于 2017-3-19 12:12

豪华座椅第二位

叶良辰和赵总 发表于 2017-3-19 13:06

Saimoe 发表于 2017-3-19 22:43

新手 看看 学习下

_BaZzi 发表于 2017-3-19 23:17

VEH作用于整个进程 和线程有关吗?

chenjinghappy 发表于 2017-3-20 00:14

谢谢楼主分享,这个思路不错

列明 发表于 2017-3-20 11:45

不會D啊,模糊的好像看懂了思路,可是實在感覺還沒看懂

影视专业 发表于 2017-3-20 14:26

好文{:5_118:}好文{:5_118:}

李沉舟 发表于 2017-3-20 17:46

_BaZzi 发表于 2017-3-19 23:17
VEH作用于整个进程 和线程有关吗?

线程设置tf位单步,特殊情况应该会有多个线程同时触发单步异常的情况。
页: [1] 2
查看完整版本: VEH补丁写法,算是对JMP_4E模块的一点补充