一个DLL引发的一些列问题

反编译一个exe文件,目的是获取一个签名字符串的实现过程,结果发现该函数调用了以下dll实现。

[DllImport("xx.dll", EntryPoint = "fun1")]
private static extern bool sign(string key, string str, ref int buffer, ref int bufferlen);

首先使用Exeinfo_PE查看该exe是使用什么语言实现的,结果发现是C#实现,然后尝试使用ILSpy反编译该exe文件,万幸没有加壳,得到以上的调用dll的函数。

函数传入的是两个字符串和两个int的引用。尝试使用ILSpy打开xx.dll文件,发现打不开,该dll是C++语言编写的。最终使用IDA工具打开该dll文件,并找到fun1函数如下,翻译为伪代码如下:

int __stdcall fun1(char a1, int a2, int a3, int a4){
    //很复杂的内容.............

本来想将函数翻译成其他语言实现,但是里面的函数之间调用十分复杂,IDA翻译的伪代码可读性太差(其实是自己太菜),就放弃了这个想法。

可以看到IDA反编译的函数形参类型与C#调用时传入的实参类型是不一样的,应该相信谁??自己用C#去写这段代码去调用dll能成功调用,传入其他类型的参数是不行的,那么IDA的伪代码的形参类型是有问题的。

分析C#去调用该函数的实现逻辑:

前两个参数是字符串类型,函数会根据传入的两个字符串生成sign签名

第三个参数是一个int类型的指针,经过函数执行后,该int的值会被修改成sign签名字符串的首字符的内存地址,

第四个参数也是int类型的指针,被执行后被修改成sign签名字符串的长度。

自己尝试使用GO和Python去调用该dll

Go语言调用xx.dll

GO语言调用时主要存在以下难题:

  1. 该函数是没有返回值的,主要靠修改指针指向的值来达到返回值的目的,Go语言中虽然有指针的概念,但是想C++那样操作指针有太多门道。
  2. 如何根据内存地址去正确的取字符串?

找了很多资料写了以下代码:

package main
import (
	"fmt"
	"log"
	"syscall"
	"unsafe"
func main() {
    getSignFromDll("aaaaa", "bbbbbbbbb")
func getSignFromDll(key string, st string) {
	// 需要将字符串转成byte形式传入该dll的函数,不然每次生成的值都一样,,,这里不知道为啥???
	k1 := []byte(key)
	s1 := []byte(st)
	keyP := unsafe.Pointer(&k1)
	stP := unsafe.Pointer(&s1)
	dll := syscall.NewLazyDLL("./ysj.dll")
	sign := dll.NewProc("sub_10001A64") //四个参数:
	var buffer int
	var bufferlen int
	sign.Call(uintptr(keyP), uintptr(stP), uintptr(unsafe.Pointer(&buffer)), uintptr(unsafe.Pointer(&bufferlen)))
	log.Println(buffer)  //这里获取到了内存地址,但是按照一下方法获取的字符串不对,不知道为什么??
	res := UintptrToString(uintptr(buffer), bufferlen)
	log.Println(res)
func UintptrToString(cstr uintptr, length int) string {
	if cstr != 0 {
		us := make([]byte, length)
		for i := 0; i < length; i++ {
			us[i] = *(*byte)(unsafe.Pointer(cstr))
			cstr++
		log.Println(string(*(*byte)(unsafe.Pointer(cstr))))  //正常这里应该是C里面的'\0'
		return string(us)
	return ""

上面的代码尝试做了很多修改,,但还是没法成功获取到想要的字符串。。。。。。。。。。。

Python语言调用xx.dll

Python语言本身是没有指针这个概念的,但是ctype包提供了与C交互的类型转换,,最后也是卡在了内存地址取字符串上面,,,,,,放弃,,,,

使用C++重构dll函数

使用python、go去调该dll都失败了,,我猜测还是那个函数设计得对其他语言不友好,返回一个内存地址,其他语言不好操作。最简单的想法就是自己重新写一个函数,直接返回该字符串,将该函数编译成dll就行。由于C#有现成的实现,编译成dll文件即可,但是发现其他语言去调用C#编译的dll动态库很恶心,非常不方便,最后还是使用C++去写一个,,因为xx.dll就是C++写的。

新建一个动态链接库项目,里面会有pch.h等文件不要删。自己新建一个头文件mydll.h,编写如下:

#include<string>
extern "C" __declspec(dllexport) char* sign(char*, char*);  //__declspec(dllexport)表示这是一个导出函数

新建mydll.cpp,编写如下

#include "pch.h"
#include "mydll.h"
#include <Windows.h>
#include<string>
using namespace std;
// __stdcall一定要加,,不然会有很多奇怪的问题,这个东西也折磨了我很久
typedef int(__stdcall* LOADDLL)(char*, char*, int*, int*);
char res[33];   //必须是全局变量,否则调用返回时有问题
extern "C" __declspec(dllexport) char* sign(char* key, char* st)
	HINSTANCE sdll = LoadLibrary(L"xx.dll");
	LOADDLL signPtr;
	if (sdll != NULL) {
		signPtr = (LOADDLL)GetProcAddress(sdll, "fun1");
		if (signPtr != NULL) {
			int buffer = 0;
			int bufferlen = 0;
			signPtr(key, st, &buffer, &bufferlen);
			if (bufferlen == 32) {
				//读取数据
				char *p = (char*)buffer;
				for (int i = 0; i < 32 && *p; i++) {
					res[i] = *p;
					p++;
				FreeLibrary(sdll);
				return res;
			else {
				FreeLibrary(sdll);
				return nullptr;
		else {
			FreeLibrary(sdll);
			return nullptr;
	else {
		return nullptr;

编写完毕,点击生成编译成dll文件,由于原始的dll是32位的,这里编译时也要使用x86去编译!!

先用C++去测试一下该dll能不能顺利调用:

typedef char* (*LOADDLL)(char*, char*);
int main()
	HMODULE sdll = LoadLibrary("signdll.dll");
	LOADDLL signPtr;
	if (sdll != NULL) {
		signPtr = (LOADDLL)GetProcAddress(sdll, "sign");
		if (signPtr != NULL) {
			char key[] = "11111";
			char st[] = "222222";
			char *p = signPtr(key, st);
			string res;
			for (; *p;) {
				res += *p;
				p++;
			cout << res << endl;
		else {
			cout << "函数载入失败" << endl;
	else {
		cout << "动态库载入失败" << endl;
	FreeLibrary(sdll);

顺利调用!!!打印结果正确。接下来的目标就是使用Python去调用重构的dll文件。

Python调用重构的Dll文件

32位的dll只能32位的python调用,命令行输入python查看python是否是32位。如下图就是32位的python。

如果是64位的python,则需要重新安装一个32位的python。conda是能够支持32位64位之间切换的。

  1. 设置conda为32位环境
set CONDA_FORCE_32BIT=1
  1. 创建一个新的32位的python环境
conda create -n py37x86 python=3.7
  1. 使用时激活这个环境即可
conda activate py37x86

查了一下python与C++之间的类型转换,C++那边用char *的话,python传字符串就OK了。但这里有个坑就是必须使用bytes类型的字符串,不能使用Unicode编码的字符直接传过去,不然每次调用后的结果都一样,,,这个坑也折磨我好久
from ctypes import CDLL,c_char_p
def GetSign(key, st):
    try:
        dll = CDLL("signdll.dll")
        # 获取动态库函数
        signFunc = dll.sign
        # ctype用于python与C++之间类型适配
        signFunc.argtypes = [c_char_p, c_char_p]
        signFunc.restype = c_char_p
        key = bytes(key, encoding="utf-8")
        st = bytes(st, encoding="utf-8")
        res = signFunc(key, st)
        res = bytes.decode(res)
        return res
    except OSError as e:
        print(e, "加载动态库失败")
        return ""
if __name__=="__main__":
    res = GetSign("111", "222")
    print(res)

最终调用成功!!!

高兴得太早

补一下后面的坑。。

测试时调用一次没有问题,但是第二次调用时返回值是None。。。。。。。。。。。

多次尝试后发现是C++里面FreeLibrary(sdll)的原因,个人猜测(瞎猜的)python运行时,虽然我们的代码多次去加载dll,但是python只加载一次,所以C++里面不能释放。注释掉代码重新生成就OK了

一个DLL引发的一些列问题反编译一个exe文件,目的是获取一个签名字符串的实现过程,结果发现该函数调用了以下dll实现。[DllImport("xx.dll", EntryPoint = "fun1")]private static extern bool sign(string key, string str, ref int buffer, ref int bufferlen);首先使用Exeinfo_PE查看该exe是使用什么语言实现的,结果发现是C#实现,然后尝试使用ILSpy反编译该e.
以下为windows系统下的使用         当python使用ctypes模块加载*.dll之后,*.dll会被相关对象的析构函数主动释放,但此释放时机依赖于python内置流程,因此若需要主动释放*.dll,可借助于win32api模块的FreeLibrary(…)函数 【sample】依赖ctypes以及pywin32模块 import ctypes import win32api # 加载*.dll obj_dll
朋友的公司是做GPS的,上周联系到我要帮做个程序把他们平台的车辆定位跟踪数据和省里的平台对接。看一下官方提供的三个文档,洋洋洒洒共一百多页,一大堆协议的定义甚是齐全,好在官方的文件中也带有个封装好通信功能的DLL和一个调用此接口的c++ DEMO程序,既然有现成的可用,那就不必去看他的协议了。 说实话,参加工作之后就基本没用过c++,生疏了。特别是要用c++操作数据库,对我来说比割几刀还...
Python 可以通过使用 C 扩展来调用 C 代码。使用 C 扩展的方式需要在 Python 中编写一些额外的代码,具体方法如下: 1. 写一个 C 扩展模块。这个模块应该包含你想要在 Python调用的 C 函数。 2. 编译这个 C 扩展模块。这一步会生成一个可加载的 Python 模块,这个模块包含你写的 C 代码。 3. 在 Python 代码中使用 import 语句来导入这个模块。这样你就可以在 Python 中使用你写的 C 函数了。 例如,假设你有一个名为 my_module.c 的 C 文件,包含一个名为 my_function 的 C 函数,你可以这样在 Python 中使用它: #include "Python.h" static PyObject * my_function(PyObject *self, PyObject *args) // 你的 C 代码 return Py_BuildValue("i", result); static PyMethodDef MyModuleMethods[] = { {"my_function", my_function, METH_VARARGS, "A C function"}, {NULL, NULL, 0, NULL} static struct PyModuleDef mymodule = { PyModuleDef_HEAD_INIT, "my_module", "A C extension module", MyModuleMethods PyMODINIT_FUNC PyInit_my_module(void) return PyModule_Create(&mymodule); 在 Python 中,你可以这样调用这个 C 函数: ```python import my_module result = my_module.my_function(arg1, arg2)