原文https://www.shuzhiduo.com/A/kPzOnKZ8dx/
感觉有些地方没有很详细,就是按照自己的理解补充说明吧
一些前置知识
(1)任何类型的指针都可以被转化为Pointer
(2)Pointer可以被转化为任何类型的指针
(3)uintptr可以被转化为Pointer
(4)Pointer可以被转化为uintptr
这四句一定要了解,当时就是这里没有很清楚的认识所以卡了很久..
把转化着重标出来,当时踩的坑就是以为包裹了unsafe.Pointer就是同一个类型了,就可以直接互相赋值了,所以没有理解好,这里很感谢一位师傅,下面是师傅的B站号
https://space.bilibili.com/353948151/
一直不厌其烦地解答我的问题,最后终于是懂了
好了这里不扯开了
先来看一下unsafe.Pointer
一个类型只能转换为相同类型的指针
用了这个之后可以把指针转换为不同类型
1 | package main |
uintptr和Pointer可以相互转换,这个也很重要
如果要让指针进行运算,需要先转换成uintptr才行
先来看一下源码
1 | func Run(sc []byte){ |
这个加载器详细的注释在原文中有
需要理解下面的代码
1 | *(**uintptr)(unsafe.Pointer(&f)) = (*uintptr)(unsafe.Pointer(&sc)) |
其实就是让sc的指针也指向f的起始位置
这样执行f函数时相当于执行了&sc的区域
还要理解VirtualProtect函数
看一下这个函数官方解释
VirtualProtect,是对应 Win32 函数的逻辑包装函数,它会在呼叫处理程序的虚拟位置空间里,变更认可页面区域上的保护。
说人话就是修改内存的属性,((void(*)())exec)()那篇里面写过内存有读写执行权限,这个函数就是修改内存的读写执行权限
看一下这个函数的参数
1 | BOOL VirtualProtect( |
lpAddress 要改变属性的内存起始地址
dwSize 要改变内存的大小
flNewProtect 内存新的属性类型
lpflOldProtect 内存原始属性保存地址,简单说就是随便一个可写地址,比如原始内存属性是只读,就会把2存放在这个地址里
然后再连起来看一下,整个过程就是
1 | func Run_test(sc []byte){ |
原文中代码直接执行是不行的,上面是修改之后可以执行的代码
这里主要是看ZwProtectVirtualMemory函数,结果网上一查这个函数竟然没有官方文档?!
后来在csdn的评论里面看到这是一个未公开的函数,好像是VirtualProtectEx的下层函数,
用法和VirtualProtectEx一样有五个参数
1 | 进程的句柄 |
但是明明参数一样使用VirtualProtectEx就不行,问了也没问到
看了一下官方文档VirtualProtectEx函数的第一个参数hProcess必须要有PROCESS_VM_OPERATION访问权限,感觉会不会是因为底层函数所以不需要这个权限
如果有知道的师傅可以教一下吗,提前谢谢了