写在前面

在前面三篇文章,已经找到数据库句柄、sqlite3_exec函数和一系列备份用的函数地址,本篇文章,尝试完成微信数据库在线备份。

回顾

数据库句柄

[[WeChatWin.dll + 0x222F3FC] + 0x1888][[WeChatWin.dll + 0x222F3FC] + 0x188C]

函数地址

微信版本:3.6.0.18

sqlite3_open = 1138ACD0
sqlite3_backup_init = 1131C110
sqlite3_backup_step = 1131C510
sqlite3_sleep = 1138B510
sqlite3_backup_finish = 1131CB50
sqlite3_close = 113880A0
sqlite3_backup_remaining = 1131CC50
sqlite3_backup_pagecount = 1131CC60
sqlite3_errcode = 11389970

以上是在IDA中的地址,换算到微信中,是WeChatWin.dll + IDAaddress - 0x10000000

备份函数

int backupDb(sqlite3* pDb, const char* szFilename,void(*xProgress)(int, int)
) {int rc;sqlite3* pFile;sqlite3_backup* pBackup;//打开数据库rc = sqlite3_open(szFilename, &pFile);if (SQLITE_OK == rc) {//初始化获取一个备份对象pBackup = sqlite3_backup_init(pFile, "main", pDb, "main");if (pBackup) {do {//每次备份5页rc = sqlite3_backup_step(pBackup, 5);//通知更新进度xProgress(sqlite3_backup_remaining(pBackup),//还剩余需要备份的页数sqlite3_backup_pagecount(pBackup)//备份的总页数);if (SQLITE_OK == rc || SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {//睡眠sqlite3_sleep(250);}} while (SQLITE_OK == rc || SQLITE_BUSY == rc || SQLITE_LOCKED == rc);//完成备份sqlite3_backup_finish(pBackup);}rc = sqlite3_errcode(pFile);}sqlite3_close(pFile);return rc;
}

重写备份函数

首先要根据上面的备份函数,使用函数指针,重写一个实现,如下(代码来自赵庆明老师):

int __cdecl backupDb(DWORD pDb,                  /* Database to back up */const char* zFilename,      /* Name of file to back up to */DWORD myMain,int address_sqlite3_open,int address_sqlite3_backup_init,int address_sqlite3_backup_step,int address_sqlite3_backup_remaining,int address_sqlite3_backup_pagecount,int address_sqlite3_sleep,int address_sqlite3_backup_finish,int address_sqlite3_errcode,int address_sqlite3_close,void(*xProgress)(int, int)  /* Progress function to invoke */
) {int rc;                      /* Function return code */DWORD pFile = 0;             /* Database connection opened on zFilename */DWORD pBackup = 0;             /* Backup handle used to copy data */Sqlite3_open p_Sqlite3_open = (Sqlite3_open)address_sqlite3_open;Sqlite3_backup_init p_Sqlite3_backup_init = (Sqlite3_backup_init)address_sqlite3_backup_init;Sqlite3_backup_step p_Sqlite3_backup_step = (Sqlite3_backup_step)address_sqlite3_backup_step;Sqlite3_backup_remaining p_Sqlite3_backup_remaining = (Sqlite3_backup_remaining)address_sqlite3_backup_remaining;Sqlite3_backup_pagecount p_Sqlite3_backup_pagecount = (Sqlite3_backup_pagecount)address_sqlite3_backup_pagecount;Sqlite3_sleep p_Sqlite3_sleep = (Sqlite3_sleep)address_sqlite3_sleep;Sqlite3_backup_finish p_Sqlite3_backup_finish = (Sqlite3_backup_finish)address_sqlite3_backup_finish;Sqlite3_errcode p_Sqlite3_errcode = (Sqlite3_errcode)address_sqlite3_errcode;Sqlite3_close p_Sqlite3_close = (Sqlite3_close)address_sqlite3_close;rc = p_Sqlite3_open((const char*)zFilename, &pFile);if (rc == SQLITE_OK) {pBackup = p_Sqlite3_backup_init(pFile, (const char*)myMain, pDb, (const char*)myMain);if (pBackup) {do {rc = p_Sqlite3_backup_step(pBackup, 5);xProgress(p_Sqlite3_backup_remaining(pBackup),p_Sqlite3_backup_pagecount(pBackup));if (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {p_Sqlite3_sleep(50);}} while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);(void)p_Sqlite3_backup_finish(pBackup);}rc = p_Sqlite3_errcode(pFile);}(void)p_Sqlite3_close(pFile);return rc;
}

将各个函数的地址作为参数传递,并使用函数指针进行调用,各个函数的原型需要参考sqlite3源码。

实现

又到了愉快的码代码时间,直接在第二篇文章的代码基础上添加功能。

标头

sqlite_backup.h

#pragma once
#include<windows.h>
#include<iostream>
#include<vector>
using namespace std;
void TestBackUp();

拷贝宏

备份函数中用到了一些宏,为了方便,我们直接从sqlite3源码中拷贝出来:

#include "pch.h"
#include "sqlite_backup.h"#define SQLITE_OK           0   /* Successful result */
#define SQLITE_ERROR        1   /* Generic error */
#define SQLITE_INTERNAL     2   /* Internal logic error in SQLite */
#define SQLITE_PERM         3   /* Access permission denied */
#define SQLITE_ABORT        4   /* Callback routine requested an abort */
#define SQLITE_BUSY         5   /* The database file is locked */
#define SQLITE_LOCKED       6   /* A table in the database is locked */
#define SQLITE_NOMEM        7   /* A malloc() failed */
#define SQLITE_READONLY     8   /* Attempt to write a readonly database */
#define SQLITE_INTERRUPT    9   /* Operation terminated by sqlite3_interrupt()*/
#define SQLITE_IOERR       10   /* Some kind of disk I/O error occurred */
#define SQLITE_CORRUPT     11   /* The database disk image is malformed */
#define SQLITE_NOTFOUND    12   /* Unknown opcode in sqlite3_file_control() */
#define SQLITE_FULL        13   /* Insertion failed because database is full */
#define SQLITE_CANTOPEN    14   /* Unable to open the database file */
#define SQLITE_PROTOCOL    15   /* Database lock protocol error */
#define SQLITE_EMPTY       16   /* Internal use only */
#define SQLITE_SCHEMA      17   /* The database schema changed */
#define SQLITE_TOOBIG      18   /* String or BLOB exceeds size limit */
#define SQLITE_CONSTRAINT  19   /* Abort due to constraint violation */
#define SQLITE_MISMATCH    20   /* Data type mismatch */
#define SQLITE_MISUSE      21   /* Library used incorrectly */
#define SQLITE_NOLFS       22   /* Uses OS features not supported on host */
#define SQLITE_AUTH        23   /* Authorization denied */
#define SQLITE_FORMAT      24   /* Not used */
#define SQLITE_RANGE       25   /* 2nd parameter to sqlite3_bind out of range */
#define SQLITE_NOTADB      26   /* File opened that is not a database file */
#define SQLITE_NOTICE      27   /* Notifications from sqlite3_log() */
#define SQLITE_WARNING     28   /* Warnings from sqlite3_log() */
#define SQLITE_ROW         100  /* sqlite3_step() has another row ready */
#define SQLITE_DONE        101  /* sqlite3_step() has finished executing */

函数指针

各个函数对应的函数指针:

typedef int(__cdecl* Sqlite3_open)(const char*, DWORD*);
typedef DWORD(__cdecl* Sqlite3_backup_init)(DWORD, const char*, DWORD, const char*);
typedef int(__cdecl* Sqlite3_backup_step)(DWORD, int);
typedef int(__cdecl* Sqlite3_backup_remaining)(DWORD);
typedef int(__cdecl* Sqlite3_backup_pagecount)(DWORD);
typedef int(__cdecl* Sqlite3_sleep)(int);
typedef int(__cdecl* Sqlite3_backup_finish)(DWORD);
typedef int(__cdecl* Sqlite3_errcode)(DWORD);
typedef int(__cdecl* Sqlite3_close)(DWORD);

计算函数地址

为方便计算函数地址,写两个函数:

#define IDA_BASE 0x10000000
DWORD OffsetFromIdaAddr(DWORD idaAddr) {return idaAddr - IDA_BASE;
}static DWORD GetWeChatWinBase() {DWORD WeChatWinBase = (DWORD)GetModuleHandle(L"WeChatWin.dll");return WeChatWinBase;
}

备份函数

代码上面已给出,此处略。

回调函数

用于显示备份进度:

void XProgress(int a, int b)
{printf("备份进度: %d/%d\n", b - a, b);return;
}

备份入口函数

备份时调用的函数:

/*
参数1:要备份的数据库句柄
参数2:备份文件的保存位置
返回值:如果备份成功,返回0;如果失败,返回非0值
*/
int BackupSQLiteDB(DWORD DbHandle, const char* BackupFile)
{DWORD wxBaseAddress = GetWeChatWinBase();PatchSQLite3_Backup_Init();cout << "开始备份,文件保存至: " << BackupFile << endl;DWORD address_sqlite3_open = wxBaseAddress + OffsetFromIdaAddr(0x1138ACD0);DWORD address_sqlite3_backup_init = wxBaseAddress + OffsetFromIdaAddr(0x1131C110);DWORD address_sqlite3_backup_step = wxBaseAddress + OffsetFromIdaAddr(0x1131C510);DWORD address_sqlite3_sleep = wxBaseAddress + OffsetFromIdaAddr(0x1138B510);DWORD address_sqlite3_backup_finish = wxBaseAddress + OffsetFromIdaAddr(0x1131CB50);DWORD address_sqlite3_close = wxBaseAddress + OffsetFromIdaAddr(0x113880A0);DWORD address_sqlite3_backup_remaining = wxBaseAddress + OffsetFromIdaAddr(0x1131CC50);DWORD address_sqlite3_backup_pagecount = wxBaseAddress + OffsetFromIdaAddr(0x1131CC60);DWORD address_sqlite3_errcode = wxBaseAddress + OffsetFromIdaAddr(0x11389970);const char* myMain = "main";int rc = backupDb(DbHandle,BackupFile,(DWORD)myMain,address_sqlite3_open,address_sqlite3_backup_init,address_sqlite3_backup_step,address_sqlite3_backup_remaining,address_sqlite3_backup_pagecount,address_sqlite3_sleep,address_sqlite3_backup_finish,address_sqlite3_errcode,address_sqlite3_close,XProgress);cout << "备份完成: " << BackupFile << endl;return rc;
}

获取句柄

为保持两篇文章功能各自独立,就不对之前写的sqlite3_exec调用代码做改动了,把获取句柄的逻辑整个拷贝过来(同名变量和函数要加静态修饰):

#define db_handles_base_offset 0x222F3FC
struct dbStruct {DWORD dbhandle;wchar_t* dbname;
};static vector<dbStruct> dbhandles;
static void GetHandles() {DWORD WeChatWinBase = GetWeChatWinBase();DWORD SqlHandleBaseAddr = *(DWORD*)(WeChatWinBase + db_handles_base_offset);DWORD SqlHandleBeginAddr = *(DWORD*)(SqlHandleBaseAddr + 0x1888);DWORD SqlHandleEndAddr = *(DWORD*)(SqlHandleBaseAddr + 0x188C);wstring dbnames = L"";while (SqlHandleBeginAddr < SqlHandleEndAddr) {DWORD dwHandle = *(DWORD*)SqlHandleBeginAddr;SqlHandleBeginAddr += 0x4;// 做一下简单的去重if (dbnames.find((wchar_t*)(*(DWORD*)(dwHandle + 0x78)), 0) != wstring::npos)continue;dbStruct db = { 0 };db.dbname = (wchar_t*)(*(DWORD*)(dwHandle + 0x78));dbnames += (wchar_t*)(*(DWORD*)(dwHandle + 0x78));db.dbhandle = *(DWORD*)(dwHandle + 0x64);dbhandles.push_back(db);}
}

绕过加密数据库限制

上面的代码可以正常工作,微信不会崩溃,不过也不能备份成功,因为sqlite3_backup_init会略过加密的数据库,提示backup is not supported with encrypted databases,所以要patch掉其中的22个字节,绕过这段限制,为什么是22呢,大家可以暂时不绕过,在OD中对sqlite3_backup_init下断,断下来后单步,会遇到这样一段汇编:

66EFC162    837C24 10 00    cmp dword ptr ss:[esp+0x10],0x0
66EFC167    0F85 11010000   jnz WeChatWi.66EFC27E
66EFC16D    837C24 14 00    cmp dword ptr ss:[esp+0x14],0x0
66EFC172    0F85 06010000   jnz WeChatWi.66EFC27E

WeChatWi.66EFC27E处就会提示不允许备份加密数据库:

66EFC27E    68 100FB967     push WeChatWi.67B90F10                   ; ASCII "backup is not supported with encrypted databases"
66EFC283    6A 01           push 0x1
66EFC285    56              push esi
66EFC286    E8 A5F6FDFF     call WeChatWi.66EDB930
66EFC28B    83C4 0C         add esp,0xC

66EFC162处的22个字节patch掉,就可以正常备份:

BOOL SQLite3_Backup_Init_Patched = FALSE;
VOID PatchSQLite3_Backup_Init() {if (SQLite3_Backup_Init_Patched)return;// patch掉这块指令,绕过`backup is not supported with encrypted databases`DWORD address_sqlite3_backup_init_patch_offset = OffsetFromIdaAddr(0x1131C110 + 0x52);DWORD patchAddress = GetWeChatWinBase() + address_sqlite3_backup_init_patch_offset;const int nopLen = 22;BYTE nopData[nopLen];for (int i = 0; i < nopLen; i++) {nopData[i] = 0x90;}WriteProcessMemory(GetCurrentProcess(), (LPVOID)patchAddress, nopData, nopLen, 0);SQLite3_Backup_Init_Patched = TRUE;return;
}

测试函数

所有的逻辑都已经写好了,接下来写一个调用函数吧:

void TestBackUp() {CreateConsole();PatchSQLite3_Backup_Init();GetHandles();for (unsigned int i = 0; i < dbhandles.size(); i++) {printf("dbname: %ws\n", dbhandles[i].dbname);char* dbname = new char[wcslen(dbhandles[i].dbname) + 1];if (dbname) {WideCharToMultiByte(CP_ACP, 0, dbhandles[i].dbname, -1, dbname, wcslen(dbhandles[i].dbname) + 1, 0, 0);BackupSQLiteDB(dbhandles[i].dbhandle, (const char*)dbname);delete[] dbname;dbname = NULL;}// 作为示例,这里只备份一个break;}
}

修改dllmain

dllmain.cpp修改如下:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include "sqlite_exec.h"
#include "sqlite_backup.h"BOOL APIENTRY DllMain( HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:{// 记得将此函数声明添加到`sqlite_backup.h`中TestBackUp();// execute();}case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;
}

结果

代码编写完毕,启动微信并登录,使用InjectDLL.exe注入,结果如下:

因为没有指定绝对路径,所以保存到微信的安装目录下了:

打开看看:

OK,备份完成。

写在后面

本篇文章介绍了如何使用找到的数据库句柄和备份相关函数地址,完成微信数据库在线备份,下一篇文章,将尝试定位数据库密码的保存位置,并编写离线解密工具,也是数据库相关的最后一篇文章。

完整代码

提供sqlite_backup.cpp完整代码,供大家参考:

#include "pch.h"
#include "sqlite_backup.h"#define SQLITE_OK           0   /* Successful result */
#define SQLITE_ERROR        1   /* Generic error */
#define SQLITE_INTERNAL     2   /* Internal logic error in SQLite */
#define SQLITE_PERM         3   /* Access permission denied */
#define SQLITE_ABORT        4   /* Callback routine requested an abort */
#define SQLITE_BUSY         5   /* The database file is locked */
#define SQLITE_LOCKED       6   /* A table in the database is locked */
#define SQLITE_NOMEM        7   /* A malloc() failed */
#define SQLITE_READONLY     8   /* Attempt to write a readonly database */
#define SQLITE_INTERRUPT    9   /* Operation terminated by sqlite3_interrupt()*/
#define SQLITE_IOERR       10   /* Some kind of disk I/O error occurred */
#define SQLITE_CORRUPT     11   /* The database disk image is malformed */
#define SQLITE_NOTFOUND    12   /* Unknown opcode in sqlite3_file_control() */
#define SQLITE_FULL        13   /* Insertion failed because database is full */
#define SQLITE_CANTOPEN    14   /* Unable to open the database file */
#define SQLITE_PROTOCOL    15   /* Database lock protocol error */
#define SQLITE_EMPTY       16   /* Internal use only */
#define SQLITE_SCHEMA      17   /* The database schema changed */
#define SQLITE_TOOBIG      18   /* String or BLOB exceeds size limit */
#define SQLITE_CONSTRAINT  19   /* Abort due to constraint violation */
#define SQLITE_MISMATCH    20   /* Data type mismatch */
#define SQLITE_MISUSE      21   /* Library used incorrectly */
#define SQLITE_NOLFS       22   /* Uses OS features not supported on host */
#define SQLITE_AUTH        23   /* Authorization denied */
#define SQLITE_FORMAT      24   /* Not used */
#define SQLITE_RANGE       25   /* 2nd parameter to sqlite3_bind out of range */
#define SQLITE_NOTADB      26   /* File opened that is not a database file */
#define SQLITE_NOTICE      27   /* Notifications from sqlite3_log() */
#define SQLITE_WARNING     28   /* Warnings from sqlite3_log() */
#define SQLITE_ROW         100  /* sqlite3_step() has another row ready */
#define SQLITE_DONE        101  /* sqlite3_step() has finished executing */#define IDA_BASE 0x10000000
#define db_handles_base_offset 0x222F3FC
BOOL SQLite3_Backup_Init_Patched = FALSE;struct dbStruct {DWORD dbhandle;wchar_t* dbname;
};static vector<dbStruct> dbhandles;typedef int(__cdecl* Sqlite3_open)(const char*, DWORD*);
typedef DWORD(__cdecl* Sqlite3_backup_init)(DWORD, const char*, DWORD, const char*);
typedef int(__cdecl* Sqlite3_backup_step)(DWORD, int);
typedef int(__cdecl* Sqlite3_backup_remaining)(DWORD);
typedef int(__cdecl* Sqlite3_backup_pagecount)(DWORD);
typedef int(__cdecl* Sqlite3_sleep)(int);
typedef int(__cdecl* Sqlite3_backup_finish)(DWORD);
typedef int(__cdecl* Sqlite3_errcode)(DWORD);
typedef int(__cdecl* Sqlite3_close)(DWORD);DWORD OffsetFromIdaAddr(DWORD idaAddr) {return idaAddr - IDA_BASE;
}static DWORD GetWeChatWinBase() {DWORD WeChatWinBase = (DWORD)GetModuleHandle(L"WeChatWin.dll");return WeChatWinBase;
}static void GetHandles() {DWORD WeChatWinBase = GetWeChatWinBase();DWORD SqlHandleBaseAddr = *(DWORD*)(WeChatWinBase + db_handles_base_offset);DWORD SqlHandleBeginAddr = *(DWORD*)(SqlHandleBaseAddr + 0x1888);DWORD SqlHandleEndAddr = *(DWORD*)(SqlHandleBaseAddr + 0x188C);wstring dbnames = L"";while (SqlHandleBeginAddr < SqlHandleEndAddr) {DWORD dwHandle = *(DWORD*)SqlHandleBeginAddr;SqlHandleBeginAddr += 0x4;// 做一下简单的去重if (dbnames.find((wchar_t*)(*(DWORD*)(dwHandle + 0x78)), 0) != wstring::npos)continue;dbStruct db = { 0 };db.dbname = (wchar_t*)(*(DWORD*)(dwHandle + 0x78));dbnames += (wchar_t*)(*(DWORD*)(dwHandle + 0x78));db.dbhandle = *(DWORD*)(dwHandle + 0x64);dbhandles.push_back(db);}
}static BOOL CreateConsole(void) {if (AllocConsole()) {AttachConsole(GetCurrentProcessId());FILE* retStream;freopen_s(&retStream, "CONOUT$", "w", stdout);if (!retStream) throw std::runtime_error("Stdout redirection failed.");freopen_s(&retStream, "CONOUT$", "w", stderr);if (!retStream) throw std::runtime_error("Stderr redirection failed.");return 0;}return 1;
}int __cdecl backupDb(DWORD pDb,                    /* Database to back up */const char* zFilename,      /* Name of file to back up to */DWORD myMain,int address_sqlite3_open,int address_sqlite3_backup_init,int address_sqlite3_backup_step,int address_sqlite3_backup_remaining,int address_sqlite3_backup_pagecount,int address_sqlite3_sleep,int address_sqlite3_backup_finish,int address_sqlite3_errcode,int address_sqlite3_close,void(*xProgress)(int, int)  /* Progress function to invoke */
) {int rc;                      /* Function return code */DWORD pFile = 0;             /* Database connection opened on zFilename */DWORD pBackup = 0;             /* Backup handle used to copy data */Sqlite3_open p_Sqlite3_open = (Sqlite3_open)address_sqlite3_open;Sqlite3_backup_init p_Sqlite3_backup_init = (Sqlite3_backup_init)address_sqlite3_backup_init;Sqlite3_backup_step p_Sqlite3_backup_step = (Sqlite3_backup_step)address_sqlite3_backup_step;Sqlite3_backup_remaining p_Sqlite3_backup_remaining = (Sqlite3_backup_remaining)address_sqlite3_backup_remaining;Sqlite3_backup_pagecount p_Sqlite3_backup_pagecount = (Sqlite3_backup_pagecount)address_sqlite3_backup_pagecount;Sqlite3_sleep p_Sqlite3_sleep = (Sqlite3_sleep)address_sqlite3_sleep;Sqlite3_backup_finish p_Sqlite3_backup_finish = (Sqlite3_backup_finish)address_sqlite3_backup_finish;Sqlite3_errcode p_Sqlite3_errcode = (Sqlite3_errcode)address_sqlite3_errcode;Sqlite3_close p_Sqlite3_close = (Sqlite3_close)address_sqlite3_close;rc = p_Sqlite3_open((const char*)zFilename, &pFile);if (rc == SQLITE_OK) {pBackup = p_Sqlite3_backup_init(pFile, (const char*)myMain, pDb, (const char*)myMain);if (pBackup) {do {rc = p_Sqlite3_backup_step(pBackup, 5);xProgress(p_Sqlite3_backup_remaining(pBackup),p_Sqlite3_backup_pagecount(pBackup));if (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {p_Sqlite3_sleep(50);}} while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);(void)p_Sqlite3_backup_finish(pBackup);}rc = p_Sqlite3_errcode(pFile);}(void)p_Sqlite3_close(pFile);return rc;
}VOID PatchSQLite3_Backup_Init() {if (SQLite3_Backup_Init_Patched)return;// patch掉这块指令,绕过`backup is not supported with encrypted databases`DWORD address_sqlite3_backup_init_patch_offset = OffsetFromIdaAddr(0x1131C110 + 0x52);DWORD patchAddress = GetWeChatWinBase() + address_sqlite3_backup_init_patch_offset;const int nopLen = 22;BYTE nopData[nopLen];for (int i = 0; i < nopLen; i++) {nopData[i] = 0x90;}WriteProcessMemory(GetCurrentProcess(), (LPVOID)patchAddress, nopData, nopLen, 0);SQLite3_Backup_Init_Patched = TRUE;return;
}void XProgress(int a, int b)
{printf("备份进度: %d/%d\n", b - a, b);return;
}int BackupSQLiteDB(DWORD DbHandle, const char* BackupFile)
{DWORD wxBaseAddress = GetWeChatWinBase();PatchSQLite3_Backup_Init();cout << "开始备份,文件保存至: " << BackupFile << endl;DWORD address_sqlite3_open = wxBaseAddress + OffsetFromIdaAddr(0x1138ACD0);DWORD address_sqlite3_backup_init = wxBaseAddress + OffsetFromIdaAddr(0x1131C110);DWORD address_sqlite3_backup_step = wxBaseAddress + OffsetFromIdaAddr(0x1131C510);DWORD address_sqlite3_sleep = wxBaseAddress + OffsetFromIdaAddr(0x1138B510);DWORD address_sqlite3_backup_finish = wxBaseAddress + OffsetFromIdaAddr(0x1131CB50);DWORD address_sqlite3_close = wxBaseAddress + OffsetFromIdaAddr(0x113880A0);DWORD address_sqlite3_backup_remaining = wxBaseAddress + OffsetFromIdaAddr(0x1131CC50);DWORD address_sqlite3_backup_pagecount = wxBaseAddress + OffsetFromIdaAddr(0x1131CC60);DWORD address_sqlite3_errcode = wxBaseAddress + OffsetFromIdaAddr(0x11389970);const char* myMain = "main";int rc = backupDb(DbHandle,BackupFile,(DWORD)myMain,address_sqlite3_open,address_sqlite3_backup_init,address_sqlite3_backup_step,address_sqlite3_backup_remaining,address_sqlite3_backup_pagecount,address_sqlite3_sleep,address_sqlite3_backup_finish,address_sqlite3_errcode,address_sqlite3_close,XProgress);cout << "备份完成: " << BackupFile << endl;return rc;
}void TestBackUp() {CreateConsole();PatchSQLite3_Backup_Init();GetHandles();for (unsigned int i = 0; i < dbhandles.size(); i++) {printf("dbname: %ws\n", dbhandles[i].dbname);char* dbname = new char[wcslen(dbhandles[i].dbname) + 1];if (dbname) {WideCharToMultiByte(CP_ACP, 0, dbhandles[i].dbname, -1, dbname, wcslen(dbhandles[i].dbname) + 1, 0, 0);BackupSQLiteDB(dbhandles[i].dbhandle, (const char*)dbname);delete[] dbname;dbname = NULL;}break;}
}

PC微信逆向--在线备份数据库相关推荐

  1. PC微信逆向--定位备份sqlite数据库相关函数

    文章目录 写在前面 备份函数 编写测试程序 配置环境 编译 OD调试 结果 OD地址 IDA地址 写在后面 写在前面 上一篇文章,介绍了如何使用找到的数据库句柄和sqlite3_exec函数执行SQL ...

  2. PC微信逆向--调用sqlite3_exec执行SQL

    文章目录 写在前面 回顾 sqlite3_exec 回调函数 函数指针 编写代码 注入的DLL 注入程序 输出结果 写在后面 写在前面 上一篇文章,实现了定位保存微信数据库句柄的容器和微信内部的sql ...

  3. PC微信逆向--定位sqlite3_exec和数据库句柄

    写在前面 最近在做PC端微信逆向,搞定了基本的收发消息,通讯录获取等,这期间遇到一个小小的问题,从通讯录获取到的内容不全,除非登录后手动点击过某个好友,不然获取不到头像.V3等,所以产生了解密数据库的 ...

  4. pc微信逆向最新3.9.2.23版本

    最近想做个微信机器人,就研究了pc端的微信逆向,发现网上开源的项目挺多的,但是都是一些老版本的,3.7版本的基本上很少,3.8的更少了,现在3.9版本开源的很少.不过还是发现了两个开源项目,特分享备忘 ...

  5. PC微信逆向--看看哪些好友删除了自己

    文章目录 写在前面 微信版本 资料 Duilib界面 定位发送CALL 具体原理 关键代码 写在后面 写在前面 搞微信逆向也有一段时间了,实现了发送文本.图片.文件.名片.xml文章消息,以及获取好友 ...

  6. 【原创】PC微信逆向分析のWeTool内部探秘

    作者:zmrbak(赵庆明老师) 前言: 先不说微信在社交领域的霸主地位,我们仅从腾讯公司所透露的在研发微信过程中踩过的无数的坑,以及公开的与微信相关的填坑的源代码中,我们可以感受到,单从技术上讲,微 ...

  7. PC微信逆向:两种姿势教你解密数据库文件

    文章目录 定位数据库文件密码 定位数据库密钥的思路 获取数据库密钥的实战分析 CreateFileW断点 常见错误 排查堆栈 排查堆栈地址 单步跟踪 用代码实现解密数据库 编译选项 解密代码 实际效果 ...

  8. PC微信逆向:使用HOOK获取好友列表和群列表

    文章目录 获取好友列表的切入点 定位查询好友信息的函数 定位微信号的地址 一次错误的尝试 再次查找目标函数 成功定位获取好友信息的函数 定位获取好友列表的函数 示例代码 实际效果 预告 获取好友列表目 ...

  9. PC微信逆向:分析微信发送文件call

    文章目录 发送文件call的结构体参数分析 组合数据call的分析 定位发送文件的call 接上一篇文章,PCXX逆向:发送与接收消息的分析与代码实现:https://blog.csdn.net/qq ...

最新文章

  1. 解析jsonarra_使用JSONReader或JSONObject / JSONArray解析JSON数据
  2. 一个不错的git学习网站
  3. Zookeeper 生产实践的一些经验分享
  4. LUA: lua基础.
  5. Cocos2d-x 3.2 异步动态加载 -- 保卫萝卜开发总结
  6. 采用Atlas+Keepalived实现MySQL读写分离、读负载均衡【转载】
  7. Codeforces1080F. Katya and Segments Sets
  8. Python 源码剖析(二)—— 第一次修改 Python 源代码
  9. CSS + DIV 让页脚始终保持在页面底部
  10. python中流程图的基本元素_面试干货:成为Python程序员的终极指南!(内附回答)...
  11. 关于local storage及session storage 应用问题
  12. unity 特效shader下载_Unity shader消融特效——(1)逻辑节点篇
  13. DRBD+Heratbeat+NFS高可用文件共享存储
  14. DJ-ZBS2漏电继电器
  15. 最小生成树 刘汝佳模板
  16. 数据分析案例 |【01】电影数据分析
  17. bandgap分析(原理、trimming、非线性、chopper)
  18. 谷歌3d卫星地图下载
  19. 【计算机视觉算法岗面经】“吐血”整理:2019秋招资料
  20. oracle查询和编写数据字典

热门文章

  1. 黑马程序员Java零基础视频教程笔记-Java基础概念
  2. Virtualbox源码分析20 NEM(Hyper-V兼容)1 Hyper-V架构和API介绍
  3. 并联串联混合的电压和电流_二极管串、并联图文分析,选对方案太重要了!
  4. 靶机渗透练习91-Grotesque:2
  5. 简述3D点云配准算法
  6. 下载视频-you-get
  7. 解决一个八阿哥bug的方法
  8. 高校挑选校园智能门锁指南,学校后勤管理者速看
  9. Apple Pay入门
  10. 归因分析计算因子贡献度常见的方法