算法分析:2019_UNCTF一道逆向題目

一.下載程序得到查殼得到一個64位的elf文件

二.直接拖進IDA進行靜態(tài)分析
一拖進IDA便看到了main函數(shù)中一些關鍵字符“You are Right”、“flag{.....}”

int __cdecl main(int argc, const char **argv, const char **envp) { ? char s[240]; // [rsp+0h] [rbp-1E0h] BYREF ? char v5[240]; // [rsp+F0h] [rbp-F0h] BYREF ? memset(s, 0, 0x1EuLL); ? printf("Please Input Key: "); ? __isoc99_scanf("%s", v5); ? encode(v5, s); ? if ( strlen(v5) == key ) ? { ? ? if ( !strcmp(s, enflag) ) ? ? ? puts("You are Right"); ? ? else ? ? ? puts("flag{This_1s_f4cker_flag}"); ? } ? return 0; }
但是仔細查看邏輯便可得到整個程序的具體邏輯:
①讓用戶輸入一個字符串,放入v5字符數(shù)組中
②將v5的首地址與s的首地址作為encode函數(shù)的兩個參數(shù)
③對比v5的長度是否等于key,如果等于則繼續(xù)判斷s指向的字符串是否等于enflag變量,如果s指向的字符串是等于enflag變量則提示“You are Right”,否則提示“flag{.....}”
到這里其實我們可以推測出這個“flag{.....}”并不是正確的flag值,而只是一個作者設計的誘餌
三.對癥下藥
通過第二步驟的分析,現(xiàn)在我們知道等待我們解決的就只有encode函數(shù)和key、enflag變量的值
①首先跟入encode函數(shù),查看邏輯

int __fastcall encode(const char *a1, __int64 a2)
{
?char v3[104]; // [rsp+10h] [rbp-70h]
?int v4; // [rsp+78h] [rbp-8h]
?int i; // [rsp+7Ch] [rbp-4h]
?i = 0;
?v4 = 0;
?if ( strlen(a1) != key )
? ?return puts("Your Length is Wrong");
?for ( i = 0; i < key; i += 3 )
?{
? ?v3[i + 64] = key ^ (a1[i] + 6);
? ?v3[i + 33] = (a1[i + 1] - 6) ^ key;
? ?v3[i + 2] = a1[i + 2] ^ 6 ^ key;
? ?*(_BYTE *)(a2 + i) = v3[i + 64];
? ?*(_BYTE *)(a2 + i + 1LL) = v3[i + 33];
? ?*(_BYTE *)(a2 + i + 2LL) = v3[i + 2];
?}
?return a2;
}
encode的形參a1、a2的對應實參是main函數(shù)中的v5(用戶輸入字符串首地址)、s(定義的空字符串首地址)
encode代碼中的if ( strlen(a1) != key )可以直接得知a1(v5)指向的字符串長度要等于key,既然遇到了,那我們就先把key找出來,雙擊key

此時可以得到key=0x12h=18,也就是說我們輸入的字符串正確長度要是18,否則就是錯誤的,if分析完繼續(xù)往下走
for ( i = 0; i < key; i += 3 ) ? { ? ? v3[i + 64] = key ^ (a1[i] + 6); ? ? v3[i + 33] = (a1[i + 1] - 6) ^ key; ? ? v3[i + 2] = a1[i + 2] ^ 6 ^ key; ? ? *(_BYTE *)(a2 + i) = v3[i + 64]; ? ? *(_BYTE *)(a2 + i + 1LL) = v3[i + 33]; ? ? *(_BYTE *)(a2 + i + 2LL) = v3[i + 2]; ? }
閱讀這個for結構可以知道最后被更改的值只有a2(main函數(shù)中的s數(shù)組),所以我們這個時候我們就要回到調用者函數(shù)main函數(shù)中查看a2在encode中更改后又發(fā)生了什么?
if ( strlen(v5) == key ) ? { ? ? if ( !strcmp(s, enflag) ) ? ? ? puts("You are Right"); ? ? else ? ? ? puts("flag{This_1s_f4cker_flag}"); ? }
好,s(encode中的a2)數(shù)組在encode中操作完了以后用來和enflag對比,如果對比相同則提示"You are Right",那說明s數(shù)組就是最后的flag變換值,它必須要等于enflag才證明我們輸入的字符串是正確的,雙擊查看enflag是多少,如圖enflag = "izwhroz""w"v.K".Ni"(還沒有轉義),那此時我們就可以通過剛才encode的邏輯反推出v5(我們輸入的字符串)是多少了,回到encode

? }注意,通過剛才的分析,a2(main函數(shù)中的s)就是"izwhroz""w"v.K".Ni"(還沒有轉義),a1(main函數(shù)中的v5,我們輸入的字符串),那我們的最終目的就是要求出a1指向的字符串,所以我們就朝著能解出a1的方向走
閱讀代碼:要求a1則求v3,要求v3則只用用我們已知的a2來反解,所以可以得到如下解密腳本
#include#includeint main() { ? ? ? ? ? ? ? ? char input[18]; ? ? ? ? int v3[104] = { 0 }; ? ? ? ? char enflag[] = "izwhroz\"\"w\"v.K\".Ni"; ? ? ? ? int key = 18; ? ? for (int i = 0; i < key; i += 3) ? ? { ? ? ? ? ? ? ? ? v3[i + 2] = *(BYTE*)(enflag + i + 2); ? ? ? ? ? ? ? ? v3[i + 33] = *(BYTE*)(enflag + i + 1); ? ? ? ? ? ? ? ? v3[i + 64] = *(BYTE*)(enflag + i); ? ? ? ? ? ? ? ? input[i + 2] = v3[i + 2] ^ key ^ 6; ? ? ? ? ? ? ? ? input[i + 1] = (v3[i + 33] ^ key) + 6; ? ? ? ? ? ? ? ? input[i] = (v3[i + 64] ^ key) - 6; ? ? } ? ? ? ? for (size_t i = 0; i < 18; i++) ? ? ? ? { ? ? ? ? ? ? ? ? printf("%c", input[i]); ? ? ? ? } ? ? ? ? return 0; }
四.得到結果
