在前面三篇文章,已经找到数据库句柄、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.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;
}
}