一次逆向fb尋找密碼的記錄及還原相關演算法
首先用peid查殼。
一個ASProtect v1.23RC1 *的殼,然後我到網上搜了下有沒有相關的脫殼教程,在這裡發現了一篇https://bbs.pediy.com/thread-207728.htm 但是他的脫殼方法有些麻煩,而且很多步驟都沒有說明原因,只能自己脫了,首先用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
#include
#include
#include
#include
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\t%x\n", 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\n", v6);
v12 = (WCHAR*)(v6/4 + a1);
printf("%ls\n", v12);
v9 = wcslen(v12);
v10 = v6 + 2 * v9 + 2;
result = v10+2;
if (result > a5)
return 1;
printf("%x\n", 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-757651
轉載請註明來自看雪社群
原文連結:[原創]一次逆向fb尋找密碼的記錄及還原相關演算法-『軟體逆向』-看雪安全論壇
相關文章
- 記一次逆向分析解密還原Class檔案2023-12-05解密
- 通過觸發器尋找密碼錯誤使用者2018-06-06觸發器密碼
- 為什麼還要記密碼2020-04-06密碼
- 演算法筆記04--分治法之尋找最大最小元素2014-11-25演算法筆記
- oracle 11.2.0.4使用dbms_stats收集統計資訊statistics及刪除和還原相關測試之一2017-05-29Oracle
- iOS逆向(1)-密碼學(RSA)2019-03-05iOS密碼學
- 記錄一次CMS的程式碼審計2024-06-26
- 演算法:塔防遊戲中的路徑尋找2014-12-26演算法遊戲
- 尋找場景-是人群但還不是群組2015-05-31
- 某幫的app協議逆向記錄2024-10-28APP協議
- Android 尋找極限編碼的「快感」2019-02-13Android
- Qt核心剖析: 尋找 QObject 的原始碼薦2010-05-27QTObject原始碼
- 記一次MySql重置root密碼無效2022-04-11MySql密碼
- 直播商城原始碼,記一次 js隨機密碼2023-03-08原始碼JS隨機密碼
- FBI 為糟糕密碼使用者支招:可使用簡單的長密碼2020-02-20密碼
- 尋找Java程式碼生成器2013-03-21Java
- FreeBSD忘記root密碼的一次修復(轉)2007-08-11密碼
- wsl 安裝mysql,設定初始密碼及遇到的問題記錄一下2021-09-16MySql密碼
- IOS 逆向開發(一)密碼學 RSA2020-04-05iOS密碼學
- IOS 逆向開發(二)密碼學 HASH2020-04-05iOS密碼學
- Windows 記錄一次磁碟相關的PC卡頓問題2024-10-26Windows
- 在字串中,尋找第一個只出現一次的字元2014-05-11字串字元
- CSS攻擊:記錄使用者密碼2019-02-25CSS密碼
- MySQL 5.7遺忘root密碼重置記錄2017-02-10MySql密碼
- (七)一個尋找陣列中眾數的演算法2016-04-14陣列演算法
- 關於mysql忘記密碼的解決策略2024-03-13MySql密碼
- 【演算法】HashMap相關要點記錄2020-10-31演算法HashMap
- 再記一次經典Net程式的逆向過程2019-08-03
- 記一次Net軟體逆向的過程(經典)2019-06-21
- 記一次公司mssql server密碼頻繁被改的事件2020-09-25SQLServer密碼事件
- 記一次百萬行WPF專案程式碼的重構記錄2022-05-13
- 尋找JAVA人材!2008-11-26Java
- 記一次Android逆向之旅(入門向)2020-04-07Android
- 第七章-尋找軟體的註冊碼2015-11-15
- 尋找圖的強連通分量:tarjan演算法簡單理解2019-07-04演算法
- 演算法尋找陣列中的第二大數2014-04-26演算法陣列
- 記錄一次測開面試題記錄2020-09-16面試題
- EBS apps, applsys 的關係及密碼更改2014-06-12APP密碼