溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

PC微信逆向分析の繞過加密訪問SQLite數(shù)據(jù)庫

發(fā)布時間:2020-04-08 16:20:56 來源:網(wǎng)絡(luò) 閱讀:1190 作者:趙慶明老師 欄目:編程語言

PC微信逆向分析の繞過加密訪問SQLite數(shù)據(jù)庫


作者:zmrbak(趙慶明老師)


前言:


微信,無疑是國內(nèi)目前最為流行的應(yīng)用軟件,其在社交領(lǐng)域的霸主地位依然無人可以撼動!它不但撐起了龐大的騰訊軟件帝國的一角,而且足以讓騰訊傲立于BAT中國互聯(lián)網(wǎng)三巨頭之首。微信,改變了這個世界,而騰訊也成了無數(shù)優(yōu)秀程序員夢想的歸宿。圍繞著微信這個軟件的延伸,騰訊也無私地為代碼世界貢獻(xiàn)了近百個優(yōu)秀的開源項目。


從古到今,任何一個巨人,都是站在前一輩巨人的肩膀上而成長為新的巨人。百度如此,阿里如此,騰訊亦是如此。SQLite數(shù)據(jù)庫,一個不為人熟知,但已超過10000億個設(shè)備在使用的開源數(shù)據(jù)庫,已經(jīng)滲入人們生活的各個環(huán)節(jié)。目前超過11億個活躍的微信號,每時每刻都在與這個深藏功與名的SQLite數(shù)據(jù)庫共舞著。


由于微信的巨大成功,從而成為眾多癡迷技術(shù)的探秘者的實驗品。同樣,嵌入微信內(nèi)部那個深藏功名的SQLite數(shù)據(jù)庫,也成為這些人的又一個解剖對象。接下來,準(zhǔn)備好我們的工具,讓我們一起來窺探其中的奧妙!


微信中的數(shù)據(jù)庫


無論是在微信安裝還是運(yùn)行的時候,普通用戶是不會覺察到微信中居然還有一個默默無聞的SQLite數(shù)據(jù)庫。就算在微信的安裝目錄中,你也不會發(fā)現(xiàn)有任何對于SQLite數(shù)據(jù)庫的描述、說明和感謝(基于SQLite的許可協(xié)議,不允許以SQLite的名義來推銷產(chǎn)品),但是卻在用戶的文件夾下(C:\Users\xxxxxx\Documents\WeChat Files\xxxx\Msg\)留下一堆以db為擴(kuò)展名的文件。對于細(xì)心的探索者來說,微信留下來的任何蛛絲馬跡,都會變成重要的探秘線索。


這些db文件,都是經(jīng)過加密處理的。即使你懷疑它是一個SQLite數(shù)據(jù)庫文件,但是你根本無法用任何一個SQLite管理器打開。雖然網(wǎng)上有不少的文章告訴你,去到微信中尋找那個密碼,然后使用某某工具進(jìn)行解密還原。先不說這些工具各種各樣的莫名其妙的問題,就算你確實可以順利搞到密碼,再到完成了解密,再進(jìn)行訪問,其實已經(jīng)晚了半拍。當(dāng)然,你要相信,騰訊的那些頂級程序員,會在保證軟件功能和性能的情況下,會為這些探秘者設(shè)置各種各樣的障礙。至少我們知道,那些db文件的確是一個個SQLite數(shù)據(jù)庫,只是經(jīng)過了加密處理而已。


窺探SQLite數(shù)據(jù)庫


SQLite數(shù)據(jù)庫是一個開源的軟件,也就是說,任何人都可以免費地獲取它的源代碼,對它進(jìn)行研究、分析,當(dāng)然還有修改。如果你要把你修改的內(nèi)容“貢獻(xiàn)”給SQLite官方,那么你必須申明你放棄你修改的這些代碼的“版權(quán)”,不過你提供的代碼基本不會原樣照搬,而是被SQLite官方按照自己的標(biāo)準(zhǔn)重寫一次(開源,但不開放)。當(dāng)然,如果你要修改后自己用的話,那就隨便你啦,SQLite官方并不會找你要錢。即便如此,g---o--o--g--l--e依然每年給予SQLite巨額的贊助,當(dāng)然國內(nèi)騰訊、阿里等這些巨頭也毫不吝嗇。雖然這個軟件包括源代碼在內(nèi)都是免費開放的,但由于有不少的巨頭為其撐腰,就算SQLite不收你的錢,它也會活得很好。如果你非要交錢買個許可的話,那就花6000美元買一個吧,官方稱這些錢會用于資助SQLite的持續(xù)改進(jìn)和支持。


既然SQLite數(shù)據(jù)庫是開放源代碼的,而且還有來自微信開發(fā)團(tuán)隊的嚴(yán)苛加密,那么將其與微信一起研究,則更為令人振奮。登上SQLite的官方主頁,http://www.sqlite.org,你可以隨時把SQLite誕生以來近20年的各個版本抓來研究一番。網(wǎng)站上還有各種示例,教你如何使用SQLite數(shù)據(jù)庫,詳盡的各種解說,如果你愿意,你可以通過這個網(wǎng)站了解到它的各種之末細(xì)節(jié)和詳盡的實現(xiàn)方式。雖然,網(wǎng)站沒提供中文版,不過,語言并不是障礙,瀏覽器的翻譯功能,可以讓你了解個八九不離十。


一個簡單的SQLite示例


如果你還不知道這個SQLite數(shù)據(jù)庫怎么用,網(wǎng)站上提供了TCL的示例代碼和C語言的示例代碼。現(xiàn)摘抄C示例代碼如下(代碼來源:https://www.sqlite.com/quickstart.html)。


01? #include <stdio.h>

02? #include <sqlite3.h>

03??

04? static int callback(void *NotUsed, int argc, char **argv, char **azColName){

05? ? int i;

06? ? for(i=0; i<argc; i++){

07? ? ? printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");

08? ? }

09? ? printf("\n");

10? ? return 0;

11? }

12??

13? int main(int argc, char **argv){

14? ? sqlite3 *db;

15? ? char *zErrMsg = 0;

16? ? int rc;

17??

18? ? if( argc!=3 ){

19? ? ? fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);

20? ? ? return(1);

21? ? }

22? ? rc = sqlite3_open(argv[1], &db);

23? ? if( rc ){

24? ? ? fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));

25? ? ? sqlite3_close(db);

26? ? ? return(1);

27? ? }

28? ? rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);

29? ? if( rc!=SQLITE_OK ){

30? ? ? fprintf(stderr, "SQL error: %s\n", zErrMsg);

31? ? ? sqlite3_free(zErrMsg);

32? ? }

33? ? sqlite3_close(db);

34? ? return 0;

35? }


示例很簡單,也很完善。由于我們的目的是為了探索和研究,因此,我們在確保程序示例可用的情況下,再繼續(xù)精簡。我們刪除了很多代碼,只剩下核心的幾條:


01? #include <stdio.h>

02? #include <sqlite3.h>

03??

04? static int callback(void *NotUsed, int argc, char **argv, char **azColName){

06? ? for(int i=0; i<argc; i++){

07? ? ? printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");

08? ? }

10? ? return 0;

11? }

12??

13? int main(int argc, char **argv){

14? ? sqlite3 *db;

22? ? sqlite3_open(argv[1], &db);

28? ? sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);

33? ? sqlite3_close(db);

34? ? return 0;

35? }


先從main函數(shù)開始,首先定義一個sqlite3的db作為數(shù)據(jù)庫的句柄,然后讓執(zhí)行sqlite3_open打開數(shù)據(jù)庫,接下來執(zhí)行sqlite3_exec查詢數(shù)據(jù)庫,最后執(zhí)行sqlite3_close關(guān)閉數(shù)據(jù)庫。在sqlite3_exec執(zhí)行期間,使用回調(diào)函數(shù)callback來處理查詢的結(jié)果。這,就是一個完整的流程。


執(zhí)行sqlite3_open打開數(shù)據(jù)庫這個操作,要執(zhí)行不少的操作,相對比價耗費系統(tǒng)資源。如果一個軟件在運(yùn)行過程中要不斷地查詢數(shù)據(jù)庫,修改數(shù)據(jù)庫,那么最優(yōu)的方式就是打開后不要關(guān)閉,等到軟件關(guān)閉的時候再關(guān)閉數(shù)據(jù)庫。這樣可以節(jié)約系統(tǒng)資源,也可以提高數(shù)據(jù)庫的響應(yīng)速度。


繞過加密訪問SQLite數(shù)據(jù)庫


無論是給數(shù)據(jù)庫加上密碼,還是使用其它更強(qiáng)有力的方式為數(shù)據(jù)庫加密,無非是為了防止數(shù)據(jù)庫被非法操作。但是,由于基于性能的考慮,應(yīng)用軟件打開SQLite數(shù)據(jù)庫后,只有等到應(yīng)用程序被關(guān)閉的時候,才去關(guān)閉數(shù)據(jù)庫。那么在整個應(yīng)用軟件運(yùn)行期間,SQLite數(shù)據(jù)庫一旦被打開,則將一直保持打開狀態(tài)。也就是說,上面示例的db,在程序還未調(diào)用sqlite3_close函數(shù)前,一直是有效的,隨時可以接受sqlite3_exec函數(shù)的查詢操作。這個查詢并不是指select,而是指所有可能的sql語句。我們暫且不討論具有破壞性的操作,如增加(INSERT)、修改(UPDATE)、刪除(DELETE)等,在這里我們只來討論“無破壞性”的“查”(SELECT)的操作。


數(shù)據(jù)庫的加密驗證,往往只在于打開數(shù)據(jù)庫的時候,一旦數(shù)據(jù)庫被打開,剩下的操作,則不再對加密進(jìn)行驗證。因此,我們等數(shù)據(jù)庫打開之后,獲取db這個數(shù)值,然后就可以“非法”地調(diào)用sqlite3_exec函數(shù),來執(zhí)行我們期望的操作。如果這樣操作,那么無論軟件設(shè)計人員設(shè)計的任何加密方式,都形同虛設(shè)。也就是說,我們可以輕松地實現(xiàn)“繞過加密來訪問SQLite數(shù)據(jù)庫”。


為了實現(xiàn)“繞過加密來訪問SQLite數(shù)據(jù)庫”,接下來需要解決三個問題。

  • 如何取得已打開的數(shù)據(jù)庫的句柄db的具體數(shù)值?

  • 可被非法地調(diào)用的sqlite3_exec函數(shù)在哪里?

  • 如何非法地調(diào)用sqlite3_exec函數(shù)?


接下來,讓我們依次來解決這三個問題。


如何取得打開數(shù)據(jù)庫的句柄?


如果從正向編程的思維來思考的話,似乎沒有答案。但是如果用逆向的思維,你會發(fā)現(xiàn),其實很簡單,在調(diào)用sqlite3_open函數(shù)的時候,其中一個參數(shù)就是db,這個db的數(shù)值,就是我們正在尋找的打開數(shù)據(jù)庫的句柄。因此,這個問題又轉(zhuǎn)換成,在什么地方調(diào)用了sqlite3_open函數(shù)。那么,只要我們在調(diào)用完sqlite3_open函數(shù)之后,馬上獲取db的值,這個問題就解決了。


如果你使用過OD或者IDA,你就知道,這些軟件可以幫你分析出,在程序的什么地方調(diào)用了這個函數(shù),而且每一個調(diào)用的地方都可以幫你分析出來。如果再配合inline hook,取數(shù)據(jù)就如同探囊取物一般容易。


我們知道,在調(diào)用sqlite3_open函數(shù)的時候,必然會打開一個文件。在Windows系統(tǒng)中,必然會調(diào)用Windows中的CreateFileW函數(shù),而這個函數(shù)可以被OD、IDA等逆向工具所識別。因此,通過倒推的方式,就能順藤摸瓜找到sqlite3_open函數(shù)。如果使用OD來動態(tài)調(diào)試的話,sqlite3_open就非常容易找到,而且還可以找到調(diào)用這個函數(shù)的代碼段,同時還可以直觀地觀察到某內(nèi)存地址中的具體數(shù)值。因此,db的值就可以很容易地獲取。如果你再編寫了inline hook后,只要程序調(diào)用了sqlite3_open函數(shù),就可以馬上獲取db的值。


sqlite3_exec函數(shù)在哪里?


在軟件中往往會執(zhí)行一些sql語句,而sqlite3_exec函數(shù)對多條函數(shù)進(jìn)行了包裝,讓程序員執(zhí)行sql語句更為簡單。因此,在應(yīng)用軟件中會有大量的sqlite3_exec函數(shù)調(diào)用。這恰好是一個切入點。


sqlite3_exec函數(shù)的第一個參數(shù)是數(shù)據(jù)庫句柄,其實也就是一個DWORD,第二個參數(shù)是一條被執(zhí)行的sql語句,其實就是一個字符串的地址。這些字符串,往往在程序員編程的時候手工寫入的,并且嵌入到軟件之中,而這些字符串在OD、IDA等這些軟件中,可以很容易地識別出來。從而成為找到這個函數(shù)的切入點。


比如在軟件中尋找“select * from”之類的sql語句,就可以簡單地定位到sqlite3_exec函數(shù),從而確定它在應(yīng)用軟件中的具體地址。


如何調(diào)用sqlite3_exec


既然我們已經(jīng)獲得到數(shù)據(jù)庫的句柄db的值,而且已經(jīng)找到和sqlite3_exec的地址,接下來我們就可以來“非法”地調(diào)用這個sqlite3_exec函數(shù)了。首先,我們來了解一下這個函數(shù)的所需的參數(shù):


sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);


第二個參數(shù),是一個查詢的sql語句。我們可以定義一個sql的字符串即可,比如“select * from sqlite_master”。由于這條語句是用來查詢這個數(shù)據(jù)庫的結(jié)構(gòu)的,因此,在任何一個sqlite數(shù)據(jù)庫中都可以執(zhí)行成功。當(dāng)然,你也可以寫一個你自己喜歡的sql語句。為了確保一定能查詢到信息,我們就用這個字符串。


第三個參數(shù),是一個回調(diào)函數(shù),參照SQLite官方的示例代碼編寫。


第四個參數(shù),是為這個回調(diào)函數(shù)提供的參數(shù),如果沒有,或者不需要提供,那么可以直接填寫0或NULL。


第五個參數(shù),返回執(zhí)行的錯誤信息,如果不需要的話,可以填寫為0或者NULL。


我們只是找到了sqlite3_exec在軟件中的地址而已,但地址并不是一個函數(shù),我們也無法直接用字符sqlite3_exec來調(diào)用這個函數(shù)。但是,我們可以通過C語言內(nèi)嵌匯編來調(diào)用這個函數(shù),也可以用“函數(shù)”指針的方式來調(diào)用。使用匯編語言,不是很直觀,因此使用函數(shù)指針,是一個比較好的方法。


我們從SQLite源代碼中找到sqlite3_exec函數(shù),把它復(fù)制過來,然后做一些修改。把sqlite3_exec第一個字符變成大寫Sqlite3_exec,加上typedef、__cdecl*,刪除形參,再將db所在的形參改為DWORD:


typedef int(__cdecl* Sqlite3_exec)(

? DWORD,? ? ? ? ? ? ? ? /* The database on which the SQL executes */

? const char *,? ? ? ? ? ? ?/* The SQL to be executed */

? sqlite3_callback,? ? ? ? ? /* Invoke this callback routine */

? void *,? ? ? ? ? ? ? ? ? /* First argument to xCallback() */

? char **? ? ? ? ? ? ? ? ? /* Write error messages here */

);


假如sqlite3_exec的地址是:0x12345678,那么我們可以這樣定義和調(diào)用:


Sqlite3_ exec p_Sqlite3_ exec = (Sqlite3_ exec) 0x12345678;

p_Sqlite3_ exec(db, zSql, callback, 0, 0);


運(yùn)行結(jié)果示例


?PC微信逆向分析の繞過加密訪問SQLite數(shù)據(jù)庫

PC微信逆向分析の繞過加密訪問SQLite數(shù)據(jù)庫

PC微信逆向分析の繞過加密訪問SQLite數(shù)據(jù)庫

PC微信逆向分析の繞過加密訪問SQLite數(shù)據(jù)庫

?

?總結(jié):


很多初次接觸到逆向的人,會被黑壓壓的匯編代碼、一串串的C代碼、還有風(fēng)格詭異的e語言所嚇倒。常常有人問我“逆向分析很難嗎?”。我也套用一句經(jīng)典來回答:“天下事有難易乎?為之,則難者亦易矣;不為,則易者亦難矣!”


逆向分析,如同玩猜謎活動,當(dāng)你看到一個個謎底慢慢浮現(xiàn)的時候,只有身在其中的人,才能享受其中的刺激和樂趣。作為看客,永遠(yuǎn)無緣體驗這份樂趣。你并不是孤身一人,你還有我們。加入我們的QQ交流群456197310,一起在玩這個其樂無窮的猜謎活動。


源碼分享

https://github.com/zmrbak/SQLiteReverse



交流QQ群:

456197310 點擊鏈接加入群聊【軟件逆向分析入門】


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI