天天看點

PC微信逆向--定位備份sqlite資料庫相關函數

文章目錄

    • 寫在前面
    • 備份函數
    • 編寫測試程式
      • 配置環境
      • 編譯
    • OD調試
    • 結果
      • OD位址
      • IDA位址
    • 寫在後面

寫在前面

上一篇文章,介紹了如何使用找到的資料庫句柄和sqlite3_exec函數執行SQL,本篇文章,來嘗試定位微信中備份sqlite資料庫的相關函數,為下一篇文章要實作的線上備份做鋪墊。

備份函數

開始找之前,要明确需要找的目标,先看一段别人寫的備份函數:

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;
}
           

這段代碼需要的三個參數,分别是需要備份的資料庫句柄、備份後儲存的檔案名,以及一個回調函數。

句柄我們已經拿到,儲存的檔案名和回調函數是自定義的,三個參數沒有需要特别注意的,函數裡面用到的幾個sqlite函數是關注的重點。

作為一個半吊子,無法解釋為什麼用到這幾個函數,也不再深究,隻需要知道它可以正常工作就好。整理一下:

sqlite3_open // 打開資料庫
sqlite3_backup_init // 初始化擷取一個備份對象
sqlite3_backup_remaining // 還剩餘需要備份的頁數
sqlite3_backup_pagecount // 備份的總頁數
sqlite3_sleep // 睡眠
sqlite3_backup_finish // 完成備份
sqlite3_errcode // 應該是檢查是否有錯誤
sqlite3_close // 關閉資料庫
           

編寫測試程式

配置環境

在準備階段已經下載下傳了sqlite3 3.28.0的源碼包,解壓後将其整理一下,标頭和源碼分開:

PC微信逆向--定位備份sqlite資料庫相關函數

現在,使用VS2019建立一個空項目,将sqlite3中的标頭和源代碼都包含進去:

添加包含目錄

PC微信逆向--定位備份sqlite資料庫相關函數

添加源檔案

PC微信逆向--定位備份sqlite資料庫相關函數

在第一篇文章找到的資料裡,前輩們幫我們踩了一些坑,在編譯之前,需要在

sqlite3.h

中添加一處宏定義:

#ifndef SQLITE3_H
#define SQLITE3_H
// 添加這一句
#define SQLITE_CORE 1
#include <stdarg.h>     /* Needed for the definition of va_list */
           

此外,需要将以下檔案排除出項目:

fts1.c

fts2.c

fts3_tokenizer.c

geopoly.c

icu.c

tclsqlite.c

最後,關閉内聯函數擴充,避免函數入口被編譯器優化掉:

PC微信逆向--定位備份sqlite資料庫相關函數

編譯

接下來編寫測試程式,并在OD中調試找到函數特征碼。測試程式如下:

#include <iostream>
#include <windows.h>
#include "sqlite3.h"

int create(sqlite3** db) {
	int rc = sqlite3_open("test.db", db);
	string sql = "CREATE TABLE IF NOT EXISTS Test (bin BLOB)";
	rc = sqlite3_exec(*db, sql.c_str(), NULL, NULL, NULL);
	return rc;
}

int main() {
    sqlite3* db = NULL;
    int rc = create(&db);
    return 0;
}
           

平台配置選擇Release,Win32(x86),然後右鍵項目->生成,等待編譯完成。

OD調試

編譯完成後,得到了測試程式,接下來要在OD中進行分析,這裡不建議使用吾愛版OD,它對函數加了很多修飾符(不知道是否可以通過設定取消),而看雪提供的OllyICE不存在此問題。

程式啟動後會立刻斷下,不需要運作,隻需按Ctrl+G,輸入函數名并跳轉:

PC微信逆向--定位備份sqlite資料庫相關函數

跳轉到目标函數入口:

00AEBCF0 >/.  55            push    ebp
00AEBCF1  |.  8BEC          mov     ebp, esp
00AEBCF3  |.  8B55 0C       mov     edx, dword ptr [ebp+C]
00AEBCF6  |.  8B4D 08       mov     ecx, dword ptr [ebp+8]
00AEBCF9  |.  6A 00         push    0
00AEBCFB  |.  6A 06         push    6
00AEBCFD  |.  E8 4EF9FFFF   call    openDatabase
00AEBD02  |.  83C4 08       add     esp, 8
00AEBD05  |.  5D            pop     ebp
00AEBD06  \.  C3            retn
           

然後,使用特征碼

8B 55 0C 8B 4D 08 6A 00 6A 06

在IDA中搜尋(可以在OD中選中彙編代碼,右鍵->二進制->二進制複制來擷取特征碼),IDA中可以按

Alt+b

打開二進制搜尋視窗:

PC微信逆向--定位備份sqlite資料庫相關函數

搜尋到的位址:

PC微信逆向--定位備份sqlite資料庫相關函數

輕按兩下檢視:

PC微信逆向--定位備份sqlite資料庫相關函數

隻能說完全一樣。

sqlite3_open

的位址就确定了,為

WeChatWin.dll + 0x138ACD0

這個找起來是如此的簡單,但并不是所有的都這麼簡單,特征碼檢索不到時,可以嘗試以下辦法:

  1. 比對特殊的立即數,比如sqlite3經常用到的

    0x4B771290、0xA029A697、0xF03B7906

  2. 字元串,比如

    sqlite3_backup_init

    可以使用

    source and destination must be distinct

    來定位。
  3. 交叉引用,比如

    sqlite3_close

    調用了

    sqlite3Close

    ,就可以先定位到

    sqlite3Close

    ,然後根據引用關系找到

    sqlite3_close

  4. 參數比對,當搜尋出的位址過多時,可以根據源碼中函數所需的參數數量(IDA中函數頭部有arg和var,是參數和局部變量)來确定到底是哪一個。
  5. 位址距離比對,比如在OD中找到

    sqlite3_open

    的位址是100,

    sqlite3_close

    位址是300,在IDA中找到的

    sqlite3_open

    位址是1000,那麼IDA中

    sqlite3_close

    的位址應該在1200附近,雖然無法确定具體位址,但是這個相對距離可以幫助我們在一堆函數中快速篩選。

結果

剩下的函數就不寫具體的找法了,都可以通過函數名在OD裡直接跳轉。IDA中尋址就留作讀者的作業吧,這裡提供一份參考答案:

OD位址

在比對位址距離的時候可以使用。

sqlite3_open = 0091BBC0
sqlite3_backup_init = 008CDF40
sqlite3_backup_step = 008CE2B0
sqlite3_sleep = 0091C110
sqlite3_backup_finish = 008CE760
sqlite3_close = 0091A1B0
sqlite3_backup_remaining = 008CE830
sqlite3_backup_pagecount = 008CE840
sqlite3_errcode = 0091B090
           

IDA位址

微信版本: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
           

寫在後面

本文介紹了如何通過特征碼等方式定位備份所需的函數,下一篇文章,将使用找到的資訊,褪去微信資料庫的神秘面紗,完成資料庫線上備份!

繼續閱讀