第1課:二進(jìn)制安全逆向介紹
二進(jìn)制安全,能干什么
-
逆向分析:
負(fù)責(zé)成品軟件的技術(shù)原理. 比如分析競(jìng)品軟件,吸取技術(shù)上的優(yōu)點(diǎn),進(jìn)行技術(shù)難點(diǎn)公關(guān) -
病毒分析:
負(fù)責(zé)分析病毒樣本.研究惡意代碼的技術(shù)手段等工作.主要是在安全公司,尤其是在殺毒軟件公司需求較多.如360 、騰訊電腦管家等. -
漏洞挖掘分析:
負(fù)責(zé)分析漏洞樣本,或者漏洞的挖掘.目前二進(jìn)制的主要方向.涉及范圍廣,從主流瀏覽器 虛擬機(jī) 內(nèi)核到IOT 還有android 和 IOS移動(dòng)平臺(tái). -
移動(dòng)安全:
負(fù)責(zé)移動(dòng)端安全.如移動(dòng)端的漏洞挖掘,還有加殼混淆等 -
安全開(kāi)發(fā):
包含較廣.比如硬件平臺(tái),內(nèi)核安全等.一般也是安全公司.如防火墻 主動(dòng)防御系統(tǒng) 反外掛等
逆向與開(kāi)發(fā)的對(duì)比
以C語(yǔ)言舉例,C語(yǔ)言代碼屬于高級(jí)語(yǔ)言代碼,不能直接被處理器執(zhí)行,這時(shí)候需要由編譯器將C語(yǔ)言代碼翻譯成處理器可以直接理解的機(jī)器代碼。
編譯流程: C語(yǔ)言代碼 -> … -> 匯編語(yǔ)言代碼 -> … -> 機(jī)器代碼
機(jī)器代碼會(huì)按照一定的排列方式存儲(chǔ)在可執(zhí)行文件中,最常見(jiàn)的可執(zhí)行文件有 Windows 的 exe 文件以及 Linux 的 ELF 文件。
逆向工程操作對(duì)象就是這一類可執(zhí)行文件,逆向即通過(guò)機(jī)器代碼反推程序的原理。機(jī)器碼毫無(wú)可讀性,可執(zhí)行文件編排也十分復(fù)雜,所以需要一款強(qiáng)大的輔助軟件輔助逆向分析過(guò)程,使分析者可以專注于代碼本身的邏輯層面。
IDA 生成高級(jí)語(yǔ)言代碼的流程:
機(jī)器代碼 -> … -> 匯編代碼 -> … -> 高級(jí)語(yǔ)言代碼
IDA 定位 Main 函數(shù)
IDA 對(duì)符號(hào)重新命名
這道題演示了如何使用 IDA 載入并分析一個(gè)可執(zhí)行的二進(jìn)制文件,并通過(guò)字符串定位的方式在茫茫的代碼海洋中找到 main 函數(shù),我們又使用 IDA 的偽代碼功能生成 main 函數(shù)的偽代碼,并修復(fù)沒(méi)有名字的函數(shù),使得程序可讀性得到極大提升。
思路: 運(yùn)行程序 -> 收集字符串 -> 尋找字符串引用代碼 -> 生成偽代碼 -> 修復(fù)匿名函數(shù) -> 分析程序邏輯 -> 得到 Flag
視頻
aHR0cHM6Ly93d3cuYmlsaWJpbGkuY29tL3ZpZGVvL0JWMTdMNDExczdHWC8=
課件
aHR0cHM6Ly9naXRodWIuY29tL1N5Y2xvdmVyVGVhbS9TeWNSZXZMZWFybg==
第2題:簡(jiǎn)單的加密算法
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
size_t i; // [esp+D0h] [ebp-114h]
char Str1[260]; // [esp+DCh] [ebp-108h] BYREF
printf("Hi CTFer,Input your flag:");
scanf("%s", Str1);
for ( i = 0; i < j__strlen(Str1); ++i )
++Str1[i];
if ( !j__strcmp(Str1, "gmbh|ZPV`GJOE`JU`IBIB~") )
printf("you are right!\n");
else
printf("you are wrong!\n");
return 0;
}
結(jié)果
target = bytearray(b'gmbh|ZPV`GJOE`JU`IBIB~')
for x in range(len(target)):
target[x] -=1
print(target.decode())
flag{YOU_FIND_IT_HAHA}
第3題:簡(jiǎn)單的加密算法2
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
size_t i; // [esp+D0h] [ebp-114h]
char v5[260]; // [esp+DCh] [ebp-108h] BYREF
sub_456502("[5] Hi CTFer,Input your flag:");
sub_4554EF("%s", v5);
for ( i = 0; i < j__strlen(v5); ++i )
v5[i] ^= i;
if ( !j__strcmp(v5, Str2) )
sub_456502("you are right!\n");
else
sub_456502("you are wrong!\n");
return 0;
}
結(jié)果
data = [0x66, 0x6D, 0x63, 0x64, 0x7F, 0x5C, 0x49, 0x52, 0x57, 0x4F, 0x43, 0x45, 0x48, 0x52, 0x47, 0x5B, 0x4F, 0x59, 0x53, 0x5B, 0x55, 0x68]
for i in range(len(data)):
data[i] ^= i
print(bytes(data).decode())
flag{YOU_FIND_IT_HAHA}
第4題:Base64 編碼逆向
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
size_t v3; // eax
size_t i; // [esp+190h] [ebp-31Ch]
char Str1[520]; // [esp+19Ch] [ebp-310h] BYREF
char Str[260]; // [esp+3A4h] [ebp-108h] BYREF
printf("[4] Hi CTFer,Input your flag:");
scanf("%s", Str);
for ( i = 0; i < j__strlen(Str); ++i )
Str[i] ^= i;
v3 = j__strlen(Str);
sub_455A94(Str, Str1, v3);
if ( !j__strcmp(Str1, "Zm1jZH9cSVJXT0NFSFJHW09ZU1tVaA==") )
printf("you are right!\n");
else
printf("you are wrong!\n");
return 0;
}
結(jié)果
import base64
data = base64.b64decode("Zm1jZH9cSVJXT0NFSFJHW09ZU1tVaA==")
data = bytearray(data)
for x in range(len(data)):
data[x] ^= x
print(data.decode())
flag{YOU_FIND_IT_HAHA}
第5題:Base64 變表逆向
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
size_t input_len; // eax
size_t i; // [esp+190h] [ebp-31Ch]
char Str1[520]; // [esp+19Ch] [ebp-310h] BYREF
char input[260]; // [esp+3A4h] [ebp-108h] BYREF
printf("[4] Hi CTFer,Input your flag:");
scanf("%s", input);
for ( i = 0; i < j__strlen(input); ++i )
input[i] ^= i;
input_len = j__strlen(input);
sub_455A94(input, Str1, input_len);
if ( !j__strcmp(Str1, "Wj1gWE9xPSGUQ0KCPCGET09WR1qSzZ==") )
printf("you are right!\n");
else
printf("you are wrong!\n");
return 0;
}
進(jìn) sub_455A94() 函數(shù),找下算法特征,推斷是 BASE64 算法
標(biāo)準(zhǔn)編碼表(T4)
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
魔改之后的編碼表(T5)
ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/
所以導(dǎo)致不對(duì)
- base64 編碼原理 類似于進(jìn)制轉(zhuǎn)換,256 -> 64 進(jìn)制
- 數(shù)據(jù)取每 6 bit -> v (0- 64) -> 從編碼表中取出下標(biāo) v 對(duì)應(yīng)的字符
分析思路
Wj1gWE9xPSGUQ0KCPCGET09WR1qSzZ==
W 在 T5 中的位置對(duì)應(yīng)到 T4 則是 Z
j 在 T5 中的位置對(duì)應(yīng)到 T4 則是 m
因此將把 Wj 替換成 Zm,后續(xù)也是同樣的操作,最后用 T4 的腳本即可解密 Flag
結(jié)果
import base64
data = 'Wj1gWE9xPSGUQ0KCPCGET09WR1qSzZ' # 去掉 ==
T4 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
T5 = 'ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/'
result = ""
for ch in data:
result += T4[T5.index(ch)]
result = bytearray(base64.b64decode(result + "=="))
for i in range(len(result)):
result[i] ^= i
print(result.decode())
flag{YOU_FIND_IT_HAHA}
C語(yǔ)言:base64 算法
int __cdecl sub_45A3F0(int a1, int a2, int a3)
{
unsigned __int8 v4; // [esp+D3h] [ebp-1Dh]
unsigned __int8 v5; // [esp+D3h] [ebp-1Dh]
int v6; // [esp+DCh] [ebp-14h]
int v7; // [esp+DCh] [ebp-14h]
int v8; // [esp+DCh] [ebp-14h]
int v9; // [esp+DCh] [ebp-14h]
int v10; // [esp+DCh] [ebp-14h]
int v11; // [esp+DCh] [ebp-14h]
int v12; // [esp+E8h] [ebp-8h]
v12 = 0;
v6 = 0;
while ( v12 < a3 )
{
*(_BYTE *)(v6 + a2) = *((_BYTE *)off_529000 + (((int)*(unsigned __int8 *)(v12 + a1) >> 2) & 0x3F));
v7 = v6 + 1;
v4 = (16 * *(_BYTE *)(v12 + a1)) & 0x30;
if ( v12 + 1 >= a3 )
{
*(_BYTE *)(v7 + a2) = *((_BYTE *)off_529000 + v4);
v8 = v7 + 1;
*(_BYTE *)(v8 + a2) = 61;
*(_BYTE *)(++v8 + a2) = 61;
v6 = v8 + 1;
break;
}
*(_BYTE *)(v7 + a2) = *((_BYTE *)off_529000 + (((int)*(unsigned __int8 *)(v12 + a1 + 1) >> 4) & 0xF | v4));
v9 = v7 + 1;
v5 = (4 * *(_BYTE *)(v12 + a1 + 1)) & 0x3C;
if ( v12 + 2 >= a3 )
{
*(_BYTE *)(v9 + a2) = *((_BYTE *)off_529000 + v5);
v10 = v9 + 1;
*(_BYTE *)(v10 + a2) = 61;
v6 = v10 + 1;
break;
}
*(_BYTE *)(v9 + a2) = *((_BYTE *)off_529000 + (((int)*(unsigned __int8 *)(v12 + a1 + 2) >> 6) & 3 | v5));
v11 = v9 + 1;
*(_BYTE *)(v11 + a2) = *((_BYTE *)off_529000 + (*(_BYTE *)(v12 + a1 + 2) & 0x3F));
v6 = v11 + 1;
v12 += 3;
}
*(_BYTE *)(v6 + a2) = 0;
return a2;
}
第6題:IDA 動(dòng)態(tài)調(diào)試
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
size_t i; // [esp+190h] [ebp-340h]
char input[264]; // [esp+3A4h] [ebp-12Ch] BYREF
char str1[32]; // [esp+4ACh] [ebp-24h] BYREF
qmemcpy(str1, &unk_50DE50, 034u);
printf("[6] Hi CTFer,Input your flag:");
scanf("%s", input);
for ( i = 0; i < j__strlen(str1); ++i )
str1[i] = ((i + 1) ^ str1[i]) - i;
if ( !j__strcmp(str1, input) )
printf("you are right\n");
else
printf("you are wrong\n");
return 0;
}
動(dòng)態(tài)調(diào)試前要確定調(diào)試的目的
1、獲取運(yùn)行時(shí)數(shù)據(jù)
2、獲取執(zhí)行流程
3、驗(yàn)證猜想是否正確
例如要獲取數(shù)據(jù),就需要提前設(shè)置斷點(diǎn)。 IDA 支持在偽代碼層面調(diào)試,所以可以直接在 IDA 的偽代碼里面設(shè)置斷點(diǎn)。
點(diǎn)擊小藍(lán)點(diǎn)之后,這行代碼就會(huì)變成紅色,就代表成功設(shè)置斷點(diǎn)。
Windows本地exe - 動(dòng)態(tài)調(diào)試
首次使用(需先配置)
選第3個(gè)【Local Windows debugger】
配置完成后,去啟動(dòng)
如果【安全提醒】 在CTF題目情況下,都是安全的,默認(rèn)信任。未知的程序,最好去虛擬機(jī)里調(diào)試。
此時(shí)斷點(diǎn)
告訴 ida 這是字符串(按鍵:A)
拿到
Flag{This_IS_T7_DEBUG_EASY}
第7題:IDA 動(dòng)態(tài)調(diào)試解RC4(繞過(guò)反調(diào)試)
有一些算法的加密與解密是相同的算法過(guò)程,例如 RC4、部分簡(jiǎn)單的異或算法等。
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
int j; // [esp+31Ch] [ebp-69Ch]
int i; // [esp+328h] [ebp-690h]
size_t v6; // [esp+334h] [ebp-684h]
char v7[55]; // [esp+340h] [ebp-678h]
char v8; // [esp+377h] [ebp-641h]
unsigned int v9; // [esp+380h] [ebp-638h]
int v10; // [esp+38Ch] [ebp-62Ch]
char Str[520]; // [esp+398h] [ebp-620h] BYREF
char v12[264]; // [esp+5A0h] [ebp-418h] BYREF
char v13[264]; // [esp+6A8h] [ebp-310h] BYREF
char v14[516]; // [esp+7B0h] [ebp-208h] BYREF
__CheckForDebuggerJustMyCode(&unk_49C00F);
j__memset(v14, 0, 0x200u);
j__memset(v13, 0, 0x100u);
j__memset(v12, 0, 0x100u);
j__memset(Str, 0, 0x200u);
v10 = 0;
v9 = 0;
v7[0] = -28;
v7[1] = 21;
v7[2] = -60;
v7[3] = -19;
v7[4] = -90;
v7[5] = 47;
v7[6] = 86;
v7[7] = 16;
v7[8] = -69;
v7[9] = 19;
v7[10] = -21;
v7[11] = -83;
v7[12] = 117;
v7[13] = 86;
v7[14] = -57;
v7[15] = -69;
v7[16] = -69;
v7[17] = -23;
v7[18] = -71;
v7[19] = -52;
v7[20] = 2;
v7[21] = 58;
v7[22] = 80;
v7[23] = -97;
v7[24] = 54;
v7[25] = -112;
v7[26] = 105;
v7[27] = -66;
v7[28] = 124;
v7[29] = 66;
v7[30] = 68;
v7[31] = -54;
v7[32] = -58;
v7[33] = -44;
v7[34] = 36;
v7[35] = 92;
v7[36] = -46;
v7[37] = -71;
v7[38] = 36;
v7[39] = -63;
v7[40] = 24;
v7[41] = -109;
v7[42] = -77;
v7[43] = -22;
sub_42F057(v14);
sub_42C40B("Welcome!! give me your flag:\n");
do
{
v8 = j_j_j___fgetchar();
if ( v8 == 10 )
break;
Str[v9++] = v8;
}
while ( (int)v9 < 44 );
if ( v9 >= 0x200 )
j____report_rangecheckfailure();
Str[v9] = 0;
v6 = j__strlen(Str);
sub_42CEFB(v13, v14, v6);
for ( i = 0; i < 256; ++i )
v12[i] = v13[i];
sub_42D5B8(v13, Str, v6);
for ( j = 0; j < 44; ++j )
{
if ( v7[j] == Str[j] )
++v10;
}
if ( v10 == 44 )
sub_42C40B("Yes, u right!\n");
else
sub_42C40B("no no no\n");
sub_42D0CC("pause");
return 0;
}
改了函數(shù)名
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
int j; // [esp+31Ch] [ebp-69Ch]
int i; // [esp+328h] [ebp-690h]
size_t input_len; // [esp+334h] [ebp-684h]
char v7[55]; // [esp+340h] [ebp-678h]
char v8; // [esp+377h] [ebp-641h]
unsigned int index; // [esp+380h] [ebp-638h]
int v10; // [esp+38Ch] [ebp-62Ch]
char input[520]; // [esp+398h] [ebp-620h] BYREF
char v12[264]; // [esp+5A0h] [ebp-418h] BYREF
char v13[264]; // [esp+6A8h] [ebp-310h] BYREF
char v14[516]; // [esp+7B0h] [ebp-208h] BYREF
__CheckForDebuggerJustMyCode(&unk_50C00F);
j__memset(v14, 0, 0x200u);
j__memset(v13, 0, 0x100u);
j__memset(v12, 0, 0x100u);
j__memset(input, 0, 0x200u);
v10 = 0;
index = 0;
v7[0] = -28;
v7[1] = 21;
v7[2] = -60;
v7[3] = -19;
v7[4] = -90;
v7[5] = 47;
v7[6] = 86;
v7[7] = 16;
v7[8] = -69;
v7[9] = 19;
v7[10] = -21;
v7[11] = -83;
v7[12] = 117;
v7[13] = 86;
v7[14] = -57;
v7[15] = -69;
v7[16] = -69;
v7[17] = -23;
v7[18] = -71;
v7[19] = -52;
v7[20] = 2;
v7[21] = 58;
v7[22] = 80;
v7[23] = -97;
v7[24] = 54;
v7[25] = -112;
v7[26] = 105;
v7[27] = -66;
v7[28] = 124;
v7[29] = 66;
v7[30] = 68;
v7[31] = -54;
v7[32] = -58;
v7[33] = -44;
v7[34] = 36;
v7[35] = 92;
v7[36] = -46;
v7[37] = -71;
v7[38] = 36;
v7[39] = -63;
v7[40] = 24;
v7[41] = -109;
v7[42] = -77;
v7[43] = -22;
sub_49F057((int)v14); // 反調(diào)試
sub_49C40B("Welcome!! give me your flag:\n");
do
{
v8 = j_j_j___fgetchar();
if ( v8 == '\n' )
break;
input[index++] = v8;
}
while ( (int)index < 44 );
if ( index >= 0x200 )
j____report_rangecheckfailure();
input[index] = 0;
input_len = j__strlen(input);
fun1(v13, v14, input_len);
for ( i = 0; i < 256; ++i )
v12[i] = v13[i];
sub_49D5B8(v13, input, input_len); // 推測(cè)核心算法,可理解為黑盒,有輸入輸出的地方
for ( j = 0; j < 44; ++j )
{
if ( v7[j] == input[j] )
++v10;
}
if ( v10 == 44 )
sub_49C40B("Yes, u right!\n");
else
sub_49C40B("no no no\n");
sub_49D0CC("pause");
return 0;
}
反調(diào)試邏輯
_BYTE *__cdecl sub_4325D0(_BYTE *a1)
{
HWND ForegroundWindow; // eax
unsigned __int64 v2; // rax
unsigned __int64 v3; // rax
int v5; // [esp+25Ch] [ebp-438h]
CHAR String[1028]; // [esp+28Ch] [ebp-408h] BYREF
if ( IsDebuggerPresent() )
*a1 = 89;
else
*a1 = 88;
ForegroundWindow = GetForegroundWindow();
GetWindowTextA(ForegroundWindow, String, 1023);
if ( j__strstr(String, "WinDbg")
|| j__strstr(String, "x64dbg")
|| j__strstr(String, "x32dbg")
|| j__strstr(String, "OllyICE")
|| j__strstr(String, "OllyDBG")
|| j__strstr(String, "Immunity") )
{
a1[1] = 111;
}
else
{
a1[1] = 48;
}
SetLastError((DWORD)"12345");
OutputDebugStringW("Test for debugger!");
if ( (char *)GetLastError() == "12345" )
a1[2] = 110;
else
a1[2] = 117;
if ( !CloseHandle((HANDLE)0x1234) && GetLastError() == 6 )
a1[3] = 66;
else
a1[3] = 65;
if ( NtCurrentPeb()->BeingDebugged )
a1[4] = 97;
else
a1[4] = 64;
v2 = __rdtsc();
v5 = v2;
v3 = __rdtsc();
if ( (unsigned int)(v3 - v5) >= 0xFF )
a1[5] = 100;
else
a1[5] = 68;
a1[6] = 0;
return a1;
}
Windows本地exe - 附加進(jìn)程
本地先打開(kāi)【rc4.exe】程序,然后點(diǎn)擊
將會(huì)已附加的形式,去調(diào)試一個(gè)正在運(yùn)行的進(jìn)程
此時(shí)會(huì)停下來(lái),點(diǎn)擊 繼續(xù)運(yùn)行
雙擊 input 變量
此時(shí)可以看到輸入的字符串
請(qǐng)注意:在 c 語(yǔ)言中 ,字符串的結(jié)尾是 0
偽造 44 位字符串【AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA】
重新調(diào)試
雙擊 input
找到內(nèi)存地址【00B6F89C】記錄下來(lái)
單步一下,此時(shí),函數(shù)內(nèi)部已經(jīng)算好
查看內(nèi)存(按鍵:G)
選中 44 位,已 0 結(jié)尾
到 16進(jìn)制【F60DC6D7B7046F0E890DFD835924E8A599C4C8F92B127FB928E05BA06E336AE4B7FA5542F08D11E578E39BD6】
得出結(jié)論
內(nèi)存地址
00B6F89C
輸入值
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
結(jié)果
F60DC6D7B7046F0E890DFD835924E8A599C4C8F92B127FB928E05BA06E336AE4B7FA5542F08D11E578E39BD6
再次重新調(diào)試
- 重新運(yùn)行程序
- 附加進(jìn)程
- 斷住
修改內(nèi)存數(shù)據(jù)
然后
記錄下
內(nèi)存地址
0046F2C8
步過(guò)(按鍵:F8)
在修改內(nèi)存后,發(fā)現(xiàn)是一樣的數(shù)據(jù)。
然后提取 v7 的值,雙擊進(jìn)去,復(fù)制hex
得到結(jié)果【E415C4EDA62F5610BB13EBAD7556C7BBBBE9B9CC023A509F369069BE7C4244CAC6D4245CD2B924C11893B3EA】
再次重新調(diào)試
附加進(jìn)程,輸入44位字符串,并修改內(nèi)存
記錄
內(nèi)存地址
0065F7AC
看到結(jié)果了,雙擊進(jìn)去
轉(zhuǎn)字符串(按鍵:A)
結(jié)果
SYC{Pjx_s_Wom3n_cl0thing_1s_S0oo0o0_cute!1i}
第8題:IDA 代碼修復(fù) & 數(shù)組識(shí)別
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+0h] [ebp-BCh]
char v5; // [esp+0h] [ebp-BCh]
int v6; // [esp+0h] [ebp-BCh]
HANDLE TimerQueue; // [esp+4h] [ebp-B8h]
int v8; // [esp+8h] [ebp-B4h]
HANDLE phNewTimer; // [esp+18h] [ebp-A4h] BYREF
char v10[52]; // [esp+1Ch] [ebp-A0h] BYREF
char Destination[52]; // [esp+50h] [ebp-6Ch] BYREF
char Str[5]; // [esp+84h] [ebp-38h] BYREF
char Source[47]; // [esp+89h] [ebp-33h] BYREF
TimerQueue = CreateTimerQueue();
CreateTimerQueueTimer(&phNewTimer, TimerQueue, Callback, 0, 0, 0x3E8u, 0x20u);
sub_401450("Welcome To XDU\n", v4);
sub_401450("Show me your password\n", v5);
sub_4013C0("%s", (char)Str);
if ( sub_401000(Str, "flag{") != (_DWORD)Str || Source[32] != 125 )
sub_401340(v6, TimerQueue);
sub_401360(Destination, Source, 0x20u);
sub_4011F0(Destination);
sub_401270(&unk_4040C0, Destination, v10);
v8 = strcmp(v10, a23gjf13au98hk3);
if ( v8 )
v8 = v8 < 0 ? -1 : 1;
if ( v8 )
sub_401340(v8, TimerQueue);
sub_401450("Congratulations!!!\n", 0);
DeleteTimerQueueEx(TimerQueue, (HANDLE)0xFFFFFFFF);
return 0;
}
代碼修復(fù)
- 判斷返回值是否有意義
- 判斷參數(shù)的類型
- 判斷局部變量的類型與數(shù)組大小
- …
隨便看個(gè)函數(shù)。交叉引用,查看只有1處調(diào)用
char __cdecl sub_4011F0(char *a1)
{
char result; // al
char *v2; // [esp+4h] [ebp-14h]
char *v3; // [esp+Ch] [ebp-Ch]
int i; // [esp+10h] [ebp-8h]
v3 = a1;
v2 = a1 + 1;
do
result = *v3;
while ( *v3++ );
if ( v3 - v2 != 32 )
sub_401340(v3 - v2, v2);
for ( i = 0; i < 32; ++i )
{
a1[i] ^= LOBYTE(dword_404040[i]);
result = i + 1;
}
return result;
}
但是 ida 認(rèn)為返回是 char 類型,需要 手動(dòng)改下。
改成無(wú)返回類型(按鍵:V)
點(diǎn)擊函數(shù)名,右鍵
改成【void】代表無(wú)返回
也可以點(diǎn)擊函數(shù)名,按 V 鍵
void __cdecl sub_4011F0(char *a1)
{
char *v1; // [esp+Ch] [ebp-Ch]
int i; // [esp+10h] [ebp-8h]
v1 = &a1[strlen(a1) + 1];
if ( v1 - (a1 + 1) != 32 )
sub_401340(v1 - (a1 + 1), a1 + 1);
for ( i = 0; i < 32; ++i )
a1[i] ^= LOBYTE(dword_404040[i]);
}
此時(shí),發(fā)現(xiàn)減少了很多代碼,邏輯就很清晰了。
當(dāng)然也會(huì)有問(wèn)題,進(jìn) sub_401340() 函數(shù),發(fā)現(xiàn) 入?yún)⑷笔Я?/p>
修復(fù)入?yún)㈩愋?/h4>
void __noreturn sub_401340()
{
char savedregs; // [esp+0h] [ebp+0h]
sub_401450("Emmmm...Wrong\n", savedregs);
exit(0);
}
void __noreturn sub_401340()
{
char savedregs; // [esp+0h] [ebp+0h]
sub_401450("Emmmm...Wrong\n", savedregs);
exit(0);
}
只需要:進(jìn)入該函數(shù),按 F5 鍵 ,再 Esc 鍵,再按 F5 鍵,即可恢復(fù)
這樣更少了
void __cdecl sub_4011F0(char *a1)
{
int i; // [esp+10h] [ebp-8h]
if ( strlen(a1) != 32 )
sub_401340();
for ( i = 0; i < 32; ++i )
a1[i] ^= LOBYTE(dword_404040[i]);
}
修改入?yún)㈩愋?,為指?/h4>
再來(lái)找個(gè)函數(shù),改改。
int __cdecl sub_401270(int a1, const char *a2, int a3)
{
signed int v3; // kr00_4
int result; // eax
signed int v5; // [esp+10h] [ebp-8h]
v5 = 0;
v3 = strlen(a2);
while ( v5 < v3 )
{
*(_BYTE *)(a3 + *(_DWORD *)(a1 + 4 * v5)) = a2[v5];
++v5;
}
result = v5 + a3;
*(_BYTE *)(v5 + a3) = 0;
return result;
}
點(diǎn)擊函數(shù)名,按 V 鍵
void __cdecl sub_401270(int a1, const char *a2, int a3)
{
signed int v3; // kr00_4
signed int v4; // [esp+10h] [ebp-8h]
v4 = 0;
v3 = strlen(a2);
while ( v4 < v3 )
{
*(_BYTE *)(a3 + *(_DWORD *)(a1 + 4 * v4)) = a2[v4];
++v4;
}
*(_BYTE *)(v4 + a3) = 0;
}
發(fā)現(xiàn)這樣不顯著,當(dāng)然也不是萬(wàn)能的
回到主函數(shù)
觀察第3個(gè)應(yīng)該是 指針,跟進(jìn)去修改
然后
void __cdecl sub_401270(int a1, const char *a2, char *a3)
{
signed int v3; // kr00_4
signed int v4; // [esp+10h] [ebp-8h]
v4 = 0;
v3 = strlen(a2);
while ( v4 < v3 )
{
a3[*(_DWORD *)(a1 + 4 * v4)] = a2[v4];
++v4;
}
a3[v4] = 0;
}
再來(lái)觀察
sub_401270(&unk_4040C0, Destination, v9);
推斷 第一個(gè)是 取值,也是指針
void __cdecl sub_401270(int a1, const char *a2, char *a3)
{
signed int v3; // kr00_4
signed int i; // [esp+10h] [ebp-8h]
i = 0;
v3 = strlen(a2);
while ( i < v3 )
{
a3[*(_DWORD *)(a1 + 4 * i)] = a2[i];
++i;
}
a3[i] = 0;
}
一個(gè)無(wú)符號(hào)整型數(shù)據(jù)占4個(gè)字節(jié)
a1 + 4 * i = 整數(shù)數(shù)組尋址:1個(gè)元素4字節(jié),第i個(gè)元素偏移量,就是數(shù)組首地址 + 4 * i
第1個(gè)入?yún)⒏臑椤緄nt *】類型
這樣代碼就清晰了,很漂亮
void __cdecl sub_401270(int *a1, const char *a2, char *a3)
{
signed int v3; // kr00_4
signed int i; // [esp+10h] [ebp-8h]
i = 0;
v3 = strlen(a2);
while ( i < v3 )
{
a3[a1[i]] = a2[i];
++i;
}
a3[i] = 0;
}
切回主函數(shù),繼續(xù)觀察
修改字符的存儲(chǔ)長(zhǎng)度(按鍵:Y)
此時(shí),主函數(shù)的邏輯更清晰了
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+0h] [ebp-BCh]
char v5; // [esp+0h] [ebp-BCh]
HANDLE TimerQueue; // [esp+4h] [ebp-B8h]
int v7; // [esp+8h] [ebp-B4h]
HANDLE phNewTimer; // [esp+18h] [ebp-A4h] BYREF
char a3[52]; // [esp+1Ch] [ebp-A0h] BYREF
char xxxx[52]; // [esp+50h] [ebp-6Ch] BYREF
char Str[52]; // [esp+84h] [ebp-38h] BYREF
TimerQueue = CreateTimerQueue();
CreateTimerQueueTimer(&phNewTimer, TimerQueue, Callback, 0, 0, 0x3E8u, 0x20u);
sub_401450("Welcome To XDU\n", v4);
sub_401450("Show me your password\n", v5);
scanf("%s", (char)Str);
if ( strstr_0(Str, "flag{") != Str || Str[37] != 125 )
sub_401340();
get_flag_body(xxxx, &Str[5], 0x20u);
sub_4011F0(xxxx);
sub_401270(a1, xxxx, a3);
v7 = strcmp(a3, a23gjf13au98hk3);
if ( v7 )
v7 = v7 < 0 ? -1 : 1;
if ( v7 )
sub_401340();
sub_401450("Congratulations!!!\n", 0);
DeleteTimerQueueEx(TimerQueue, (HANDLE)0xFFFFFFFF);
return 0;
}
先拿 a1 的值
因?yàn)槭?4個(gè)字節(jié)訪問(wèn)的
來(lái)到
void __cdecl sub_4011F0(char *a1)
{
int i; // [esp+10h] [ebp-8h]
if ( strlen(a1) != 32 )
sub_401340();
for ( i = 0; i < 32; ++i )
a1[i] ^= LOBYTE(dword_404040[i]);
}
因?yàn)槭?低位 表現(xiàn)的
修改
然后
a1 = [0x00000004, 0x0000000F, 0x0000000B, 0x0000001E, 0x0000000E, 0x00000014, 0x0000001F, 0x00000009, 0x00000017, 0x00000002, 0x00000019, 0x0000001C, 0x00000012, 0x00000010, 0x00000000, 0x00000008, 0x00000011, 0x00000001, 0x00000015, 0x00000003, 0x0000000A, 0x0000001D, 0x0000000C, 0x00000016, 0x00000018, 0x0000000D, 0x0000001B, 0x00000005, 0x00000007, 0x00000006, 0x00000013, 0x0000001A]
target = bytearray(b'23gjf13au98hk3a1090zp8qjs41h39jp')
input_xxx = [0] * 32
for i in range(len(target)):
input_xxx[i] = target[a1[i]]
# input_xxx[0] = target[4]
print(input_xxx)
X = [0x00000053, 0x00000045, 0x0000005C, 0x0000001E, 0x00000050, 0x00000013, 0x0000002F, 0x00000078, 0x00000004, 0x00000053, 0x00000058, 0x0000004A, 0x00000043, 0x00000001, 0x00000041, 0x0000002A, 0x00000008, 0x00000040, 0x00000067, 0x0000002F, 0x0000000C, 0x0000004A, 0x00000012, 0x0000002E, 0x00000041, 0x0000006C, 0x00000005, 0x00000054, 0x00000040, 0x00000012, 0x0000005B, 0x0000004F]
for i in range(len(target)):
input_xxx[i] ^= X[i]
a = bytes(input_xxx).decode()
print(a == '5t4t1c_An4lys1s_1s_E4sy_2_me!!!~')
結(jié)果
flag{5t4t1c_An4lys1s_1s_E4sy_2_me!!!~}
第9題:UPX脫殼(待完善)
去看視頻。
IDA pro 反匯編程序,基礎(chǔ)教程
查閱代碼流程視圖 / 匯編代碼 (按鍵:空格)
將常量轉(zhuǎn)換為X進(jìn)制(按鍵:右擊)
Hexadecimal:十六進(jìn)制
Octal:八進(jìn)制
Char:字符(按鍵:R)
查看所有字符串(按鍵:shift + F12)
交叉引用(按鍵:X)
復(fù)制字節(jié)數(shù)組(ida插件)
-
需要安裝【LazyIDA】插件
https://github.com/P4nda0s/LazyIDA.git -
將【LazyIDA.py】放到 ida_pro 主目錄下的 【\plugins】目錄。
修改變量/函數(shù)名(按鍵:N)
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-816168.html
調(diào)試技巧(步入/步過(guò)/直到返回/光標(biāo)位置)
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-816168.html
常用快捷鍵
切換文本視圖與圖表視圖 空格鍵
返回上一個(gè)操作地址 ESC
搜索地址和符號(hào) G
對(duì)符號(hào)進(jìn)行重命名 N
常規(guī)注釋 冒號(hào)鍵
可重復(fù)注釋 分號(hào)鍵
添加標(biāo)簽 Alt+M
查看標(biāo)簽 Ctrl+M
查看段的信息 Ctrl+S
查看交叉應(yīng)用 X
查看偽代碼 F5
搜索文本 Alt+T
搜索十六進(jìn) Alt+B
常用快捷鍵2
a:將數(shù)據(jù)轉(zhuǎn)換為字符串
f5:一鍵反匯編
esc:回退鍵,能夠倒回上一部操作的視圖(只有在反匯編窗口才是這個(gè)作用,如果是在其他窗口按下esc,會(huì)關(guān)閉該窗口)
shift+f12:可以打開(kāi)string窗口,一鍵找出所有的字符串,右擊setup,還能對(duì)窗口的屬性進(jìn)行設(shè)置
ctrl+w:保存ida數(shù)據(jù)庫(kù)
ctrl+s:選擇某個(gè)數(shù)據(jù)段,直接進(jìn)行跳轉(zhuǎn)
ctrl+鼠標(biāo)滾輪:能夠調(diào)節(jié)流程視圖的大小
x:對(duì)著某個(gè)函數(shù)、變量按該快捷鍵,可以查看它的交叉引用
g:直接跳轉(zhuǎn)到某個(gè)地址
n:更改變量的名稱
y:更改變量的類型
/ :在反編譯后偽代碼的界面中寫下注釋
\:在反編譯后偽代碼的界面中隱藏/顯示變量和函數(shù)的類型描述,有時(shí)候變量特別多的時(shí)候隱藏掉類型描述看起來(lái)會(huì)輕松很多
;:在反匯編后的界面中寫下注釋
ctrl+shift+w:拍攝IDA快照
u:undefine,取消定義函數(shù)、代碼、數(shù)據(jù)的定義
動(dòng)態(tài)調(diào)試快捷鍵
F2:下斷點(diǎn)
F3:打開(kāi)程序
F4:運(yùn)行到當(dāng)前光標(biāo)處(可應(yīng)用在跳出 循壞)
F7:?jiǎn)尾讲饺耄ㄟM(jìn)函數(shù))
F8:?jiǎn)尾?步過(guò)
F9;運(yùn)行
F10:打開(kāi)反匯編選項(xiàng)菜單快捷鍵
F12:暫時(shí)停止
Ctrl+F2:重新開(kāi)始
Art+F2:結(jié)束跟蹤
Shift+F2:打開(kāi)附加選項(xiàng)窗口
Shift+F4:打開(kāi)條件對(duì)話窗
Shift+F7:與F7相同,但是如果被調(diào)試程序發(fā)生異常而中止,調(diào)試器會(huì)首先嘗試步入被調(diào)試程序指定的異常處理
Ctrl+F7:自動(dòng)步入,在所有的函數(shù)調(diào)用中一條一條地執(zhí)行命令,斷點(diǎn)或異常時(shí),自動(dòng) 停止
Shift+F8與F8相同,但是如果被調(diào)試程序發(fā)生異常而中止,調(diào)試器會(huì)首先嘗試步過(guò)被調(diào)試程序指定的異常處理
Ctrl+F8:自動(dòng)步過(guò),一條一條的執(zhí)行命令,程序到達(dá)斷點(diǎn),或者發(fā)生異常時(shí),自動(dòng)步過(guò)過(guò)程都會(huì)停止
Shift+F9:與F9相同,但是如果被調(diào)試程序發(fā)生異常而中止,調(diào)試器會(huì)首先嘗試執(zhí)行被調(diào)試程序指定的異常處理
Ctrl+F9:執(zhí)行直到返回,跟蹤程序直到遇到返回,在此期間不進(jìn)入子函數(shù)也不更新CPU數(shù)據(jù)。因?yàn)槌绦蚴且粭l一條命令執(zhí)行的,所以速度可能會(huì)慢一些。按Esc鍵,可以停止跟蹤。
Alt+F9:執(zhí)行直到返回到用戶代碼段,跟蹤程序直到指令所屬于的模塊不在系統(tǒng)目錄中,在此期間不進(jìn)入子函數(shù)也不更新CPU數(shù)據(jù)。按Esc鍵,可以停止跟蹤。
Ctrl+F11:Run跟蹤步入,一條一條執(zhí)行命令,進(jìn)入每個(gè)子函數(shù)調(diào)用,并把寄存器的信息加入到Run跟蹤的存儲(chǔ)數(shù)據(jù)中。Run跟蹤不會(huì)同步更新CPU窗口。
Ctrl+F12 :Run跟蹤。步過(guò),一條一條執(zhí)行命令,但是不進(jìn)入子函數(shù)調(diào)用,并把寄存器的信息加入到Run跟蹤的存儲(chǔ)數(shù)據(jù)中。Run跟蹤不會(huì)同步更新CPU窗口。
Art+C:快速回到主界面
Alt+B:顯示斷點(diǎn)窗口
Alt+E:顯示模塊窗口
Art+L:顯示記錄窗口
Alt+M:顯示內(nèi)存窗口
Alt+O:顯示調(diào)試選項(xiàng)窗口
Alt+K:顯示呼叫堆棧
Ctrl+E:編輯機(jī)器碼
Ctrl+G:輸入跟隨地址
Ctrl+N:查找名稱標(biāo)志,選擇你要下斷的內(nèi)容
Ctrl+S:打開(kāi)查找命令次序窗口
Ctrl+P:顯示補(bǔ)丁窗口
Ctrl+F9:返回到跟蹤
Ctrl+F8:自動(dòng)步進(jìn)掃描,按F12可停止
Ctrl+F7:同上,功能略有不同
Ctrl+F6:回到OL主窗口
到了這里,關(guān)于《算法還原 - CTF》逆向exe程序 + ida Pro 反匯編分析偽C代碼 + python算法復(fù)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!