微信3.1.0.41逆向-微信3.1.0.41HOOK介面(WeChatHelper3.1.0.41.dll)-VC++呼叫例項方法(win32)
WeChatHelper3.1.0.41.dll介面適用所有語言,今天我來講一下用VC++(win32)來做個例項呼叫。
第一步:新增rapidjson類庫
VC++用到的JSON庫為:rapidjson,rapidjson標頭檔案經在專案目錄中了,我們把它包含到專案中:
第二步:建立HTTP類
VC++用wininet進行HTTP通訊,
HttpHelper.h
#pragma once
#include <iostream>
#include <windows.h>
#include <wininet.h>
using namespace std;
//每次讀取的位元組數
#define READ_BUFFER_SIZE 4096
enum HttpInterfaceError
{
Hir_Success = 0, //成功
Hir_InitErr, //初始化失敗
Hir_ConnectErr, //連線HTTP伺服器失敗
Hir_SendErr, //傳送請求失敗
Hir_QueryErr, //查詢HTTP請求頭失敗
Hir_404, //頁面不存在
Hir_IllegalUrl, //無效的URL
Hir_CreateFileErr, //建立檔案失敗
Hir_DownloadErr, //下載失敗
Hir_QueryIPErr, //獲取域名對應的地址失敗
Hir_SocketErr, //套接字錯誤
Hir_UserCancel, //使用者取消下載
Hir_BufferErr, //檔案太大,緩衝區不足
Hir_HeaderErr, //HTTP請求頭錯誤
Hir_ParamErr, //引數錯誤,空指標,空字元
Hir_UnknowErr,
};
enum HttpRequest
{
Hr_Get,
Hr_Post
};
class HttpHelper
{
public:
HttpHelper(void);
~HttpHelper(void);
public:
// 通過HTTP請求:Get或Post方式獲取JSON資訊
const std::string RequestData(const std::string& strUrl,
HttpRequest type = Hr_Get,
std::string lpHeader = "",
std::string lpPostData = "");
protected:
// 關閉控制代碼
void Release();
// 釋放控制代碼
void ReleaseHandle(HINTERNET& hInternet);
// 解析URL地址
void ParseURLWeb(std::string strUrl, std::string& strHostName, std::string& strPageName, WORD& sPort);
private:
HINTERNET m_hSession;
HINTERNET m_hConnect;
HINTERNET m_hRequest;
HttpInterfaceError m_error;
};
HttpHelper.cpp
#include "HttpHelper.h"
#include <fstream>
#pragma comment(lib, "Wininet.lib")
#include <tchar.h>
HttpHelper::HttpHelper(void) :m_hSession(NULL), m_hConnect(NULL), m_hRequest(NULL)
{
}
HttpHelper::~HttpHelper(void)
{
Release();
}
// 通過HTTP請求:Get或Post方式獲取JSON資訊
const std::string HttpHelper::RequestData(const std::string& lpUrl,
HttpRequest type,
std::string strHeader,
std::string strPostData)
{
std::string strRet = "";
try
{
if (lpUrl.empty())
{
throw Hir_ParamErr;
}
Release();
m_hSession = InternetOpen(_T("Http-connect"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, NULL); //區域性
if (NULL == m_hSession)
{
throw Hir_InitErr;
}
INTERNET_PORT port = INTERNET_DEFAULT_HTTP_PORT;
std::string strHostName = "";
std::string strPageName = "";
ParseURLWeb(lpUrl, strHostName, strPageName, port);
printf("lpUrl:%s,\nstrHostName:%s,\nstrPageName:%s,\nport:%d\n", lpUrl.c_str(), strHostName.c_str(), strPageName.c_str(), (int)port);
m_hConnect = InternetConnectA(m_hSession, strHostName.c_str(), port, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, NULL);
if (NULL == m_hConnect)
{
throw Hir_ConnectErr;
}
std::string strRequestType;
if (Hr_Get == type)
{
strRequestType = "GET";
}
else
{
strRequestType = "POST";
}
m_hRequest = HttpOpenRequestA(m_hConnect, strRequestType.c_str(), strPageName.c_str(), "HTTP/1.1", NULL, NULL, INTERNET_FLAG_RELOAD, NULL);
if (NULL == m_hRequest)
{
throw Hir_InitErr;
}
DWORD dwHeaderSize = (strHeader.empty()) ? 0 : strlen(strHeader.c_str());
BOOL bRet = FALSE;
if (Hr_Get == type)
{
bRet = HttpSendRequestA(m_hRequest, strHeader.c_str(), dwHeaderSize, NULL, 0);
}
else
{
DWORD dwSize = (strPostData.empty()) ? 0 : strlen(strPostData.c_str());
bRet = HttpSendRequestA(m_hRequest, strHeader.c_str(), dwHeaderSize, (LPVOID)strPostData.c_str(), dwSize);
}
if (!bRet)
{
throw Hir_SendErr;
}
char szBuffer[READ_BUFFER_SIZE + 1] = { 0 };
DWORD dwReadSize = READ_BUFFER_SIZE;
if (!HttpQueryInfoA(m_hRequest, HTTP_QUERY_RAW_HEADERS, szBuffer, &dwReadSize, NULL))
{
throw Hir_QueryErr;
}
if (NULL != strstr(szBuffer, "404"))
{
throw Hir_404;
}
while (true)
{
bRet = InternetReadFile(m_hRequest, szBuffer, READ_BUFFER_SIZE, &dwReadSize);
if (!bRet || (0 == dwReadSize))
{
break;
}
szBuffer[dwReadSize] = '\0';
strRet.append(szBuffer);
}
}
catch (HttpInterfaceError error)
{
m_error = error;
}
return std::move(strRet);
}
// 解析URL地址 [3/14/2017/shike]
void HttpHelper::ParseURLWeb(std::string strUrl, std::string& strHostName, std::string& strPageName, WORD& sPort)
{
sPort = 80;
string strTemp(strUrl);
std::size_t nPos = strTemp.find("http://");
if (nPos != std::string::npos)
{
strTemp = strTemp.substr(nPos + 7, strTemp.size() - nPos - 7);
}
nPos = strTemp.find('/');
if (nPos == std::string::npos) //沒有找到
{
strHostName = strTemp;
}
else
{
strHostName = strTemp.substr(0, nPos);
}
std::size_t nPos1 = strHostName.find(':');
if (nPos1 != std::string::npos)
{
std::string strPort = strTemp.substr(nPos1 + 1, strHostName.size() - nPos1 - 1);
strHostName = strHostName.substr(0, nPos1);
sPort = (WORD)atoi(strPort.c_str());
}
if (nPos == std::string::npos)
{
return;
}
strPageName = strTemp.substr(nPos, strTemp.size() - nPos);
}
// 關閉控制代碼
void HttpHelper::Release()
{
ReleaseHandle(m_hRequest);
ReleaseHandle(m_hConnect);
ReleaseHandle(m_hSession);
}
// 釋放控制代碼
void HttpHelper::ReleaseHandle(HINTERNET& hInternet)
{
if (hInternet)
{
InternetCloseHandle(hInternet);
hInternet = NULL;
}
}
第三步:解析JSON
和其它語言一樣,我就用獲取即時聊天資料為例項:
聊天記錄物件是根據聊天記錄資料和型別建立的,我們來看一下聊天記錄的JSON資料結構:
{
"cmdid": 7,
"maxid": 5,
"count": 75,
"status": "ok",
"qq": "2376140244",
"data": [
{
"localId": 42,
"MsgSvrID": "2062239765205091112",
"StrTalker": "18618087204@chatroom",
"StrContent": "<?xml version=\"1.0\"?>\n<msg>\n\t<img aeskey=\"611118464e36d48e86c891c79e25b07c\" encryver=\"1\" cdnthumbaeskey=\"611118464e36d48e86c891c79e25b07c\" cdnthumburl=\"3058020100044c304a0201000204be9026a302032f5d0302045938f0b702045fefffb90425617570696d675f383038376264343062646134646465335f31363039353634303839373537020401090a020201000405004c56fb00\" cdnthumblength=\"4043\" cdnthumbheight=\"140\" cdnthumbwidth=\"56\" cdnmidheight=\"0\" cdnmidwidth=\"0\" cdnhdheight=\"0\" cdnhdwidth=\"0\" cdnmidimgurl=\"3058020100044c304a0201000204be9026a302032f5d0302045938f0b702045fefffb90425617570696d675f383038376264343062646134646465335f31363039353634303839373537020401090a020201000405004c56fb00\" length=\"24332\" md5=\"692efd877bce4ad7dc41becca0d2278d\" hevc_mid_size=\"24332\" />\n</msg>\n",
"CreateTime": 1609564091,
"IsSender": 0,
"type": 3,
"SubType": 0,
"CompressContent": "",
"BytesExtra": {
"wxid": "wxid_y7hw81zn588b12",
"thumb": "keepmoving8\\FileStorage\\Image\\Thumb\\2021-01\\e03c67108d6c39c020c48696dbd36916_t.dat",
"image": "keepmoving8\\FileStorage\\Image\\2021-01\\4953c7f182c4741eec97c9ab6bfe1eb4.dat",
"video": ""
}
},
{
"localId": 43,
"MsgSvrID": "3036343329427015238",
"StrTalker": "2656683682@chatroom",
"StrContent": "收滿不收彈頭40",
"CreateTime": 1609564122,
"IsSender": 0,
"type": 1,
"SubType": 0,
"CompressContent": "",
"BytesExtra": {
"wxid": "wxid_87rw855vmv4h22",
"thumb": "",
"image": "",
"video": ""
}
},
{
"localId": 44,
"MsgSvrID": "6558585969683208319",
"StrTalker": "JI282940039",
"StrContent": "要的滴滴我",
"CreateTime": 1609564128,
"IsSender": 0,
"type": 1,
"SubType": 0,
"CompressContent": "",
"BytesExtra": {
"wxid": "",
"thumb": "",
"image": "",
"video": ""
}
}
]
}
上面記錄中有三種記錄:
1.群圖片訊息,群傳送的微信ID和圖片的地址已顯示在擴充套件資料中。
2.群文字訊息,群傳送的微信ID顯示在擴充套件資料中。
3.普通好友文字訊息,擴充套件資料都是空。
根據JSON資料結構,我們在建立RecordObject物件
RecordObject.h
#pragma once
#include <list>
using namespace std;
struct RecordBytesExtra
{
char wxid[0x40] = { 0 };// 群訊息時傳送者微信ID"
char thumb[0x200] = { 0 }; // 圖片型別時縮微圖路徑"
char image[0x200] = { 0 };// 圖片或視訊型別時圖片路徑"
char video[0x200] = { 0 };//視訊型別時MP4路徑
};
struct RecordData
{
int localId = 0;//記錄ID
char MsgSvrID[255] = { 0 };//訊息ID
char StrTalker[0x40] = { 0 };//訊息微信ID
char StrContent[0x4000] = { 0 };//內容
int CreateTime = 0;//建立時間
int IsSender = 0;//是否自己傳送
int type = 0;//訊息型別
int SubType = 0;//子型別
char CompressContent[0x40] = { 0 }; //這裡為轉賬金額資訊,有+與-表示:收到與轉出
RecordBytesExtra BytesExtra;//擴充套件訊息
};
class RecordObject
{
public:
RecordObject();
RecordObject(const char * jsonData);
int cmdid = 0;
int maxid = 0;//記錄索引
int count = 0;//資訊條數
char qq[20] = { 0 };//本人QQ
char status[20] = { 0 };//狀態
list<RecordData> data;//訊息主體陣列
};
RecordObject.cpp
#include "RecordObject.h"
#include <stdio.h>
#include <string.h>
#include<fstream>
#include<iostream>
#include <windows.h>
#include <tchar.h>
#include "rapidjson.h"
#include "document.h"
#include "stringbuffer.h"
#include "filereadstream.h"
#include "filewritestream.h"
#include "writer.h"
using namespace rapidjson;
RecordObject::RecordObject()
{
}
RecordObject::RecordObject(const char * jsonData)
{
rapidjson::Document document;
document.Parse(jsonData);
if (document.HasParseError())
{
return;
}
rapidjson::Value::ConstMemberIterator iter = document.FindMember("cmdid");
if (iter != document.MemberEnd()) {
cmdid = iter->value.GetInt();
}
iter = document.FindMember("cmdid");
if (iter != document.MemberEnd()) {
cmdid = iter->value.GetInt();
}
iter = document.FindMember("maxid");
if (iter != document.MemberEnd()) {
maxid = iter->value.GetInt();
}
iter = document.FindMember("count");
if (iter != document.MemberEnd()) {
count = iter->value.GetInt();
}
iter = document.FindMember("qq");
if (iter != document.MemberEnd()) {
sprintf_s(qq, "%s", iter->value.GetString());
}
iter = document.FindMember("status");
if (iter != document.MemberEnd()) {
sprintf_s(status, "%s", iter->value.GetString());
}
const rapidjson::Value& dataValue = document["data"];
if (dataValue.IsArray())
{
for (rapidjson::SizeType i = 0; i < dataValue.Size(); ++i)
{
const rapidjson::Value& obj = dataValue[i];
RecordData recordData;
recordData.localId = obj["localId"].GetInt();
sprintf_s(recordData.MsgSvrID, "%s", obj["MsgSvrID"].GetString());
sprintf_s(recordData.StrTalker, "%s", obj["StrTalker"].GetString());
sprintf_s(recordData.StrContent, "%s", obj["StrContent"].GetString());
recordData.CreateTime = obj["CreateTime"].GetInt();
recordData.IsSender = obj["IsSender"].GetInt();
recordData.type = obj["type"].GetInt();
recordData.SubType = obj["SubType"].GetInt();
sprintf_s(recordData.CompressContent, "%s", obj["CompressContent"].GetString());
const rapidjson::Value& subObj = obj["BytesExtra"];
if (subObj.IsObject())
{
sprintf_s(recordData.BytesExtra.wxid, "%s", subObj["wxid"].GetString());
sprintf_s(recordData.BytesExtra.thumb, "%s", subObj["thumb"].GetString());
sprintf_s(recordData.BytesExtra.image, "%s", subObj["image"].GetString());
sprintf_s(recordData.BytesExtra.video, "%s", subObj["video"].GetString());
}
data.push_back(recordData);
}
}
}
第四步:對資料編碼和解碼
因為 VC++是用 Unicode字符集,而Json都是用Utf-8,所以要對它們進行相互轉碼
Common.h
#pragma once
#include <vector>
#include <iostream>
#include <iterator>
#include <regex>
#include <ctime>
#include <string.h>
#include <io.h>
#include <direct.h>
#include <shlobj.h>
#include <string>
#include <codecvt>
#include <locale>
#include <iostream>
using namespace std;
char* UnicodeToUtf8(wchar_t* unicode);
char* UnicodeToUtf8(const wchar_t* unicode);
wchar_t* UTF8ToUnicode(char* str);
wchar_t* UTF8ToUnicode(const char* str);
wchar_t* UTF8ToUnicode(string text);
Common.cpp
#include "common.h"
/*
//unicode תUtf8
*/
char* UnicodeToUtf8(const wchar_t* unicode)
{
int len;
len = WideCharToMultiByte(CP_UTF8, 0, unicode, -1, NULL, 0, NULL, NULL);
char* szUtf8 = (char*)malloc(len + 1);
if (szUtf8 != 0) {
memset(szUtf8, 0, len + 1);
}
WideCharToMultiByte(CP_UTF8, 0, unicode, -1, szUtf8, len, NULL, NULL);
return szUtf8;
}
char* UnicodeToUtf8(wchar_t* unicode)
{
int len;
len = WideCharToMultiByte(CP_UTF8, 0, unicode, -1, NULL, 0, NULL, NULL);
char* szUtf8 = (char*)malloc(len + 1);
if (szUtf8 != 0) {
memset(szUtf8, 0, len + 1);
}
WideCharToMultiByte(CP_UTF8, 0, unicode, -1, szUtf8, len, NULL, NULL);
return szUtf8;
}
/*
//Utf8תunicode
*/
wchar_t* UTF8ToUnicode(const char* str)
{
int textlen = 0;
wchar_t* result;
textlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
result = (wchar_t*)malloc((textlen + 1) * sizeof(wchar_t));
if (result != 0)
{
memset(result, 0, (textlen + 1) * sizeof(wchar_t));
}
MultiByteToWideChar(CP_UTF8, 0, str, -1, (LPWSTR)result, textlen);
return result;
}
wchar_t* UTF8ToUnicode(char* str)
{
int textlen = 0;
wchar_t* result;
textlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
result = (wchar_t*)malloc((textlen + 1) * sizeof(wchar_t));
if (result != 0)
{
memset(result, 0, (textlen + 1) * sizeof(wchar_t));
}
MultiByteToWideChar(CP_UTF8, 0, str, -1, (LPWSTR)result, textlen);
return result;
}
wchar_t* UTF8ToUnicode(string text)
{
const char * str = text.c_str();
int textlen = 0;
wchar_t* result;
textlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
result = (wchar_t*)malloc((textlen + 1) * sizeof(wchar_t));
if (result != 0)
{
memset(result, 0, (textlen + 1) * sizeof(wchar_t));
}
MultiByteToWideChar(CP_UTF8, 0, str, -1, (LPWSTR)result, textlen);
return result;
}
第五步:在主程式中應用
在vc++中我們用SetTimer來定時重新整理即時聊天記錄
// CWechatHelper.cpp : 定義應用程式的入口點。
//
#include "framework.h"
#include "CWechatHelper.h"
#include "resource.h"
#include "common.h"
#include "HttpHelper.h"
#include "RecordObject.h"
#define MAX_LOADSTRING 100
INT_PTR CALLBACK DialogProc(HWND, UINT, WPARAM, LPARAM);
HWND hwndDlg;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
//載入視窗
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);
MSG msg = {};
// 5.1獲取訊息
while (GetMessage(&msg, 0, 0, 0))
{
// 5.2翻譯訊息
TranslateMessage(&msg);
// 5.3轉發到訊息回撥函式
DispatchMessage(&msg);
}
}
//追加文字
void AppendText(const TCHAR *newText)
{
int outLength = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_EDIT_RECORD));
TCHAR * buf = new TCHAR[outLength+1];
GetDlgItemText(hwndDlg, IDC_EDIT_RECORD, buf, outLength + 1);
wstring text(buf);
text.append(newText).append(L"\r\n");
//_tcscat_s(buf, outLength + 1, newText);
SetDlgItemText(hwndDlg, IDC_EDIT_RECORD, text.c_str());
}
//回撥重新整理即時聊天記錄
void CALLBACK TimerProc(HWND hWnd, UINT nMsg, UINT nTimerid, DWORD dwTime)
{
HttpHelper* http = new HttpHelper();
string data = http->RequestData("http://127.0.0.1/?cmdid=7");
RecordObject *recordObj = new RecordObject(data.c_str());
for (auto record : recordObj->data)
{
AppendText(UTF8ToUnicode(record.StrContent));
}
delete http;
delete recordObj;
}
//視窗訊息回撥
INT_PTR CALLBACK DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
//視窗初始化
case WM_INITDIALOG:
{
hwndDlg = hDlg;
//定時器
SetTimer(hDlg, 1, 3000, TimerProc);
SendMessage(GetDlgItem(hDlg,IDC_COMBO_TYPE), CB_ADDSTRING, 0, (LPARAM)(L"GET"));
SendMessage(GetDlgItem(hDlg, IDC_COMBO_TYPE), CB_ADDSTRING, 0, (LPARAM)(L"POST"));
SendMessage(GetDlgItem(hDlg, IDC_COMBO_TYPE), CB_SETCURSEL, 0, 0);
SetDlgItemText(hwndDlg, IDC_EDIT1, TEXT("http://127.0.0.1/"));
break;
}
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析選單選擇:
switch (wmId)
{
case IDC_BUTTON_REQUEST:
{
int i = SendMessage(GetDlgItem(hDlg, IDC_COMBO_TYPE), CB_GETCURSEL,0, 0);
string data;
WCHAR wurl[500] = { 0 };
GetDlgItemText(hwndDlg, IDC_EDIT1, wurl, sizeof(wurl));
string url =UnicodeToUtf8(wurl);
WCHAR wparam[2000] = { 0 };
GetDlgItemText(hwndDlg, IDC_EDIT_PARAM, wparam, sizeof(wparam));
string param = UnicodeToUtf8(wparam);
HttpHelper* http = new HttpHelper();
if (i == 0)
{
data = http->RequestData(url);
}
else
{
data = http->RequestData(url,HttpRequest::Hr_Post,"content-type:application/json;charset:utf-8;", param);
}
SetDlgItemText(hwndDlg, IDC_EDIT_RESPOSE,UTF8ToUnicode(data));
delete http;
break;
}
case IDM_EXIT:
DestroyWindow(hDlg);
break;
default:
return DefWindowProc(hDlg, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hDlg, &ps);
// TODO: 在此處新增使用 hdc 的任何繪圖程式碼...
EndPaint(hDlg, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hDlg, message, wParam, lParam);
}
現在看看效果:
到時,vc++的呼叫方法結束,對其它的資料結構依照這個例項都能寫出來!
原始碼下載地址(vs2017):https://download.csdn.net/download/keepmoving0407/14017024
也可以進**流下載:(交流群:561112477)
「其他文章」
- Flink/Blink 原理漫談(六)容錯機制(fault tolerance)詳解
- 這是阿里技術專家對 SRE 和穩定性保障的理解
- html中meta標籤
- Obsuite:混合雲可觀測性中臺
- 聊聊職業規劃,懷疑人生的那種!
- 程式設計師成長思維:把自己當做產品來發展
- Flink 有狀態運算元和應用Demo詳解
- Hello World!--Skyline的第一篇部落格
- 【Bilibili直播源】瀏覽器抓取真實直播源地址(純前端JS & PHP解析原始碼)
- 新的側通道攻擊可以從硬體 2FA 金鑰中獲得加密金鑰
- JavaWeb學習-案例練習-圖書管理後臺- 9 -分頁操作
- 微信3.1.0.41逆向-微信3.1.0.41HOOK介面(WeChatHelper3.1.0.41.dll)-VC 呼叫例項方法(win32)
- 關於Zookeeper叢集搭建節點數量為什麼是2n 1個問題
- 看完這篇文章,別再說二叉樹了,樹根都給你拔起來了(圖解 程式碼 詳細思路)
- 微信掃碼訪問網站呼叫預設瀏覽器開啟如何實現?
- 前端常用網站彙總
- 關於演算法筆試,labuladong 說點套路
- styled-components v4測試版釋出:原生支援 ref,效能提升25%
- 對RecyclerView的介面卡的封裝
- 【Java進階面試系列之一】哥們,你們的系統架構中為什麼要引入訊息中介軟體?