本帖最后由 镜中神无 于 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作为演示。
在下学艺不精,乃是菜鸟一只,如有错误,还望指正,本人不胜感激。
用我写的一个程序做演示。(请到帖子尾部下载)
[Asm] 纯文本查看 复制代码 .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测试编译通过,更高版本未测试。学艺不精,如有错误,还望指正。
该例子只考虑了单线程,而未考虑多线程,实际应用中需要注意
[Delphi] 纯文本查看 复制代码 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函数导入到程序输入表,打开就会发现这个。
附件下载:
VEH.rar
(45.45 KB, 下载次数: 59)
|