PC微信逆向--在线备份数据库

写在前面

在前面三篇文章,已经找到数据库句柄、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
#include
#include
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注入,结果如下:
PC微信逆向--在线备份数据库_第1张图片
因为没有指定绝对路径,所以保存到微信的安装目录下了:
PC微信逆向--在线备份数据库_第2张图片
打开看看:
PC微信逆向--在线备份数据库_第3张图片
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;
	}
}

你可能感兴趣的:(软件逆向,数据库,微信,sqlite,逆向)