网站建设 环讯传媒,微网站 模板,陕西网页,新媒体营销与运营目录修整
目前的系列目录(后面会根据实际情况变动):
在windows11上编译python将python注入到其他进程并运行注入Python并使用ctypes主动调用进程内的函数和读取内存结构体使用汇编引擎调用进程内的任意函数利用beaengine反汇编引擎的c接口写一个pyd库#xff0c;用于实现inl…目录修整
目前的系列目录(后面会根据实际情况变动):
在windows11上编译python将python注入到其他进程并运行注入Python并使用ctypes主动调用进程内的函数和读取内存结构体使用汇编引擎调用进程内的任意函数利用beaengine反汇编引擎的c接口写一个pyd库用于实现inline hook利用beaengine反汇编引擎的python接口写一个py库用于实现inline hook注入python到微信实现简单的收发消息Bug修复和细节优化允许Python加载运行py脚本并且支持热加载读取微信内存中的好友联系人列表的信息结构体数据做一个僵尸粉检测工具
ctypes的主要功能 ctypes是Python与c写的文件做交互的库能和Python直接交互的也就是动态库了。所以在Windows上主要是调用dllLinux上则是调用so。 不过在这个系列文章里它的作用稍微有些不同。因为Python已经被注入到其他进程可以用ctypes随意操作其他进程的数据和调用其他进程里的函数相对于用c写的dll注入后只需要把c的接口改成Python的。这样就能动态操作不需要频繁改动dll代码注入卸载了 同时它还能调用其他进程里的任意函数不过默认只能调用stdcall和cdecl两种调用约定的函数。如果不是这两种调用约定则需要使用内联汇编来调用。当然Python无法直接内联汇编但可以通过汇编引擎将汇编指令翻译成机器能识别的机器码写入到内存达到内联汇编的效果。也可以不用汇编引擎直接写机器码到内存只要你能记得汇编指令代表的机器码(人肉汇编引擎)。
与进程交互 对于调用dll相关的功能我这里就不多赘述了之前写的一篇文章里有Python基础库-ctypes 这里我主要说下ctypes与进程交互方面比如读取内存结构体调用内存中的函数等
写一个测试程序 先自己写一个测试程序然后在自己的程序测试这样可以避免很多错误也方便调试。简单写了几个函数和结构体测试代码如下
typedef int(*cdecl_add_pointer)(int, int);
typedef int(__stdcall *stdcall_add_pointer)(int, int);struct CString
{wchar_t* s nullptr;size_t len 0;CString(wchar_t* ss) {s ss;len wcslen(ss);}
};CString ccs((wchar_t*)Laaaaaa这是个全局变量结构体);int cdecl_add(int a, int b) {std::wcout Lcdecl调用约定\n;return a b;
}int __stdcall stdcall_add(int a, int b) {std::wcout Lstdcall调用约定\n;return a b;
}int add_callback(stdcall_add_pointer add, int a, int b) {std::wcout Ladd_callback \n;return add(a, b);
}int console_print(CString* cs) {std::wcout Lprint CString: ;std::wcout cs-s;std::wcout L\n;return cs-len;
}
调用进程内的函数
这里就用上一篇的pyexe.dll来将Python注入到目标进程。
现在开始调用cdecl_add和stdcall_add这两个函数首先需要找到他们的地址偏移上面的函数里都有一个字符串这也是我为了方便定位刻意写的。
在x32dbg里搜索字符串就能定位这两个函数比如cdecl_add
得出cdecl_add函数的偏移就是00AF4190-00AE0000, 00AE0000是exe的基址。同理可以知道stdcall_add的基址为0x00AF43B0 - 0x00AE0000
先定义一个GetModuleHandleW函数用于获取exe的基址
import ctypeskernel32 ctypes.WinDLL(kernel32, use_last_errorTrue)
GetModuleHandleW kernel32.GetModuleHandleW
GetModuleHandleW.argtypes (ctypes.c_wchar_p, )
GetModuleHandleW.restype ctypes.c_int
base GetModuleHandleW(CtypesTest.exe)
以下几行代码就是调用cdecl_add的全部代码看注释一行一行解释
# 定义函数指针类型第一个参数是返回值类型后面的都是参数类型
cdecl_add_pfunc ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int)
# 函数的偏移
cdecl_add_offset 0x00AF4190 - 0x00AE0000
# 通过基址和偏移得到当前函数所在内存地址然后传给cdecl_add_pfunc就能得到这个函数
cdecl_add cdecl_add_pfunc(base cdecl_add_offset)
# 传入相应的参数就能调用成功
print(cdecl_add: , cdecl_add(111, 222)) 可以看到结果成功输出也没有报错。没有打印cdecl调用约定是因为我们在注入Python是重定向了stdout如果想要打印目标进程的输出则需要使用上一篇文章提到的CPython接口重定向stdout。
而调用stdcall_add和它基本一样将 ctypes.CFUNCTYPE改成ctypes.WINFUNCTYPE即可
构建结构体并调用函数
接着我们开始调用console_print它的参数类型是一个结构体指针所以要先在Python构建出结构体
ctypes定义结构体代码如下
class CString(ctypes.Structure):_fields_ [(s, ctypes.c_wchar_p),(len, ctypes.c_uint)]
定义console_print函数
console_print_pfunc ctypes.CFUNCTYPE(ctypes.c_int, ctypes.POINTER(CString))
console_print_offset 0x00AF2F10 - 0x00AE0000
console_print console_print_pfunc(base console_print_offset)
创建结构体并赋值
cs CString()
s Python结构体字符串
cs.s ctypes.c_wchar_p(s)
cs.len len(s)
为了确保创建的结构体和目标进程里的一样可以先在Python控制台创建然后在x32dbg里查看。
这里我为了避免一直要输入代码使用import sys;sys.path.append(rT:\Code\PyRobot\part3\py_code)来将目录添加到sys.path然后导入我写的代码import testa。
如果要重新导入import importlib;importlib.reload(testa)查看Python构建的结构体内存地址有三种方法
print(ctypes.byref: , ctypes.byref(cs))
print(ctypes.addressof: , hex(ctypes.addressof(cs)))
print(ctypes.cast: , hex(ctypes.cast(ctypes.pointer(cs), ctypes.c_void_p).value))
效果如下
可以看到cs的内存地址是0x1570d40然后在x32dbg里查看这个内存地址。
在命令里输入dump 0x1570d40或者打开帮助-计算器输入这个地址然后在内存窗口打开
这个地址的内容就是Python构建出的结构体如果不清楚结构体在内存中长啥样可以把c代码创建的结构体也打印出来然后在x32dbg中查看
最后调用这个函数ctypes.byref的作用是传递指针的引用ctypes.pointer也可以它是构造一个新的指针
result console_print(ctypes.byref(cs))
print(console_print result: , result)
调用成功说明结构体构造的没问题
读取内存中的全局结构体
一样是先计算偏移
# 全局变量的内存地址一般偏移是固定的如果是函数内的局部变量就不能这么计算了
ccs_offset 0x00AFE2D0 - 0x00AE0000
css_addr base ccs_offset
然后从地址中读取出结构体里的字符串和整数
s ctypes.c_wchar_p.from_address(css_addr)
l ctypes.c_uint.from_address(css_addr 0x4)
print(单独读取内存结构体: , s.value, l)
更简单的方法就是直接转为结构体
css CString.from_address(css_addr)
print(读取整个结构体: , css.s, css.len)
执行结果如下图:
调用回调函数
先定义一个Python回调函数
def python_stdcall_add(a:int, b:int):print(python_stdcall_add: , a, b)return a-b
定义add_callback函数
add_callback_pfunc ctypes.CFUNCTYPE(ctypes.c_int, stdcall_add_pfunc, ctypes.c_int, ctypes.c_int)
add_callback_offset 0x00AF40D0 - 0x00AE0000
add_callback add_callback_pfunc(base add_callback_offset)
因为回调函数的类型stdcall_add之前已经定义了这里就直接用了
result add_callback(stdcall_add_pfunc(python_stdcall_add), 5, 2)
print(add_callback: , result)
执行结果:
本篇就到此结束了其他更复杂的数据类型在后面的实战中再说。
本篇文章用到的文件和代码
https://github.com/kanadeblisst00/PyRobot-part3