【Python微信机器人】第三篇:使用ctypes调用进程函数和读取内存结构体
目录修整
目前的系列目录(后面会根据实际情况变动):
- 在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上主要是调用dll,Linux上则是调用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*)L"aaaaaa这是个全局变量结构体");int cdecl_add(int a, int b) {std::wcout << L"cdecl调用约定\n";return a + b;
}int __stdcall stdcall_add(int a, int b) {std::wcout << L"stdcall调用约定\n";return a + b;
}int add_callback(stdcall_add_pointer add, int a, int b) {std::wcout << L"add_callback \n";return add(a, b);
}int console_print(CString* cs) {std::wcout << L"print 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_error=True)
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(r"T:\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
相关文章:

【Python微信机器人】第三篇:使用ctypes调用进程函数和读取内存结构体
目录修整 目前的系列目录(后面会根据实际情况变动): 在windows11上编译python将python注入到其他进程并运行注入Python并使用ctypes主动调用进程内的函数和读取内存结构体使用汇编引擎调用进程内的任意函数利用beaengine反汇编引擎的c接口写一个pyd库,用于实现inl…...
easyExcel按模板填充数据,处理模板列表合并问题等,并导出为html,pdf,png等格式文件demo
1.工具类 /*** excle模板填充并转换html* * @param response 返回* @param order 主体内容* @param goods 配件列表* @param pro 项目列表* @throws IOException*/public static void moudleExcleToHtml(HttpServletResponse response, String moudleUrl, Object o…...

怎么开发小程序?微信小程序开发方式
小程序开发之所以受到欢迎,是因为相比于APP,小程序的开发成本更低。不需要下载和安装,用户可以直接在微信内使用,节省了开发和维护的成本。小程序的开发方式有很多种,常见的有:自己源码开发、模板编辑、购买…...

测试从外包到自研再到大厂,这5年鬼知道我是怎么过来的
18岁那年我背井离乡来到从来没有来过的郑州。在一所普通的二本院校里学网络工程。 很明显,在大学以前只会用电脑开关机打LOL的我恍然间只觉得自己来到了人间天堂,没有人管,也没有任何烦恼无忧无虑的过了三年大学生涯。 直到秋招的开始&…...

Stable Diffusion系列(二):ControlNet基础控件介绍
文章目录 线稿提取类Canny:边缘检测SoftEdge:软边缘检测Lineart:精细线稿提取Scribble/Sketch:涂鸦提取MLSD:建筑领域的线条提取 3D提取类Normal map:法线贴图Depth:深度计算Segmentation&#…...

sql server数据库跟踪——SQL Server Profiler解析
工具: SQL Server Profiler这个工具是SQL Server数据库自带的语句执行跟踪工具,常使用于分析软件修改数据库时所执行的语句,适合用来研究软件运行数据库的原理。 打开方式: 本机安装了SQL server的话,都是自带的。直接…...

多机多卡分布式训练
1. 环境搭建 分布式训练框架:acceleratedeepspeedpdsh(可有可无)基础环境:cuda、显卡驱动、pytorch 1.1 安装相关包 cuda安装:参考官网安装步骤 wget https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda-…...

打字练习软件 Type Fu mac中文版技能介绍
Type Fu mac是一款打字练习和提高打字速度的应用程序。它旨在帮助用户通过练习键盘打字,提高打字准确性和速度。无论您是初学者还是想要提高打字技能的专业人士,Type Fu都是一个很好的选择! Type Fu mac采用了一种互动,游戏化的方…...

我的云栖大会之旅:见证云计算创新的15年
云栖大会,曾经是一次不可思议的科技之旅,却如今已见证了我对云计算世界的15年关注和发展。第一次踏上云栖大会之旅,我记得是在2009年。那时的云计算还是一个新生事物,而云栖大会正是其中的奠基石。 我清楚地记得那个炎热的夏天&am…...

一个小技巧,显著提升大模型推理能力!加州大学提出MAF多反馈框架
作者 | 谢年年 最近,多篇文章《GPT-4的推理能力非常有限,有两篇论文为证》、《DeepMind:无法自我纠正推理,除非提前得知正确答案》指出大模型在推理任务中似乎没有自我改进的能力。即在无任何外部反馈的情况下无法通过自我纠正的形…...

测开 (Junit 单元测试框架)
目录 了解 Junit 引入相关依赖 1、Junit注解 Test BeforeEach、BeforeAll AfterEach && AfterAll 2、断言 1、Assertions - assertEquals 方法 2、Assertions - assertNotEquals 方法 3、Assertions - assertTrue && assertFalse方法 4、Assertions…...
ncurse编程指南
文章目录 0. 介绍1. Hello, Ncurse2. 初始化函数2.1 raw() 和 cbreak()2.2 echo()和noecho()2.3 keypad()2.4 halfdelay()2.5 初始化样例 3. 命名规范4. 输出函数4.1 addch()类函数4.2 printw()类函数4.3 addstr()类函数4.4 注意4.5 输出函数例子 5. 输入函数5.1 getch()5.2 sc…...
Graph U-Net Code【图分类】
1. main.py # GNet是需要用到的model net GNet(G_data.feat_dim, G_data.num_class, args) # graph, 特征维度,类别数,参数 trainer Trainer(args, net, G_data) #开始训练数据 # 正式开始训练数据 trainer.train()2. network.py class GNet(nn.Modul…...
PTA 秀恩爱分得快(树)
题目 古人云:秀恩爱,分得快。 互联网上每天都有大量人发布大量照片,我们通过分析这些照片,可以分析人与人之间的亲密度。如果一张照片上出现了 K 个人,这些人两两间的亲密度就被定义为 1/K。任意两个人如果同时出现在…...

文心一言4.0对比ChatGPT4.0有什么优势?
目录 总结 文心一言4.0的优势 文心一言4.0的劣势 免费分享使用工具 后话 生成式AI的困境 “不会问”“不会用”“不敢信” 为什么要出收费版本? 目前使用过国内的文心一言3.5和WPS AI,国外的ChatGPT4.0。 文心一言和其他国内产品相比࿰…...

美观且可以很方便自定义的MATLAB绘图颜色
函数介绍 主函数是draw_test,用于测试函数。 draw_h是函数,用于给Matlab提供美观且可以很方便自定义的绘图颜色。 draw_h函数介绍 这是一个带输入输出的函数,输入1/2/3,输出下面三种颜色库的配色,每种库均有五种颜色…...

基于jsp,ssm物流快递管理系统
开发工具:eclipse,jdk1.8 服务器:tomcat7.0 数据库:mysql5.7 技术: springspringMVCmybaitsEasyUI 项目包括用户前台和管理后台两部分,功能介绍如下: 一、用户(前台)功能: 用…...

陪诊系统|挂号陪护搭建二开陪诊师入驻就医小程序
我们的陪诊小程序拥有丰富多样的功能,旨在最大程度满足现代人的需求。首先,我们采用了智能排队系统,通过扫描二维码获取排号信息,让您从繁琐的排队过程中解放出来。其次,我们提供了多种支付方式,不仅可以实…...

恒驰服务 | 华为云数据使能专家服务offering之大数据建设
恒驰大数据服务主要针对客户在进行智能数据迁移的过程中,存在业务停机、数据丢失、迁移周期紧张、运维成本高等问题,通过为客户提供迁移调研、方案设计、迁移实施、迁移验收等服务内容,支撑客户实现快速稳定上云,有效降低时间成本…...

轻量级狂雨小说cms系统源码 v1.5.2 基于ThinkPHP5.1+MySQL
轻量级狂雨小说cms系统源码 v1.5.2 基于ThinkPHP5.1MySQL的技术开发 狂雨小说cms提供一个轻量级小说网站解决方案,基于ThinkPHP5.1MySQL的技术开发。 KYXSCMS,灵活,方便,人性化设计简单易用是最大的特色,是快速架设小说类网站首选…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...

华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...

springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...