1. 首页 > 手游攻略

C c 获取cpu型号

作者:admin 更新时间:2024-11-28
摘要:一、C 语言中怎么编写 DLL 文件? 如果会用Cmake工具的话那就超级简单了。参考附件中的例子简单修改即可。 有几个关键点要说明一下 CMake中要是用命令: ADD_LIBRARY (${PROJECT_NAME} SHARED,C c 获取cpu型号

 

一、C 语言中怎么编写 DLL 文件?

如果会用Cmake工具的话那就超级简单了。参考附件中的例子简单修改即可。

有几个关键点要说明一下

  1. CMake中要是用命令: ADD_LIBRARY(${PROJECT_NAME} SHARED

>

#指定cmake的最小版本号
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)

#指定项目名称
PROJECT(Sample LANGUAGES CXX)

add_compile_options(/DBUILD_DLL)

#将各种文件归类到指定变量
FILE(GLOB_RECURSE HEADER_FILES "*.h" "*.hpp")
FILE(GLOB_RECURSE SRC_FILES "*.c" "*.cpp" "*.cxx")

# 将工程编译成 DLL ( SHARED -> DLL, STATIC -> LIB)
ADD_LIBRARY(${PROJECT_NAME}  SHARED 
    ${HEADER_FILES}  ${SRC_FILES} 
)
# [EOF]

2. 生成动态库的时候,要定义宏常量 add_compile_options(/DBUILD_DLL)

3. 引用动态库的时候,则不需要定义 宏常量 BUILD_DLL

CMakelists.txtBuild_BY_Cmake.batSample.cppSample.h

这个例子编译完之后,用depends工具查看的效果如下图:

二、进程注入的dll文件该怎么编写?

用了半个暑假学C++,hook WX,成功调用发送消息call,接收消息响应call。在B站参考了Hezone和Hellmessage两位大神的视频,虽然WX更新较快,汇编代码已经面目全非,但思路很有启发性。写篇文章记下实现过程,方便日后复习。

用到的工具有:visual studio,cheat engine,ollydbg。

写一个DLL

这个DLL相当于我们的间谍,潜入WX内部,然后和外部主程序通信进行相应操作。

  1. 打开VS,创建新项目,选择”动态链接库“

创建了一个名为Dll1的DLL,dllmain.cpp里有vs为我们准备好的DLL程序入口。

我们的执行的代码要在DLL_PROCESS_ATTACH下面写。

>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        /*我们的代码,注入DLL后会执行*/
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

先打开头文件pch.h,然后写上

>

#include <windows.h>

再回到dllmain,加一个弹窗。

>

case DLL_PROCESS_ATTACH:
        /*我们的代码,注入DLL后会执行*/
        MessageBox(0,L"成功潜入微信内部!",L"报告主人",NULL);

按F5生成。

打开项目目录下的Debug文件夹,我们可以看到我们生成的Dll1.dll和Dll1.pdb。

DLL注入程序

写完简单的DLL,然后写一个Inject DLL 注入程序(exe),用远程线程注入的方法,把自己的DLL注入到WX进程。

我们再创建一个新项目,选择“控制台程序”,将项目命名为Inject。

方法是先通过WX程序的Pid,调用OpenProcess用可获得的所有权限打开进程,再用VirtualAllocEx在WX进程的地址空间开辟一块内存,调用WriteProcessMemory把我们写的DLL的路径写到WX的内存里。[1]

我们要想办法创建一个远程线程(CreateRemoteThreadEx),让线程执行“LoadLibrary"让微信载入我们的DLL。我们不能直接&LoadLibrary取到函数地址然后传过去让线程执行,因为我们的注入程序的LoadLibrary地址和WX地址空间的LoadLibrary地址不一致。所以我们要用GetProcAddress获取LoadLibrary的地址。

完整代码如下:

>

int main() {
	DWORD pid = 0;
        scanf("%d",pid);
	
	HANDLE hprocess = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
	if (!hprocess) {
		std::cout << "can not get handle" << std::endl;
		return 1;
	}
	SIZE_T PathSize = (strlen(DLLPath) + 1) * sizeof(TCHAR);
	LPVOID StartAddress = VirtualAllocEx(hprocess, NULL, PathSize, MEM_COMMIT, PAGE_READWRITE);
	if (!StartAddress) {
		std::cout << "开辟内存失败" << std::endl;
		return 1;
	}
	if (!WriteProcessMemory(hprocess, StartAddress, DLLPath, PathSize, NULL)) {
		std::cout << "无法写入DLL路径" << std::endl;
		return 1;
	}
	PTHREAD_START_ROUTINE pfnStartAddress = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "LoadLibraryA");
	if (!pfnStartAddress) {
		std::cout << "无法获取函数地址" << std::endl;
		return 1;
	}
	HANDLE hThread = CreateRemoteThreadEx(hprocess, NULL, NULL, pfnStartAddress, StartAddress, NULL, NULL, NULL);
	if (!hThread) {
		std::cout << "创建线程失败" << std::endl;
		return 1;
	}
	//WaitForSingleObject(hThread, INFINITE);//等待DLL结束
	std::cout << "注入成功!\n";
	CloseHandle(hThread);
	CloseHandle(hprocess);
	return 0;
}

然后生成。

登录WX,我们再在cmd输入tasklist,找到WeChat.exe,这个WX的主进程,记下PID,之后运行我我们写的Inject.exe,输入pid。

成功显示,任务栏下方还光明正大的表示成WX的图标,真是血统正宗!


还是来更新一下,做个预告吧。

上面我们让我们的DLL注入到WX里了,这相当于我们光明正大的闯进了他的房子,可以为所欲为!

是的,我们的DLL可以像操作自己的内存一样,读取修改WX的内存,甚至调用和修改他的执行代码!

接下来会以获取到用户信息,如昵称,WX号,电话号码,头像等,讲怎么读取他的内存。

经评论区知友好心提醒,怕被抓,所以#define WX 微信

我又回来啦!

DLL读取用户信息

登录微信后,打开CE,添加微信。

将Value Type改成String,然后输入我们的微信号,一定要一字符不差。

然后first scan扫描,会发现两个绿色的地址。绿色就代表着这个地址的相对位置是不变的,因为我们的微信号、手机号这些,登录之后就不会变化,所以这两个地址符合我们的条件。把他们添加到下栏。

鼠标悬停在上面可以看到,如果你的绿色地址和我一样是一个dll加一个十六进制数,那么后面的0x1D29B60就是我们要找的偏移量。我们可以用WeChatWin.dll基址加上偏移量,就能得出每次运行微信后,微信号的地址。

如果你只是个绿色的地址,那就点击add address manually,输入WeChatWin.dll,然后OK,下栏会多出一个以4个0结尾的地址,这就是这次启动微信WeChatWin.dll的值。

打开计算器,选程序员模式,换到十六进制,用刚刚的一个绿色地址和这个值相减,得出的数值便是偏移。

得到偏移后,找个踏实的地方记下来,以免丢失,又要重新来过。可以直接define到我们的DLL源码里。

用同样方法可以找出微信昵称和电话号码、微信头像什么的。

所以,要获取微信号的地址,只要获取WeChatWin模块基址和偏移然后相加,我们已经找到偏移,怎么让dll知道基址呢?

因为这会我们的DLL已经注入进去,是和WeChatWin.dll等同属一个进程,所以我们可以直接调用GetModuleHandle:

>

GetModuleHandle(L"WeChatWin.dll");

为了方便易理解,我们封装成函数:

>

HMODULE GetWechatWin() {
    return GetModuleHandle(L"WeChatWin.dll");
}

现在就可以计算出微信号的地址:

>

pwxid = (DWORD)hModule + WXIDOFFSET;
pphone = (DWORD)hModule + PHONEOFFSET;

然后难点来了,虽然得到了地址,但它是一串DWORD,怎么转换成指针呢。

因为我是从Python入门,没有强制转换这个概念,所以脑筋转不过来。

>

wxid = (char*)pwxid;
phone = (char*)pphone;

把DWORD强制转换成一个指向char字符串的指针,我们真正就得到了微信号。

来,开个弹窗看看找没找对:

>

MessageBoxA(0,wxid,0,0);

对了,忘了说,前面wxid和Phone两个char*不是要先定义再使用的嘛,但是呢你在case下面定义,vs就会提示说wxid的定义被跳过,所以这些数据要在switch外定义好,你可以在DllMain函数下面定义初始化,也可以全局。

接下来会说下发送消息。

我一开始是先选发送消息的,因为看着汇编代码比较简单。但是写好了注入进去之后调用call,不是让微信异常终止,就是没错误但调用不了,用OD和VS都调试不出个所以然,所以就暂时放弃,又去hook接收消息,虽然复杂,但好理解,最后成功了。然后回过头来重捋一遍发送消息,才搞定,但始终没清楚一开始为什么会不行,是什么问题导致的。

但能用就行了,管他呢。

之后的一些内容可能会分开几篇文章来写,全堆到这一篇的话,又难维护又难阅读。


Dll1发送消息函数

下面这篇是发送消息的分析教程:

C++ DLL注入微信实现自动接收、发送消息之调用发送call

Python获取用户信息

为了让python调用,实现注入DLL,我又新建了一个DLL项目名为InjectDll,把前面的DLL注入程序(Inject.exe)的代码复制过来,然后让Python ctypes载入InjectDll就可以注入,实现Python和注入进WX的DLL沟通。

然而一大堆的函数,复制来还得重新调整,因为数据类型问题很难调用,所以我就放弃了。

原本最长的注入函数,被我改成了:

>

void injectdll() {




    system("D:\\vsproject\\Inject\\Debug\\Inject.exe");
}

或许这才是真正的优雅。

内存映射传递数据

重点是通信,管他是怎么注入的,进去了就行。

然后用内存映射文件让里面的DLL把用户信息传出来。

在Dll1里新建一个cpp源文件,名为shareMsg.cpp[2]。函数有:

>

int createMapping(PCTSTR pszPathname,PTSTR mapname) {
    
    HANDLE s_hFileMap = CreateFileMapping(pszPathname == NULL ? INVALID_HANDLE_VALUE : CreateFile(pszPathname, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL), NULL, PAGE_READWRITE, 0, 8 * 1024, mapname);
    if (s_hFileMap != NULL) {
        if (GetLastError() == ERROR_ALREADY_EXISTS) {
            //MessageBox(NULL, L"Mapping already exists - not created.", 0, 0);
            CloseHandle(s_hFileMap);
            return 1;
        }
        else {
            PVOID pView = MapViewOfFile(s_hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
            if (pView != NULL) {
                
                UnmapViewOfFile(pView);
                return 0;
            }
            else {
                //MessageBox(NULL, L"Can't map view of file.",0,0);
                return 2;
            }
        }
    }
    else {
        //MessageBox(NULL, L"Can't create file mapping.", 0, 0);
        return 3;
    }
}

创建Mapping,如果没有文件名会用-1。文件创建的比较大,因为wx的一些特殊消息比如动画表情,图片,链接等,是以xml的形式呈现的。

>

int share(PTSTR mapname,PTSTR source, rsize_t sourceSize) {
    HANDLE hFileMapT = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, mapname);
    if (hFileMapT != NULL) {
        PVOID pView = MapViewOfFile(hFileMapT, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
        if (pView != NULL) {
            wchar_t null[0x1000] = { 0 };
            wmemcpy_s((PTSTR)pView, 8 * 1024, null, 0x1000);
            wmemcpy_s((PTSTR)pView, 8 * 1024, source, sourceSize);
            UnmapViewOfFile(pView);
            return 0;
        }
        else { //MessageBox(NULL, L"can't map view.", 0, 0); 
            return 1;
        }
        CloseHandle(hFileMapT);
    }
    else {
        //MessageBox(NULL, L"can't open mapping.", 0, 0);
        return 2;
    }

}

写入mapping。

>

PTSTR readMapping(PCTSTR mapname, PTSTR buff, const rsize_t buffSize) {
    HANDLE hFileMapT = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, mapname);
    if (hFileMapT != NULL) {
        PVOID pView = MapViewOfFile(hFileMapT, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
        if (pView != NULL) {
            rsize_t pViewSize = _tcslen((PTSTR)pView);

            wmemcpy_s(buff, buffSize, (PTSTR)pView, buffSize > pViewSize ? pViewSize + 1 : buffSize);
            UnmapViewOfFile(pView);
            return buff;
        }
        else { //MessageBox(NULL, L"can't map view.", 0, 0); 
            return NULL;
        }
        CloseHandle(hFileMapT);
    }
    else {
        //MessageBox(NULL, L"can't open mapping.", 0, 0);
        return NULL;
    }
}

读Mapping,虽然没用到,但还是写出来吧。

好,然后在DllMain处,createmapping.

>

createMapping(L"C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python38\\wechat\\userdata.dat", (PTSTR)L"UserData");

因为要传递用户信息,和接收消息内容,所以要创建多个内存映射,第一个参数最好是个数据文件,而不要写NULL。

然后拼接用户信息:

>

        strcat_s(userdata, 0x40, wxid);
        strcat_s(userdata, 0x40, "\n");
        strcat_s(userdata, 0x40, phone);

然后分享:

>

        if (share((PTSTR)L"UserData", (PTSTR)userdata, 0x40) != 0)
            MessageBox(0, L"userdata lose", 0, 0);

写入方准备好了,之后是Python的读取。

在InjectDll里加上函数:

>

LPSTR readMappingA(LPCSTR mapname, LPSTR buff, const rsize_t buffSize) {
    HANDLE hFileMapT = OpenFileMappingA(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, mapname);
    if (hFileMapT != NULL) {
        PVOID pView = MapViewOfFile(hFileMapT, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
        if (pView != NULL) {
            rsize_t pViewSize = strlen((LPSTR)pView);

            memcpy_s(buff, buffSize, (LPSTR)pView, buffSize > pViewSize ? pViewSize + 1 : buffSize);
            UnmapViewOfFile(pView);
            return buff;
        }
        else { //MessageBox(NULL, L"can't map view.", 0, 0); 
            return NULL;
        }
        CloseHandle(hFileMapT);
    }
    else {
        //MessageBox(NULL, L"can't open mapping.", 0, 0);
        return NULL;
    }
}

让其可以导出:

>

    extern "C"{
__declspec(dllexport) LPSTR readMappingA(LPCSTR mapname, LPSTR buff, const rsize_t buffSize);
}

创建一个Python项目,名为wechat。

在wechat.py中,

注入DLL:

>

from ctypes import *
inject_dll = windll.LoadLibrary("D:\\vsproject\\InjectDll\\x64\\Debug\\InjectDll.dll")

获取用户信息的函数:

>

    def getuserdata(self):
        inject_dll.readMappingA.restype = c_char_p
        buff = create_string_buffer(1000)

        x=inject_dll.readMappingA(c_char_p(bytes("UserData","utf-8")),buff,len(buff))
        #print(x)
        x=x.decode('utf-8').split('\n')
        return x


Py与Dll1管道通信

在发送消息这篇中,我们在DLL内部实现了一个发送WX消息的函数

>

void SendWechatMessage(wchar_t* wxid, wchar_t* msg);

在DLL内部调用就能发送消息。但是怎么让外部程序调用?把DLL注入进去之后,就不和我们一个进程了,我们调用不了DLL里面的函数。

所以只能靠通信了,让DLL进去后开个循环,一直监听我们的消息,一有消息就执行相应操作。去网上查了查进程间通信的方式,有内存映射文件,信号,管道,套接字等,粗看觉得管道最直接简单,就选了命名管道。

觉得管道比较适合面向对象编程,本来我的代码就已经够乱了,不能再乱下去,顺便学习下C++ class,就打算写个pipe类。

去MSDN翻了翻,发现有CreateNamedPipe创建管道的函数,有WaitNamedPipe等待,和ConnectNamedPipe连接。

所以分两个类来写吧,服务器和客户端。

>

class Serverpipe {
public:
    HANDLE h_Pipe;
    bool create() {
        h_Pipe = CreateNamedPipe(
            TEXT("\\\\.\\Pipe\\mypipe"),						//管道名字
            PIPE_ACCESS_DUPLEX,									//管道类型
            PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,	//管道参数
            PIPE_UNLIMITED_INSTANCES,							//管道能创建的最大实例数量
            0,													//输出缓冲区的长度 0表示默认
            0,													//输入缓冲区的长度 0表示默认
            NMPWAIT_WAIT_FOREVER/*NMPWAIT_USE_DEFAULT_WAIT*/,								//超时时间
            NULL);
        if (h_Pipe == INVALID_HANDLE_VALUE)
        {
            MessageBox(0, L"Failed to CreateNamedPipe!", 0, 0);
            return false;

        }
        return true;
    }
    DWORD connect() {
        if (ConnectNamedPipe(h_Pipe, NULL) == NULL)				//连接,等待
        {
            if (GetLastError() == ERROR_PIPE_CONNECTED) {
                //MessageBox(0, L"我还没准备好,先别进来!", 0, 0);
                return ERROR_PIPE_CONNECTED;
            }
            MessageBox(0, L"Failed to Connect!", 0, 0);
            return FALSE;

        }
        else
        {
            return TRUE;
        }
    }
    DWORD read(wchar_t* buff,DWORD nNumberOfBytesToWrite) {
        DWORD len = 0;
        if (ReadFile(h_Pipe, buff, nNumberOfBytesToWrite, &len, NULL) == FALSE)	//接收客户端
        {
            //wchar_t error[100];
            //swprintf_s(error, 100, L"错误代码为0x%x", GetLastError());
            //MessageBox(0, error, L"Failed to read data!", 0);
            return FALSE/*GetLastError()*/;

        }
        return len;
    }
    DWORD write(wchar_t* buff,DWORD nNumberOfBytesToWrite) {
        DWORD len_ = 0;
        WriteFile(h_Pipe, buff, nNumberOfBytesToWrite, &len_, 0);			//向客户端发送内容}
        return len_;
    }
    DWORD close() {
        return CloseHandle(h_Pipe);
    }

};

现在就可以在dllmain.cpp中,创建一个实例。

>

Serverpipe pipe;

然后,

>

wchar_t* senddata = new wchar_t[0x3000];
wchar_t seps[] = L"|cut|";
wchar_t* token1 = NULL;
wchar_t* token2 = NULL;
wchar_t* next_token1 = NULL;
wchar_t* next_token2 = NULL;
wchar_t Wxid[20] = { 0 };
wchar_t *Msg = new wchar_t[0x3000];

因为要传两个参数,发送目标和消息内容,所以读取管道内容后,以"|cut|"为分割标记,用strok截断字符串,标记之前作为发送目标,标记之后作为发送内容,也算是一种解析吧。[3]

在DllMain里,

>

pipe.create();
if(pipe.connect()!=0){
    while (true) {
        memset(senddata, '\0', 0x3000);
        while (pipe.read(senddata, 0x3000) == 0) {}
        _tcscpy_s(Wxid, 20, _tcstok_s(senddata, seps, &next_token1));
        _tcscpy_s(Msg, 0x3000, _tcstok_s(NULL, seps, &next_token1));
        MessageBox(0, Msg, Wxid, 0);
        SendWechatMessage(Wxid, Msg);
    }
}

然后我们再给InjectDll写上客户端pipe。

>

class Clientpipe{
public:
     HANDLE h_Pipe;
    
     DWORD connect() {
        bool bRet = WaitNamedPipe(TEXT("\\\\.\\Pipe\\mypipe"), NMPWAIT_WAIT_FOREVER);

        if (!bRet)
        {
            MessageBox(0, L"Failed to connect pipeline!", 0, 0);
            return -1;
        }

         h_Pipe = CreateFile(						//管道属于一种特殊的文件
            TEXT("\\\\.\\Pipe\\mypipe"),				//文件名字
            GENERIC_READ | GENERIC_WRITE,				//文件模式
            0,											//是否共享
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,						//文件属性(只读,默认...)NORMAL 为默认属性
            NULL);

        if (h_Pipe == INVALID_HANDLE_VALUE)
        {
            //MessageBox(0, L"Failed to CreateFile pipeline file!", 0, 0);
            return FALSE;
        }
        return TRUE;
    }
      DWORD read(wchar_t* buff, DWORD nNumberOfBytesToWrite) {
        DWORD len = 0;
        if (ReadFile(h_Pipe, buff, nNumberOfBytesToWrite, &len, NULL) == FALSE)	//接收客户端发送的内容
        {

            //wchar_t error[100];
            //swprintf_s(error, 100, L"错误代码为0x%x", GetLastError());
            //MessageBox(0, error, L"Failed to read data!", 0);
            return GetLastError();

        }
        return len;
    }
     DWORD write(wchar_t* buff, DWORD nNumberOfBytesToWrite) {
        DWORD len_ = 0;
        if (WriteFile(h_Pipe, buff, nNumberOfBytesToWrite, &len_, 0) == FALSE)
            return GetLastError();//向客户端发送内容}
        return len_;
    }
};

生成后,给Python导入,却发现Python貌似根本用不了这个C++类诶。。。

几番折腾还是导出不了,

见知乎上有大佬直接写两个函数,一个创建类实例并返回指针,一个销毁。然后导出两个函数。

我就干脆把class去掉了。

>

HANDLE h_Pipe;
    
     DWORD connect() {
        bool bRet = WaitNamedPipe(TEXT("\\\\.\\Pipe\\mypipe"), NMPWAIT_WAIT_FOREVER);

        if (!bRet)
        {
            MessageBox(0, L"Failed to connect pipeline!", 0, 0);
            return -1;
        }

         h_Pipe = CreateFile(						//管道属于一种特殊的文件
            TEXT("\\\\.\\Pipe\\mypipe"),				//文件名字
            GENERIC_READ | GENERIC_WRITE,				//文件模式
            0,											//是否共享
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,						//文件属性(只读,默认...)NORMAL 为默认属性
            NULL);

        if (h_Pipe == INVALID_HANDLE_VALUE)
        {
            //MessageBox(0, L"Failed to CreateFile pipeline file!", 0, 0);
            return FALSE;
        }
        return TRUE;
    }
      DWORD read(wchar_t* buff, DWORD nNumberOfBytesToWrite) {
        DWORD len = 0;
        if (ReadFile(h_Pipe, buff, nNumberOfBytesToWrite, &len, NULL) == FALSE)	//接收客户端发送的内容
        {

            //wchar_t error[100];
            //swprintf_s(error, 100, L"错误代码为0x%x", GetLastError());
            //MessageBox(0, error, L"Failed to read data!", 0);
            return GetLastError();

        }
        return len;
    }
     DWORD write(wchar_t* buff, DWORD nNumberOfBytesToWrite) {
        DWORD len_ = 0;
        if (WriteFile(h_Pipe, buff, nNumberOfBytesToWrite, &len_, 0) == FALSE)
            return GetLastError();//向客户端发送内容}
        return len_;
    }

让类方法变成全局函数。

然后逐个导出:

>

extern "C"{    
    __declspec(dllexport) DWORD connect();
    __declspec(dllexport) DWORD read(wchar_t* buff, DWORD nNumberOfBytesToWrite);
    __declspec(dllexport) DWORD write(wchar_t* buff, DWORD nNumberOfBytesToWrite);
}

OK,搞定。

Python发送消息函数

回到wechat.py,写一个发送消息的函数:

>

    def send(self,msg,wxid=None,name=None,findtime=1.8):

        if wxid:
            msg=wxid+'|cut|'+msg
            buff = create_unicode_buffer(msg)
            x=inject_dll.write(buff,len(buff)*2)
        elif name:
            self.find(name)
            self.write(msg)
            pag.hotkey('alt', 's')
            return True

        return x

如果传入了wxid也就是发送目标,就会构造发送目标+发送内容的字符串,然后写入管道,管道对面的Dll1监听到后立马进行发送操作,函数最后返回写入管道的字节数。如果只输入了好友昵称,就搜索找到对应好友聊天框,发送窗口消息+模拟按键发送。

多线程解决无响应

于是,感觉大功告成的我激动地打开IDLE,调用send函数,然而wx却是

无 响 应 。。

这当头一棒,打得我措手不及。

回想刚写的代码,就只有管道和接收消息循环。管道是不会出什么大问题的,那肯定是Dll1里的循环了。当时想着,写下无限循环,最多卡着当前的线程,不会影响到整个进程,其他GUI组件应该还能用。或许真有问题吧。

于是我回到Dll1,把最后的while循环整个包装入一个函数里,

>

void waitmsg() {
    while (true) {
        memset(senddata, '\0', 0x3000);
        while (pipe.read(senddata, 0x3000) == 0) {}
        _tcscpy_s(Wxid, 20, _tcstok_s(senddata, seps, &next_token1));
        _tcscpy_s(Msg, 0x3000, _tcstok_s(NULL, seps, &next_token1));
        MessageBox(0, Msg, Wxid, 0);
        SendWechatMessage(Wxid, Msg);
    }
}

然后在DllMain调用此函数。

重启wx,ctrl+F6 IDLE,再send。

诶哟,成功发送了!

来让我再发几条~

第二次调用send,WX又卡死了。。

看来要不卡死,只能并发了。

>

#include <thread>

>

thread th(waitmsg);

创建一个线程th,线程执行函数waitmsg。

把DllMain最后的

>

waitmsg();

改为

>

thread th(waitmsg);

这样,到最后,就会创建新线程,单独执行while循环,这样就不会影响主线程了。

重新生成,VS却提示“th的初始化被标签case跳过”。

可是这个线程实例,我什么时候初始化它,它就什么时候开始执行,你不给我在里面初始化,那我只能在外部提前初始化,管道没连接就开始了监听,肯定会引发错误啊。

不管了,我非要在里面初始化类实例。于是我又写了一个函数,

>

void _th() {
    thread th(waitmsg);
}

然后在DllMain里的最后改为

>

_th();

生成,vs不报错了。

但是想到在函数里创建的变量什么的,函数执行完毕会被销毁哦,保险起见,加个static

>

void _th() {
    static thread th(waitmsg);
}

OK。再次打开IDLE,发送消息,成了!

成了!

成了!

至此,终于封装好了发送wx消息的Python函数。

Dll1接收消息Hook

下面是HOOK接收消息

C++ DLL注入微信hook实现自动接收消息

具体的找函数和写代码hook已经单独写在上文。

Python获取新消息

在接收消息那篇文章中,我们写了响应函数,一有消息就会内存映射并弹窗显示,现在Python只需要读内存映射就行了。

在我们写的InjectDll中有读取内存映射文件的函数readMapping,我们上面已经让Python导入了InjectDll,现在就用它的函数。

这里涉及到ctypes库,比较繁杂,建议先看一下文档,再继续。

>

inject_dll.readMapping.restype = c_wchar_p
buff = create_unicode_buffer(5000)
msg = inject_dll.readMapping("NewMessageLog",buff,5000)

设置readMapping函数的返回值类型为cwchar指针,然后创建buff传参。这里的buff相当于c++ 的

>

wchar_t buff[5000]={0};

包装成函数:

>

def getmsg(self):
        inject_dll.readMapping.restype = c_wchar_p
        buff = create_unicode_buffer(5000)
        msg = inject_dll.readMapping("NewMessageLog",buff,5000)
        for i in range(-1, -20, -1):

                if msg[i] == '}':
                    j = i
                if msg[i] == '{':
                    k = i
                    break

        msgtype = ''
        try:
            for i in range(k + 1, j):
                msgtype = msgtype + msg[i]
        except UnboundLocalError:
            msgtype = '长链接'
            return {"text":msg,"wxid":None,"type":msgtype}
        msgwxid = msgtype.split('[')[1].replace(']','')
        msgtype = msgtype.split('[')[0]
        msg = msg[::-1].replace(msgtype[::-1],'')[::-1]
        msg = msg[::-1].replace(msgwxid[::-1], '')[::-1].replace('{[]}','')
        return {"text":msg,"wxid":msgwxid,"type":msgtype}

到这里已经写了几个Python函数,为了方便管理和日后调用,建议把它们包装成一个Wechat类。

以后就可以

>

>>> import wechat
>>> myweixin = wechat.Wechat()
...
>>> myweixin.send(...)
...
>>> myweixin.getmsg()
...

也可以给其他程序调用。

自定义新消息响应函数

我们现在可以获取到最新一条的消息了,但是我还想要能自定义一个回调函数,每有新消息,就让程序调用我们的处理函数。

要及时响应,就要开循环去监听对吧,我们在写DLL的时候试过了,直接开while循环就会卡死做不了其他事情,对于Python这个动态语言肯定是自断功力的。吃一堑长一智,我们同样可以用多线程的方法解决这个问题。

Python中的多线程库有threading,这是标准库。

导出线程类:

>

from threading import Thread

继承,写子类:

>

class myThread (Thread):
    def __init__(self, threadID, name, getmsgfunc,cbfunc):
        Thread.__init__(self)
        self.running = True
        self.threadID = threadID
        self.name = name
        self.getmsgfunc = getmsgfunc
        self.cbfunc = cbfunc
    def run(self):
        while True:
            if self.running:
                msglist = self.getmsgfunc()
                if msglist:
                    self.cbfunc(msglist)
            time.sleep(1)

类接收这四个参数,其中threadID和name是我们自己设定的,用来区分。之后是两个函数引用,分别是获取消息的函数和我们自定义的回调函数。之后子线程开循环一直get消息然后将消息列表传参给回调函数。

这时注意了,如果我们的getmsg函数一直返回消息的话,循环就会一直执行回调函数,因为他不知道这是不是最新消息了,消息是否被处理过了。所以我们要做好区分,让新消息只能接收一次。下一次就return False。

调整一下:

>

def getmsg(self):
        inject_dll.readMapping.restype = c_wchar_p
        buff = create_unicode_buffer(5000)
        msg = inject_dll.readMapping("NewMessageLog",buff,5000)
        if not msg:
            return False
        buff = create_unicode_buffer(50)
        inject_dll.writeMapping("NewMessageLog",buff,50)
        for i in range(-1, -20, -1):

                if msg[i] == '}':
                    j = i
                if msg[i] == '{':
                    k = i
                    break

        msgtype = ''
        try:
            for i in range(k + 1, j):
                msgtype = msgtype + msg[i]
        except UnboundLocalError:
            msgtype = '长链接'
            return {"text":msg,"wxid":None,"type":msgtype}
        msgwxid = msgtype.split('[')[1].replace(']','')
        msgtype = msgtype.split('[')[0]
        msg = msg[::-1].replace(msgtype[::-1],'')[::-1]
        msg = msg[::-1].replace(msgwxid[::-1], '')[::-1].replace('{[]}','')
        return {"text":msg,"wxid":msgwxid,"type":msgtype}

这样,我们每获取一次消息,就把内存映射文件清空,表明我们已经收到了这个消息,下次再读取就会读到None,表明已经没有新消息了。

感觉我这getmsg后面这么长一段解析字符串的代码,可以用正则去匹配哦。大家可以自己改进一下,我也懒得改了。

>

        th = myThread(2, "hook", self.getmsg, self.hookcallback)
        self.th = th
        print(self.th)
        self.th.start()

然后我们可以在wechat类的_init_里start线程。

之后我们可以实时调整callback函数(应该可以,没试过调整)。

然后我们可以:

>

>>> mywx = wechat.Wechat()
>>> def cb(alist):
        print(f'好友{alist["wxid"]}像你发送了这条{alist["type"]}消息:  {alist["text]}')
>>> mywx.hookcallback = cb
...

这是最基本的,这个回调大家看怎么用了。

进阶应用

1.手机命令电脑

手机控制电脑,这是个鸡肋的功能,但保不准有什么用,比如工作时暂时离开,向文件传输助手发个shutdown -s就能关机,如果有某些心怀不轨的同事朋友偷偷看我们的电脑,我们可以向文件传输助手发送take Photos命令截图留下证据然后锁屏,还可以发送模拟键盘鼠标操作命令在手机上使用电脑……

甚至给配置个多功能聊天机器人,就可以在文件传输助手查天气聊闲天查资料什么的,(我手机的语音助手太垃了,连GUI界面都没有,和其他手机相比简直就是猪与人的差距)。

不知不觉已经两万多字了,手机端几乎编辑不了了,要卡死,所以后文分几篇文章写。

手机命令电脑

之后可以有很多分支,比如自动回复,消息语音播报,我不手把手说了,自己探索吧(安全第一,我选择苟)。

接下来打算把InjectDll这个DLL改成一个Python库的形式,直接Import,不需要又用ctypes载入DLL,不然调用个函数都要几行代码,还无法导出类。

C++给python写扩展库

按评论区建议,又来补充上了在群里@人的教程。

C++ DLL注入微信之发送群@消息

三、编写dll文件要学那门编程语言?

改变现有的dll,几乎不太现实,考虑走黑客路线,或许有办法,但我不知道。

自己写代码生成一个dll,.net族谱的都可以c++,c#,vb。

四、DLL如何编写?

编写DLL(动态链接库)需要以下步骤:

首先,选择合适的编程语言,如C++或C#。

然后,定义DLL的接口,包括函数、类和数据结构。

接下来,实现接口中定义的功能,并编译为DLL文件。在编译过程中,需要注意指定正确的编译选项和链接选项。

最后,将生成的DLL文件与其他程序进行链接或加载,以便使用DLL中的功能。

编写DLL时,还需要考虑内存管理、异常处理和安全性等方面的问题,以确保DLL的稳定性和可靠性。

五、自己编写的dll程序如何生成lib文件?

解决办法:在工程上右键 -> 添加 -> 新建项 -> 选"模块定义文件(.def)" -> 随便填写个名字 -> 添加重新编译编译就可生成.lib 文件了,之后把添加的文件删除就可以了.

六、游戏dll文件丢失怎么恢复?

关于这个问题,如果您的游戏出现了dll文件丢失的问题,可以尝试以下方法来恢复:

1.重新安装游戏:有时dll文件丢失是由于游戏文件损坏或不完整导致的,重新安装游戏可能会解决问题。

2.下载缺失的dll文件:您可以从Internet上下载缺失的dll文件并将其放入游戏文件夹中,但是请注意只从可信的网站下载文件,以避免下载到恶意软件。

3.运行系统文件检查工具:在Windows操作系统中,您可以使用系统文件检查工具来扫描并修复系统文件中的任何损坏或丢失的dll文件。打开命令提示符并输入“sfc /scannow”即可运行此工具。

4.使用系统还原:如果您最近安装了新软件或更新,导致游戏dll文件丢失,您可以尝试使用系统还原将系统还原到先前的状态。请注意,这将删除您最近安装的所有软件和更新。

请注意,如果您不确定如何操作,请寻求专业帮助或咨询游戏开发商的支持团队。

七、如何编写DLL使其可重入?

可重入DLL不能使用全局变量。您使用的是全局变量C。

如果模型包含DLL的两个实例i1和i2,则DLL只加载一次。因此,C只有一个实例。

在plecsStart()中将内存分配给实例i1和i2的Converter_struct。现在,一旦模拟开始,将为实例i1和i2调用plecsStart(),但不能假设首先调用哪个实例。在任何情况下,C都只指向上次调用plecsStart()时分配的内存。

在模拟过程中,为实例i1和i2调用了plecsOutput()。同样,您不能假设何时调用哪个实例。由于您直接使用全局变量C,因此无论是为i1还是i2调用plecsOutput(),您都将始终使用上次调用plecsStart()时分配的内存。

解决方案很简单:去掉全局变量C。相反,在plecsStart()和plecsOutput()中使用局部变量声明。在plecsOutput()中,将本地变量分配给aState->userData的值,您已经在plecsStart()中使用了该值来存储指向为C分配的内存的指针。

八、dll 文件分析

DLL文件分析

DLL文件分析

DLL(动态链接库)是一种在Windows操作系统中广泛使用的文件格式,它包含了可由多个程序同时使用的代码和数据。在进行软件开发时,DLL文件在编译过程中起着非常重要的作用。今天,我们将讨论如何进行DLL文件的分析。

DLL文件分析的重要性

DLL文件分析对于软件开发者来说非常重要,因为它可以帮助我们更好地理解DLL文件的工作原理和功能。通过分析DLL文件,我们可以发现其中的函数、变量、符号表等信息,这些信息可以帮助我们优化代码、解决兼容性问题、改进性能等方面的问题。

分析DLL文件的步骤

1. 打开DLL文件:首先,我们需要打开DLL文件并查看其内容。可以使用一些专业的工具或软件来打开DLL文件,例如使用Visual Studio或Dependency Walker等工具。在打开DLL文件后,我们可以查看其中的函数、变量和符号表等信息。

2. 识别关键信息:通过分析DLL文件,我们可以找到其中的关键信息,例如函数签名、函数调用关系、全局变量等。这些信息可以帮助我们更好地了解DLL文件的工作原理和功能。

3. 分析兼容性和性能:除了关键信息的识别外,我们还应该分析DLL文件的兼容性和性能。这包括检查DLL文件与其他组件的兼容性、分析性能瓶颈和优化性能等方面的问题。

总结

通过对DLL文件的深入分析,我们可以更好地了解其工作原理和功能,解决兼容性和性能方面的问题。这对于软件开发者和测试人员来说是非常重要的技能,它可以帮助我们提高软件的质量和性能。

九、steam游戏缺少dll文件怎么修复?

修复方法:

1、先在360软件管家搜索到directx修复工具,点击下载按钮。   

2、下载完成后,点击Directx修复工具点击安装按钮。   

3、选择合适的修复工具,在弹出的页面中找到DirectX_Repair_win8_win10.exe并双击打开。   

4、打开软件后,点击检测并修复工具按钮,等待软件检测完成即可。 

5、修复完成后,点击退出按钮,电脑重启即可。

十、dll文件如何反汇编成源码,C++语言编写?

除非是未编译成机器语言的语言像java,否则想c/c++等编译出来的目标码只能反编译成汇编,查看源码几乎是不可能的。 既然dll编写者没有公开源码,肯定是有商业目的或其他原因,如果要学习或在自己的程序中使用,建议还是找开源的替代品吧!