一次逆向fb尋找密碼的記錄及還原相關演算法

一次逆向fb尋找密碼的記錄及還原相關演算法

6 人贊了文章

首先用peid查殼。

一個ASProtect v1.23RC1 *的殼,然後我到網上搜了下有沒有相關的脫殼教程,在這裡發現了一篇bbs.pediy.com/thread-20 但是他的脫殼方法有些麻煩,而且很多步驟都沒有說明原因,只能自己脫了,首先用od載入程序,單步了幾下,發現一個pushad

然後用esp定律,f9 斷在這個地方

接著單步幾下,發現沒有oep特徵,由此可知這個加了幾層殼,繼續單步,發現單步很久都沒發現啥,如果你有耐心,會發現到後面還會出現幾次pushad ,大概要單步幾千次吧,,於是我又用了幾次esp定律,繼續單步,發現這玩意一直帶著我在幾個函數之間轉圈圈,,我當時調這個應該是調了一天,,這麼一直轉下去不是辦法,只能用點特殊的辦法,讓他幫我們還原到最接近oep的地方,我對esp有特徵的下硬體斷點,然後記錄f9的次數,和啥時候該下斷點,

這是當時調試記錄的筆記=-=。倒數第四行那裡我發現,f9 71次時代碼已經被還原。於是就從70次開始單步,本來以為離oep已經很近了的,結果一單步下來又是幾千條指令,單步了幾個小時還是沒發現oep。指令還是在幾個函數間轉圈圈,本來我打算繼續用之前的辦法再縮小到oep的距離的。可這樣下去不是辦法,於是,嘗試直接f9,從堆棧中倒著來找oep,選了幾個堆棧返回地址,

在這個地址往上翻了翻就翻到了oep。

一個典型的vc++的oep,然後開始脫殼分析。用od載入脫完殼的程序。搜索所有字元串

看見了這幾個網址,點進去,再用ida打開,找到相關函數f5。

signed int __thiscall sub_406540(void *this, int a2){ int v2; // ebx@1 int v3; // esi@1 int v4; // ebp@2 int v5; // edx@2 const char **v6; // edi@3 int v7; // ebp@11 const char **v8; // edi@12 int v9; // edx@17 DWORD v10; // eax@20 const char *v11; // eax@21 bool v12; // bl@22 bool v13; // bl@23 int v14; // ebp@26 int v15; // edx@26 const char **v16; // edi@27 wchar_t *v18; // eax@54 const char *v19; // eax@56 bool v20; // bl@57 bool v21; // bl@58 int v22; // ebp@61 int v23; // edx@61 const char **v24; // edi@62 const char *v25; // [sp+28h] [bp-C74h]@1 int i; // [sp+2Ch] [bp-C70h]@1 LPVOID ppv; // [sp+30h] [bp-C6Ch]@1 DWORD dwIndex; // [sp+34h] [bp-C68h]@3 int v29; // [sp+38h] [bp-C64h]@1 DWORD cchValueName; // [sp+3Ch] [bp-C60h]@1 DWORD cbData; // [sp+40h] [bp-C5Ch]@12 char v32; // [sp+44h] [bp-C58h]@1 int v33; // [sp+48h] [bp-C54h]@3 int v34; // [sp+4Ch] [bp-C50h]@1 int v35; // [sp+58h] [bp-C44h]@23 int v36; // [sp+5Ch] [bp-C40h]@21 HKEY phkResult; // [sp+60h] [bp-C3Ch]@18 int v38; // [sp+64h] [bp-C38h]@52 char v39; // [sp+68h] [bp-C34h]@52 LPCWSTR lpWideCharStr; // [sp+6Ch] [bp-C30h]@54 CHAR ValueName; // [sp+90h] [bp-C0Ch]@20 BYTE Data; // [sp+490h] [bp-80Ch]@20 int v43; // [sp+C98h] [bp-4h]@1 cchValueName = (DWORD)this; v25 = (const char *)dword_5FAB30; v43 = 0; v29 = 0; ppv = 0; LOBYTE(v43) = 2; sub_5810E6(&v32); LOBYTE(v43) = 3; CStringArray::SetAtGrow((CStringArray *)&v32, v34, "http://www.facebook.com"); CStringArray::SetAtGrow((CStringArray *)&v32, v34, "http://www.facebook.com/"); CStringArray::SetAtGrow((CStringArray *)&v32, v34, "https://login.facebook.com"); CStringArray::SetAtGrow((CStringArray *)&v32, v34, "https://login.facebook.com/"); CStringArray::SetAtGrow((CStringArray *)&v32, v34, "http://www.facebook.com/login.php"); CStringArray::SetAtGrow((CStringArray *)&v32, v34, "http://www.facebook.com/index.php"); CStringArray::SetAtGrow((CStringArray *)&v32, v34, "https://www.google.com/accounts/servicelogin"); CStringArray::SetAtGrow((CStringArray *)&v32, v34, "https://www.regnow.com/vendorpriv/"); CStringArray::SetAtGrow((CStringArray *)&v32, v34, "https://www.regnow.com/affiliatepriv/"); CStringArray::SetAtGrow((CStringArray *)&v32, v34, "http://127.0.0.1/phpmyadmin/"); CStringArray::SetAtGrow((CStringArray *)&v32, v34, "http://www.yandex.ru/"); v2 = a2; v3 = 0; for ( i = 0; v3 < v34; i = v3 ) { v4 = *(_DWORD *)(v2 + 8); v5 = 0; if ( v4 > 0 ) { v6 = *(const char ***)(v2 + 4); dwIndex = *(_DWORD *)(v33 + 4 * v3); do { if ( !strcmp((const char *)dwIndex, *v6) ) break; ++v5; ++v6; } while ( v5 < v4 ); v3 = i; v2 = a2; } if ( v5 == v4 ) sub_581340(v2, v4, v33 + 4 * v3); ++v3; } i = 0; if ( *(_DWORD *)(cchValueName + 8) > 0 ) { do { v7 = *(_DWORD *)(v2 + 8); dwIndex = 0; if ( v7 > 0 ) { v8 = *(const char ***)(v2 + 4); cbData = *(_DWORD *)(*(_DWORD *)(cchValueName + 4) + 4 * i); do { if ( !strcmp((const char *)cbData, *v8) ) break; ++v8; ++dwIndex; } while ( (signed int)dwIndex < v7 ); } if ( dwIndex == v7 ) sub_581340(v2, v7, *(_DWORD *)(cchValueName + 4) + 4 * i); v9 = *(_DWORD *)(cchValueName + 8); ++i; } while ( i < v9 ); } if ( !RegOpenKeyExA(HKEY_CURRENT_USER, "Software\Microsoft\Internet Explorer\TypedURLs", 0, 1u, &phkResult) )// 打開註冊表 { dwIndex = 0; while ( 1 ) { v10 = dwIndex++; Data = 0; ValueName = 0; cbData = 2048; cchValueName = 1024; if ( RegEnumValueA(phkResult, v10, &ValueName, &cchValueName, 0, 0, &Data, &cbData) )// 遍歷註冊表,第一個參數是打開的註冊表句柄,第二個參數是遍歷的index,第三個參數是接收註冊表鍵名的緩衝區,第四個參數指定第三個緩衝區的大小,倒數第二個是鍵值的緩衝區,最後一個參數指定前一個的大小 break; CString::operator=((CString *)&v25, (char *)&Data);// 把鍵值賦值給v25 v11 = *(const char **)sub_57F15A(&v25, (int)&v36, 4u); LOBYTE(v43) = 4; if ( !strcmp(v11, "http") || (v12 = strcmp(*(const char **)sub_57F15A(&v25, (int)&i, 3u), (const char *)&dword_5F882C) == 0, sub_58643C(&i), v12) || (v13 = strcmp(*(const char **)sub_57F15A(&v25, (int)&v35, 3u), (const char *)&off_5F8828) == 0, sub_58643C(&v35), v13) ) { v13 = 1; } LOBYTE(v43) = 3; sub_58643C(&v36); if ( v13 ) { v14 = 0; v15 = *(_DWORD *)(a2 + 8); if ( v15 > 0 ) { v16 = *(const char ***)(a2 + 4); do { if ( !strcmp(v25, *v16) ) // 如果鍵值和a2偏移4的那個變數不相等,則跳出循環 break; ++v14; ++v16; // 跟下一條字元串進行比較 } while ( v14 < v15 ); } if ( v14 == v15 ) // 若沒找到fb官網的鍵值 sub_581340(a2, v15, (int)&v25); // 調用這個函數 } } RegCloseKey(phkResult); } if ( ppv ) (*(void (__stdcall **)(LPVOID))(*(_DWORD *)ppv + 8))(ppv);// 釋放對象 ppv = 0; if ( CoCreateInstance(&rclsid, 0, 1u, &riid, &ppv) < 0 )// 創建一個com對象 { LOBYTE(v43) = 2; CStringArray::~CStringArray((CStringArray *)&v32); LOBYTE(v43) = 1; if ( ppv ) (*(void (__stdcall **)(LPVOID))(*(_DWORD *)ppv + 8))(ppv);// 釋放對象 LOBYTE(v43) = 0; if ( v29 ) (*(void (__stdcall **)(int))(*(_DWORD *)v29 + 8))(v29);LABEL_49: v43 = -1; sub_58643C(&v25); return 0; } if ( v29 ) (*(void (__stdcall **)(int))(*(_DWORD *)v29 + 8))(v29); v29 = 0; if ( !ppv ) sub_449BF9(-2147467261); if ( (*(int (__stdcall **)(LPVOID, int *))(*(_DWORD *)ppv + 28))(ppv, &v29) < 0 )// 調用進程中的某個方法,得到一個新的函數地址 { LOBYTE(v43) = 2; CStringArray::~CStringArray((CStringArray *)&v32); LOBYTE(v43) = 1; if ( ppv ) (*(void (__stdcall **)(LPVOID))(*(_DWORD *)ppv + 8))(ppv);// 釋放對象 LOBYTE(v43) = 0; if ( v29 ) (*(void (__stdcall **)(int))(*(_DWORD *)v29 + 8))(v29); goto LABEL_49; } while ( 1 ) { if ( !v29 ) sub_449BF9(-2147467261); if ( (*(int (__stdcall **)(int, signed int, char *, int *))(*(_DWORD *)v29 + 12))(v29, 1, &v39, &v38) < 0 || !v38 )// 得到ie最近的歷史記錄, break; v18 = wcschr(lpWideCharStr, 0x3Fu); if ( v18 ) *v18 = 0; CString::operator=((CString *)&v25, lpWideCharStr); v19 = *(const char **)sub_57F15A(&v25, (int)&cbData, 4u); LOBYTE(v43) = 5; if ( !strcmp(v19, "http") || (v20 = strcmp(*(const char **)sub_57F15A(&v25, (int)&v36, 3u), (const char *)&dword_5F882C) == 0, sub_58643C(&v36), v20) || (v21 = strcmp(*(const char **)sub_57F15A(&v25, (int)&v35, 3u), (const char *)&off_5F8828) == 0, sub_58643C(&v35), v21) ) { v21 = 1; } LOBYTE(v43) = 3; sub_58643C(&cbData); if ( v21 ) { v22 = 0; v23 = *(_DWORD *)(a2 + 8); if ( v23 > 0 ) // 遍歷他,對比程序前面的幾個http鏈接 { v24 = *(const char ***)(a2 + 4); do { if ( !strcmp(*v24, v25) ) break; ++v22; ++v24; } while ( v22 < v23 ); } if ( v22 == v23 ) sub_581340(a2, v23, (int)&v25); } } LOBYTE(v43) = 2; CStringArray::~CStringArray((CStringArray *)&v32); LOBYTE(v43) = 1; if ( ppv ) (*(void (__stdcall **)(LPVOID))(*(_DWORD *)ppv + 8))(ppv); LOBYTE(v43) = 0; if ( v29 ) (*(void (__stdcall **)(int))(*(_DWORD *)v29 + 8))(v29); v43 = -1; sub_58643C(&v25); return 1;}

分析之後我們發現他是從註冊表的

這個註冊表裡存著的是ie收藏的url。

查找比對url,然後再調用ieframe.dll裡面的某個函數來獲取歷史記錄,至於是什麼函數目前我還沒辦法知道。ida也分析不出來..可能只能去調那個dll?看功能來分析?不過我後來獲取了一下ieframe的導出表。查看了一下導出函數,想到可能可以從基址偏移來看調用的是哪個函數。他調用那個函數取出來的也是一些url。是ie的歷史url緩存,這個函數大概的意思就是把url都取出來然後push到一個類似vector的結構裡面存著。解密的關鍵並不在這裡。在字元串搜索里我們還看見了一串註冊表的位置,

打開相應位置看看,

發現是一些加密數據,可能關鍵就在這裡了,往下翻,發現調用了這個函數

具體代碼請自行分析,跟進

繼續跟進,

發現在這個函數里調用了關鍵函數,

CryptUnprotectData,其中第一個參數是加密數據的結構體,圖中a2便是加密數據的地址,a3是加密時的附加嫡,為url的登陸鏈接的位元組碼,解密完了的數據在pDataout結構體里,用od調試看看,

這個是最後那個解密完數據存放的地址,

解密之前,

解密之後,前四個位元組是此解密數據的大小,後四個位元組是解密數據的地址,

大概就是這些數據了,我們可以很明顯的看到在最後面存放著我們需要的密碼,test123,密碼是我自己測試設置的,

然後此函數處理這些解密數據,得到密碼

跟進

#define _CRT_SECURE_NO_WARNINGS#pragma comment(lib,"Crypt32.lib")#include <Windows.h>#include <dpapi.h>#include <iostream>#include <cwchar>#include <atlstr.h>unsigned int convert(int* a1, unsigned int a5);int main() { DATA_BLOB DataIn; std::cout << sizeof(DATA_BLOB) << std::endl; DATA_BLOB DataOut; DATA_BLOB pOptionalEntropy; char dest[100] = {0}; BYTE bpkey[310] { 0x01,0x00,0x00,0x00,0xD0,0x8C,0x9D,0xDF,0x01,0x15,0xD1,0x11,0x8C,0x7A,0x00,0xC0, 0x4F,0xC2,0x97,0xEB,0x01,0x00,0x00,0x00,0xC0,0x6E,0xDE,0xB1,0x74,0x52,0xCB,0x46, 0x9F,0xF5,0x92,0x73,0x39,0x9D,0x52,0x72,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, 0x00,0x00,0x10,0x66,0x00,0x00,0x00,0x01,0x00,0x00,0x20,0x00,0x00,0x00,0x03,0x9F, 0x65,0xE0,0x95,0xEC,0x94,0xE1,0x55,0x78,0x8D,0x0F,0x7F,0xF6,0x79,0x98,0x16,0xEF, 0xB3,0xB8,0x11,0x2A,0x6E,0x9B,0xD1,0xCC,0x37,0xC5,0xFB,0x36,0x4F,0x7F,0x00,0x00, 0x00,0x00,0x0E,0x80,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x00,0x00,0x00,0x0C,0x92, 0xA1,0xB9,0x50,0x34,0x45,0xE9,0xF4,0xEC,0x1D,0x8E,0xF2,0x51,0x6D,0xA1,0x95,0x1A, 0xB9,0xE5,0x93,0x27,0xEB,0x44,0x61,0x37,0xAD,0xD4,0x73,0xB4,0x04,0xC6,0x60,0x00, 0x00,0x00,0xA1,0x60,0x4E,0xA4,0x84,0x8A,0x7A,0x21,0x3A,0xC9,0x32,0x94,0xD0,0x0E, 0x94,0x77,0x22,0x2B,0x05,0x42,0xB5,0xE7,0x3B,0x4B,0x2F,0x7E,0x1B,0x8A,0x77,0x79, 0x30,0x98,0xFD,0xBB,0x06,0x22,0xAE,0x8C,0x0F,0xAA,0x9D,0xA8,0x27,0x57,0x79,0x18, 0x8E,0x6A,0x8E,0xBD,0x4C,0x43,0xEF,0xF8,0x5B,0x7C,0xED,0x8E,0x1A,0xA0,0x63,0x8D, 0x9E,0xBA,0xE2,0x60,0xA8,0x99,0xCE,0xFC,0xE7,0xE8,0x80,0xC0,0xFA,0x71,0x58,0x6A, 0xC2,0x08,0x9A,0x4F,0x1B,0xFC,0x47,0x88,0x56,0x0D,0xE4,0x06,0x1A,0x53,0x66,0xFC, 0x70,0x7C,0x40,0x00,0x00,0x00,0x40,0xEB,0xD0,0x8F,0xE7,0x12,0xF6,0x7F,0xAB,0x9A, 0x92,0x00,0xDA,0xA1,0x1B,0xB4,0xF2,0x66,0x54,0xE7,0x92,0x10,0xFD,0xB0,0xC0,0x6B, 0xEC,0xDF,0x20,0x11,0x63,0xB1,0xA0,0x08,0xE0,0x31,0xE5,0x4A,0x05,0xF8,0x59,0x61, 0x91,0x2D,0x93,0xC0,0x90,0xB0,0x41,0xBF,0x14,0x53,0xEA,0x65,0x1E,0xC5,0x95,0x98, 0xFA,0x5A,0xB8,0x70,0xFA,0x64 }; BYTE url[66]{ 0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,0x3A,0x00,0x2F,0x00,0x2F,0x00,0x77,0x00, 0x77,0x00,0x77,0x00,0x2E,0x00,0x33,0x00,0x76,0x00,0x62,0x00,0x6F,0x00,0x6F,0x00, 0x6B,0x00,0x73,0x00,0x2E,0x00,0x63,0x00,0x6F,0x00,0x6D,0x00,0x2F,0x00,0x6C,0x00, 0x6F,0x00,0x67,0x00,0x69,0x00,0x6E,0x00,0x2E,0x00,0x70,0x00,0x68,0x00,0x70,0x00 }; DataIn.pbData = (byte*)bpkey; DataIn.cbData = 310; DataOut.cbData = 0; DataOut.pbData = 0; pOptionalEntropy.pbData = (BYTE*)url; pOptionalEntropy.cbData = 66; if (CryptUnprotectData( &DataIn, 0, &pOptionalEntropy, // Optional entropy NULL, // Reserved NULL, // Here, the optional // prompt structure is not // used. 1, &DataOut)) { convert((int*)DataOut.pbData, DataOut.cbData); //printf("%s", result); LocalFree(DataOut.pbData); } else { printf("%d", GetLastError()); printf("Decryption error!"); } system("pause"); return 0;}unsigned int convert(int* a1, unsigned int a5){ int *v5; // esi@1 unsigned int result; // eax@1 int v7; // edi@1 BYTE * v8; // eax@2 size_t v9; // eax@3 int v10; // ebx@3 WCHAR* v13; size_t v11; // edi@4 WCHAR* v12; // [sp+8h] [bp-4h]@3 v5 = (int *)(a1 + 1); printf("%x %x
", v5,a1); int v6 = *v5 + *a1; if (v6 + 2 < a5) { v8 = (BYTE*)a1 + a5; //printf("%x", v8); *(v8 - 1) = 0; *(v8 - 2) = 0; //do //{ printf("%x
", v6); v12 = (WCHAR*)(v6/4 + a1); printf("%ls
", v12); v9 = wcslen(v12); v10 = v6 + 2 * v9 + 2; result = v10+2; if (result > a5) return 1; printf("%x
", v10); v13 = (WCHAR*)(v10 + (BYTE*)a1); printf("%ls", v13); v11 = 2 * wcslen((const wchar_t *)(v10/4 + a1)); //(**a2)(a3, a4, v12, v10 + v5); //} while (v6 + 2 < a5); } return 0;}

上面是我還原的關鍵演算法,

運行結果:

另外吐槽一下od的udd保存的文件注釋那些竟然只看文件名,不看路徑,我在od里寫的注釋在載入其他程序的時候全都沒了,心塞......

本文由看雪論壇 clay 原創,看雪ID:clay bbs.pediy.com/user-7576

轉載請註明來自看雪社區

原文鏈接:[原創]一次逆向fb尋找密碼的記錄及還原相關演算法-『軟體逆向』-看雪安全論壇

推薦閱讀:

安卓逆向菜鳥的初體驗
正反合璧,挖掘war3資源地址詳細過程

TAG:密碼 | 演算法 | 軟體逆向工程 |