好友
阅读权限30
听众
最后登录1970-1-1
|
本帖最后由 bester 于 2019-7-8 11:33 编辑
首先感谢Delphi学习交流群的myy,昨天调试了6个小时,终于完成了,记录一下,以免有爱好者走弯路,某度的资料我等菜鸟看不懂,思来想去还是自己挖坑自己填。
win10下注意两个问题:
1.OpenProcess无法获取进程句柄,一般此问题在Dll中调用此函数出现,用GetLastError返回5,意思是拒绝访问,DLL暂时没试验以下三种办法
解决方法:
1.EXE用管理员运行再用OD附加
2.用提权函数提升至DEBUG权限
3.DELPHI自身IDE运行EXE不会出现此问题
2.WriteProcessMemory的第5个参数,IpNumberOfByteWritten(表示:实际写入字节),他的类型不是Cardinal类型,是NativeUInt类型,这里参考写错了,如果写Cardinal类型会提示实参和形参不一致。也可能是NativeUInt类型是Cardinal类型的分支,这里不做探究,反正Delphi的参考进去了winapi.windows看了源码以后,他的参考又不一样了,坑。
代码如下,会逐句解释
[Delphi] 纯文本查看 复制代码 function lens(x,y:Integer):Integer; //声明一个函数lens,lens函数是计算JMP距离的,也就是从api函数跳转到新函数的距离
begin
Result:=x-y-5; // X=新函数地址,Y=api函数地址,由于Jmp(16进制E9占1个字节)+距离(占4个字节),实际上跳转是从api地址+5开始起跳,如果不减5,实际上会跳到新函数的地址+5
end; //例:如果新函数地址=00000006,api地址=00000000,那么起跳位置实际是00000006-00000000-5,如果不减5,将会跳转到00000006+5的位置
function MyMessages(hWnd: Cardinal; lpText, lpCaption: PAnsiChar; uType: Cardinal):Integer; stdcall; //声明一个和MessageBoxA函数一样类型的函数,命名为MyMessages,注意一定要用stdcall约定,否则会调用出错
var
text: PAnsiChar; //定义一个变量为text,类型和形式参数的类型一样为PAnsiChar,便于操作形式参数
begin //注意声明一个函数,不论函数是否有代码,都应该用begin end括起来,否则IDE无限提示出错
text := lpText; //令ipText的值=text,这样的话,我就能获取到MessageBoxA的参数的内容了
ShowMessage('我获取到的信息框标题是: ' + text); //此时调用ShowMessage,调试输出MessageBoxA的IpText参数的值
end;
procedure TForm1.Button2Click(Sender: TObject); //这里是一个按钮的按钮事件,我添加了两个按钮,一个是调用原始信息框的按钮,一个是Hook按钮,这里是Hook按钮
var
hp: Integer; //定义一个变量为HP,用来保存进程句柄
apiaddr: Integer; //定义一个变量为apiaddr,用来保存获取到的api地址,获取到的api的地址为10进制,但我们无需转成OD的16进制,为了方便查看可以手动调用calc计算器进行转换或者ctrl+F5添加监视
read: array[1..5] of Byte; //定义一个元素序号为1-5的数组,数组名为read,类型为byte类型,表示保存5个字节,用来保存api函数的5个字节,因为我们需要修改这5个字节跳转到新函数,到时候可便于恢复Hook
rd: NativeUInt; //定义一个变量为rd,类型为NativeUInt,这个变量是函数保存实际操作了几个字节,比如我读取或者写入了5个字节,如果操作成功,这里的值就是5,否则就是0,上面有作说明,类型必须为NativeUInt,
jmps: Integer; //定义一个变量为Jmps,这里用来保存Jmp的机器码(E9)的十进制,因为我们操作的是10进制,上面说了无需转换成OD的16进制
tiaoshi,tiaoshi1: Integer; //这里用来调试输出内容,方便我查找问题,可以不要。
begin
hp := OpenProcess(PROCESS_ALL_ACCESS, False, GetCurrentProcessId); //OpenProcess函数,如果操作成功,那么hp变量将保存进程句柄,第三个参数用GetCurrentProcessId,表示获取自身进程的进程ID,如果是操作别的进程,这里要填需要操作的进程的进程ID,但注意要用DLL形式,EXE无法操作
apiaddr := Integer(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxA')); //这里用LoadLibrary获取user32.dll的模块地址,也可以说是模块句柄,再用GetProcessAddress获取MessageBoxA的api地址,GetProcessAddress返回的是Pointer类型,所以用Integer转成整数型,也就是10进制
ReadProcessMemory(hp, Pointer(apiaddr), Pointer(@read), 5, rd); //用ReadProcessMemory读取api函数的头5个字节,保存在read数组当中,ReadProcessMemory(进程句柄:Cradinal类型,读取地址:pointer类型,保存读取的数据:pointer类型(加@表示保存的数据的地址,也就是所谓的传址),读取数据的大小:Cradinal类型,实际大小:NativeUInt类型)
jmps := 233; //JMP的机器码(E9)的十进制
WriteProcessMemory(hp, Pointer(apiaddr), Pointer(@jmps), 1, rd); //将E9写入到首地址的第一个字节
tiaoshi:=Integer(@MyMessages) ; //这里我调试输出查看一下是否获取到新函数的地址,加@表示取新函数的实际地址,跟上面的readprocessmemory的第三个参数是一样的意思。
tiaoshi1 := lens(Integer(@MyMessages), apiaddr); //这里我调试输出查看距离(占用4个字节),也就是JMP后面的4个字节的机器码,实际上转换成16进制是这样的形式,假设调试输出的值是01020304,那么在OD中查看需要倒转一下,也就是04030201,这样的形式
WriteProcessMemory(hp, Pointer(apiaddr + 1), Pointer(@tiaoshi1), 4, rd); //这里将距离写入,因为获取到的api的地址是第一个字节我们写入了E9,所以要从第二个字节开始写入4个字节的大小,所以apiaddr+1
CloseHandle(hp) ; //这里关闭我们打开的进程句柄,虽然我也不知道是为什么,但是好像就是要关闭。
end;
procedure TForm1.Button1Click(Sender: TObject); //这里就是我说的第一个按钮,调用原始的MessageBoxA
begin
MessageBoxA(0, '我是标题', '我是内容', 0)
end;
下期讲注入器部分以及怎么把上面的代码在DLL中使用,这里用EXE是为了方便我调试,有错误欢迎指正,欢迎交流Delphi,我的博客园里面会写一些文章,因为菜所以菜。 |
免费评分
-
参与人数 4 | 吾爱币 +6 |
热心值 +4 |
收起
理由
|
asong
| + 1 |
+ 1 |
谢谢@Thanks! |
苏紫方璇
| + 3 |
+ 1 |
欢迎分析讨论交流,吾爱破解论坛有你更精彩! |
朱朱你堕落了
| + 1 |
+ 1 |
最好的HOOK是七字节热补丁方式,也是微软推荐的HOOK方式。 |
homejun
| + 1 |
+ 1 |
用心讨论,共获提升! |
查看全部评分
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|