【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,灵活,方便,人性化设计简单易用是最大的特色,是快速架设小说类网站首选…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
