首页 > 系统相关 >【Python微信机器人】第二篇:将python注入到其他进程

【Python微信机器人】第二篇:将python注入到其他进程

时间:2023-10-23 17:26:36浏览次数:31  
标签:Python 微信 Py dll python 终端 Main

目录修整

目前的系列目录(后面会根据实际情况变动):

  1. 在windows11上编译python
  2. 将python注入到其他进程并运行
  3. 使用C++写一个python的pyd库,用于实现inline hook
  4. Python ctypes库的使用
  5. 使用ctypes主动调用进程内的任意函数
  6. 使用汇编引擎调用进程内的任意函数(为了调用不遵守任何一个调用约定的x86函数)
  7. 注入python到微信实现在Python终端收发消息
  8. 优化使用方法,允许Python加载运行py脚本,而不是打开Python终端
  9. Bug修复和细节优化

注入器

在注入Python之前,先使用Python写一个注入器。原理和C++的远程线程注入是一样的,使用CreateRemoteThread在进程内部调用LoadLibrary加载dll。

核心代码如下:

# 通过微信进程pid获取进程句柄
hProcess = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
# 在微信进程中申请一块内存
lpAddress = VirtualAllocEx(hProcess, None, MAX_PATH, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
# 往内存中写入要注入的dll的绝对路径
WriteProcessMemory(hProcess, lpAddress, c_wchar_p(dllpath), MAX_PATH, byref(c_ulong()))
# 因为注入的微信是32位的,所以运行的Python也需要是32位的
if platform.architecture()[0] != '32bit':
		raise Exception("需要使用32位Python才能正常执行,请更换后重试!")
# 在微信进程内调用LoadLibraryW加载dll
hRemote = CreateRemoteThread(hProcess, None, 0, LoadLibraryW, lpAddress, 0, None)
time.sleep(0.1)
# 关闭句柄
CloseHandle(hProcess)
CloseHandle(hRemote)

其中像OpenProcess这样的函数都是Windows提供的API,在Python中可以使用ctypes库调用它们。完整代码这里就不放了,想了解的可以看后面的github。另外,后面会有一篇文章单独讲ctypes,里面就会涉及调用Windows API和定义C结构体

打包Python为dll

众所周知,Python的核心函数都在python310.dll里,然后再加上pyd、dll和py这些基础库和一些exe可执行文件,python.exe和pythonw.exe只是调用了python310.dll里的导出函数。

1、猜想

既然核心都在python310.dll,我是不是只需要将这个dll注入到其他进程,然后参考python.exe的代码进行初始化就能完成Python的注入。

实际上有一个Python库(Pymem)就是这么干的:Injecting a python interpreter into any process,不过测试下来局限性很大,首先是调用远程函数的API是CreateRemoteThread,它只支持调用的函数传递一个参数,虽然可以是结构体,但是Python构造结构体也不方便,然后是代码比较繁琐,所以还是自己研究了。

先看看python.exe的代码:
1.png

可以看到整个exe只调用了一个函数Py_Main,如果我用C++写一个exe,在里面加载python310.dll,然后调用Py_Main是不是能写一个python.exe

先测试一下,这里我不去引入python的头文件和lib库,而是使用LoadLibraryW加载python310.dll

C++代码如下:

#include <windows.h>
#include <iostream>

int wmain(int argc, wchar_t* argv[]) {
	// 指定要加载的DLL,这里我打算放到同一目录,就不写绝对路径了
	const wchar_t* dllFileName = L"python310.dll";
	// 加载DLL
	HMODULE hModule = LoadLibraryW(dllFileName);
	// 获取导出函数地址
	FARPROC Py_Main_addr = GetProcAddress(hModule, "Py_Main"); // 替换为导出函数的名称
	// 定义函数指针和调用函数
	typedef int(*Py_Main_Type)(int, wchar_t**);
	Py_Main_Type Py_Main = (Py_Main_Type)Py_Main_addr;

	int result = Py_Main(argc, argv);

	return 0;
}

编译后把它放到python310.dll同级目录,然后双击运行,测试发现和python.exe的功能是一模一样的

也支持参数传递和运行py文件
2.png

2、将python.exe打包成dll

因为dll无法传递进程参数,所以我将argv写死了,python310.dll也使用绝对路径,完整代码如下:

#include "pch.h"

void run_python() {
	const wchar_t* dllFileName = L"T:\\Code\\compile_python\\Inject_dll\\python-3.10.11-embed-win32\\python38.dll";
	HMODULE hModule = LoadLibraryW(dllFileName);

	FARPROC Py_Main_addr = GetProcAddress(hModule, "Py_Main");
	typedef int(*Py_Main_Type)(int, wchar_t**);
	Py_Main_Type Py_Main = (Py_Main_Type)Py_Main_addr;

    // 获取当前进程可执行文件的路径
	wchar_t exePath[MAX_PATH];
	GetModuleFileNameW(NULL, exePath, MAX_PATH);

	int argc = 1;
	wchar_t* argv[2];
	argv[0] = exePath;
	argv[1] = nullptr;

	Py_Main(argc, argv);
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
	case DLL_PROCESS_ATTACH: {
		run_python();
	}
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

当我将编译好的dll注入到其他进程内却什么也没有发生,为了不受其他进程影响,我先把他注入到我自己写的进程里,就用之前帖子写的SubProcess了。

3、无法运行的原因

先验证下python有没有运行成功,调用PyRun_SimpleString来执行代码写文件测试看看

将上面的Py_Main(argc, argv);换成PyRun_SimpleString("with open('D:\\\\1.txt', 'w') as f: f.write('aaaaa')");

文件并没有被写到指定的路径,谷歌搜索了下,是因为没有调用Py_InitializeEx或Py_Initialize初始化,加上下面两行代码就可以了。

Py_SetProgramName(exePath);
Py_InitializeEx(1);
PyRun_SimpleString("with open('D:\\\\1.txt', 'w') as f: f.write('aaaaa')");

这时候交互式终端也正常打开
3.png

4、其他进程交互式终端无法打开

我用同样的dll注入到微信时,界面没有什么变化,但是文件正常写到指定路径,说明Python执行没有问题。猜想可能是Py_Main不会自己打开终端,因为之前的进程本来就是控制台程序,注入之后能看到Python输出再控制台。而微信没有控制台,所以没办法看到。

那么看看能不能打开进程的控制台,搜索发现还真有这样的函数,一行代码即可AllocConsole();,另外还需要三行代码来将Python的输入输出重定向到这个控制台,不然还是看不到Python的输出和交互式终端

freopen("CONIN$", "r+t", stdin);
freopen("CONOUT$", "w+t", stdout);
freopen("CONOUT$", "w+t", stderr);

这样需要添加一个宏来允许不安全的函数,或者用下面的代码

FILE* conin = stdin;
FILE* conout = stdout;
FILE* conerr = stderr;
freopen_s(&conin, "conin$", "r+t", stdin);
freopen_s(&conout, "conout$", "w+t", stdout);
freopen_s(&conout, "conout$", "w+t", stderr);

这样就能在注入dll后打开Python的交互式终端,查看进程id也是注入进程的id,到此本篇文章的目的已经达到了。
4.png
这里有个特别无语的坑,freopen函数无法在Debug模式下生效,也就是说如果你dll编译成Debug的话,能打开终端,但是看不到Python输出和交互式终端,为此我还谷歌了搜了一天,以为是freopen没效果。

5、意外发现一些没什么用的知识

在知道上面不显示交互式终端是因为Debug模式不生效之前,我还尝试了一些操作,比如这篇

PyObject* sys = PyImport_ImportModule("sys");
PyObject* io = PyImport_ImportModule("io");
PyObject* pystdout = PyObject_CallMethod(io, "open", "ss", "CONOUT$", "wt");
if (-1 == PyObject_SetAttrString(sys, "stdout", pystdout)) {
    /* Announce your error to the world */
}
Py_DECREF(sys);
Py_DECREF(io);
Py_DECREF(pystdout);

如果不想去Python源码里拷贝PyObject的定义,可以直接定义成void *,然后定义PyObject_SetAttrString函数指针时参数也是void*类型的就可以

改完以后的代码(Py_DECREF不是导出函数,调用起来比较麻烦就不调了):

void* sys = PyImport_ImportModule("sys");
void* io = PyImport_ImportModule("io");
void* pystdout = PyObject_CallMethod(io, "open", "ss", "CONOUT$", "wt");
if (-1 == PyObject_SetAttrString(sys, "stdout", pystdout)) {
}

在c代码中设置sys.stdout来将Python输出打印到终端,测试是可以将print显示到终端的。上面的代码应该和freopen("CONOUT$", "w+t", stdout);是一样的,所以只要在加上设置stdin和stderr的就能打开交互式终端。这里我就不测试了。

另外,我还尝试了多种打开终端的方式

  1. python -i test.py: 在执行python脚本时,可以指定-i选项进入交互式终端,当然也可以不指定脚本用python -i,见这篇
    大概意思是,Python只有在启动是控制台应用时才会打开终端,必须指定-i选项强制打开终端
  2. import code;code.interact(): 这两行代码也能打开交互式终端,可以在代码任意位置插入,就能访问当前的变量
  3. code.InteractiveConsole(locals()).interact(): 这个和上面的一样,只是添加到locals里的变量到终端
  4. import os;os.environ["PYTHONINSPECT"] = "1": 可以通过设置PYTHONINSPECT这个环境变量来控制python打开交互式终端,不一样要代码设置,在系统添加这个环境变量也可以

静态链接

上面是通过LoadLibraryW来动态加载Python的dll,这里我测试下通过lib库和头文件来链接Python。这样做的优点是可以不用写函数指针这种,少了很多代码。

代码如下:

#include "pch.h"
#include "Python.h"
#include <iostream>

DWORD WINAPI run_python(PVOID pParam) {
	AllocConsole();
	freopen("CONIN$", "r+t", stdin);
	freopen("CONOUT$", "w+t", stdout);
	freopen("CONOUT$", "w+t", stderr);

	wchar_t exePath[MAX_PATH];
	GetModuleFileNameW(NULL, exePath, MAX_PATH);

	int argc = 1;
	wchar_t* argv[2];
	argv[0] = exePath;
	argv[1] = nullptr;

	Py_SetProgramName(exePath);
	Py_InitializeEx(1);
	
	PyRun_SimpleString("print('hello')");

	Py_Main(argc, argv);
	return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
	case DLL_PROCESS_ATTACH: {
		CreateThread(NULL, 0, run_python, NULL, 0, NULL);
	}
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

另外还需要添加Python的头文件库和lib库
5.png
6.png

这里又出现个比较麻烦的问题,编译后的dll和python里的所有文件都得放到被注入进程的目录才能生效,这个应该是跟Windows下dll搜索路径有关。对Windows编程不是很熟悉,搜了下也没找到解决办法,有知道的大佬请指教下

在功能上和上面使用LoadLibraryW基本一样,所以这样写会简洁很多,不用定义那么多的函数指针和类型

以上用到的所有代码

https://github.com/kanadeblisst00/PyRobot-part2

标签:Python,微信,Py,dll,python,终端,Main
From: https://www.cnblogs.com/kanadeblisst/p/17782962.html

相关文章

  • Python生成器
    Python生成器1、什么是生成器根据程序员制定的规则循环生成数据,当规则不成立时则生成数据结束。数据不是一次性全部生成出来的,而是使用一个,再生成一个,可以节约大量的内存。创建生成器的方式:①生成器推导式②yield关键字2、生成器推导式#创建生成器#生成器推导式=》(......
  • Python:深拷贝与浅拷贝
    python:深拷贝与浅拷贝一、了解几个概念变量:是一个系统表的元素,拥有指向对象的连接空间对象:被分配的一块内存,存储所代表的值引用:是自动形成的从变量到对象的指针类型:属于对象,而非变量不可变对象:一旦创建就不可修改的对象(值内存地址固定后不可以再修改其值),包括字符......
  • 栩栩如生,音色克隆,Bert-vits2文字转语音打造鬼畜视频实践(Python3.10)
    诸公可知目前最牛逼的TTS免费开源项目是哪一个?没错,是Bert-vits2,没有之一。它是在本来已经极其强大的Vits项目中融入了Bert大模型,基本上解决了VITS的语气韵律问题,在效果非常出色的情况下训练的成本开销普通人也完全可以接受。BERT的核心思想是通过在大规模文本语料上进行无监督预......
  • python通过脚本路径获取对应脚本里的内容
    importinspectfromimportlib.utilimportspec_from_file_location,module_from_specscript_path="test.py"spec=spec_from_file_location("test",script_path)module=module_from_spec(spec)spec.loader.exec_module(module)print(modul......
  • python 计算指定日期是今年的第几周和这个月的第几周
    Python当前时间是一年中第几周_python计算一年的第几周-CSDN博客以上感觉可能索引是从0开始ISO8601每个日历星期从星期一开始,星期日为第7天。第一个日历星期有以下四种等效说法:1,本年度第一个星期四所在的星期;2,1月4日所在的星期;3,本年度第一个至少有4天在同一星期内的星......
  • 《安富莱嵌入式周报》第321期:开源12导联便携心电仪,PCB AI设计,150M示波器差分探头,谷歌
     视频版:https://www.bilibili.com/video/BV1ju4y1D7A8/1、开源12导联便携心电仪https://voltagedivide.com/2017/10/14/psoc-design-and-implementation-of-a-12-lead-portable-ecg/这个开源有完整的上位机,下位机和原理图,并且有一个详细的设计论文。12导联心电图是心电图检查中常......
  • Python工具箱系列(四十四)
    使用py7zr对目录与文件进行压缩打包 7z是一种主流高效的压缩格式,它拥有极高的压缩比。在计算机科学中,7z是一种可以使用多种压缩算法进行数据压缩的档案格式。该格式最初被7-Zip实现并采用,但是这种档案格式是公有的,并且7-Zip软件本身亦在GNU宽通用公共许可证(GNULGPL)协议下开......
  • ubuntu20.04下源码编译python 3.12
    需要注意的地方 1.安装依赖:https://devguide.python.org/getting-started/setup-building/#build-dependenciessudoapt-getinstallbuild-essentialgdblcovpkg-config\libbz2-devlibffi-devlibgdbm-devlibgdbm-compat-devliblzma-dev\libnc......
  • 图书推荐与管理系统Python+协同过滤推荐算法+Django网页界面
    一、介绍图书管理与推荐系统。使用Python作为主要开发语言。前端采用HTML、CSS、BootStrap等技术搭建界面结构,后端采用Django作为逻辑处理,通过Ajax等技术实现数据交互通信。在图书推荐方面使用经典的协同过滤算法作为推荐算法模块。主要功能有:角色分为普通用户和管理员普通用户可注......
  • [924] f-strings in Python
    ref:f-stringsinPythonref:Python'sF-StringforStringInterpolationandFormattingF-strings,alsoknownasformattedstringliterals,areafeatureintroducedinPython3.6thatprovideaconciseandconvenientwaytoembedexpressionsinside......