Reverse
SignIn
exeinfope 載入查殼。一個(gè)64位的ELF程序,無殼眯牧。
NOT Win EXE - .o - ELF executable [ 64bit obj. Shared obj file - CPU : AMD x86-64 - OS: unspecified ]
先來靜態(tài)分析一波蹋岩,載入IDA。找到 main 函數(shù)地址学少,F(xiàn)5大法...
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char N; // [rsp+0h] [rbp-4A0h]
char e; // [rsp+10h] [rbp-490h]
char m; // [rsp+20h] [rbp-480h]
char c; // [rsp+30h] [rbp-470h]
char v8; // [rsp+40h] [rbp-460h]
char v9; // [rsp+B0h] [rbp-3F0h]
unsigned __int64 v10; // [rsp+498h] [rbp-8h]
v10 = __readfsqword(0x28u);
puts("[sign in]");
printf("[input your flag]: ", a2);
__isoc99_scanf("%99s", &v8);
sub_96A(&v8, (__int64)&v9);
__gmpz_init_set_str((__int64)&c, (__int64)"ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35", 16LL);
__gmpz_init_set_str((__int64)&m, (__int64)&v9, 16LL);
__gmpz_init_set_str(
(__int64)&N,
(__int64)"103461035900816914121390101299049044413950405173712170434161686539878160984549",
10LL);
__gmpz_init_set_str((__int64)&e, (__int64)"65537", 10LL);
__gmpz_powm((__int64)&m, (__int64)&m, (__int64)&e, (__int64)&N);
if ( (unsigned int)__gmpz_cmp(&m, &c) )
puts("GG!");
else
puts("TTTTTTTTTTql!");
return 0LL;
}
可以看到程序先接收了輸入到 v8 數(shù)組剪个。然后經(jīng)過sub_96A函數(shù)的處理。通過 gdb 動(dòng)態(tài)調(diào)試可得該函數(shù)的作用即是將 HEX 轉(zhuǎn) ASCII版确。
繼續(xù)往下看扣囊,程序調(diào)用了 __gmpz_init_set_str 函數(shù),經(jīng)過 Google 之后得知這其實(shí)是一個(gè) GNU 高精度算法庫(GNU Multiple Precision Arithmetic Library)绒疗。
通過查閱官方文檔侵歇,我知道了 __gmpz_init_set_str 其實(shí)就是 mpz_init_set_str
int mpz_init_set_str (mpz_t rop, const char *str, int base) [Function]
Initialize rop and set its value like mpz_set_str
int mpz_set_str (mpz_t rop, const char *str, int base) [Function]
Set the value of rop from str, a null-terminated C string in base base. White space is allowed
in the string, and is simply ignored.
很顯然這個(gè)函數(shù)的作用就是將 str 字符數(shù)組以 base 指定的進(jìn)制解讀成數(shù)值并寫入 rop 所指向的內(nèi)存。該程序通過調(diào)用這個(gè)函數(shù)來實(shí)現(xiàn)數(shù)據(jù)的初始化賦值吓蘑。
之后調(diào)用的一個(gè)函數(shù) __gmpz_powm 在文檔中的定義是這樣的:
void mpz_powm (mpz_t rop, const mpz_t base, const mpz_t exp, const mpz_t mod) [Function]
Set rop to base^exp mod mod.
該函數(shù)將計(jì)算 base 的 exp 次方惕虑,并對(duì) mod 取模,最后將結(jié)果寫入 rop 中磨镶。
這種計(jì)算與RSA中的加密過程如出一轍溃蔫。
再往下就是關(guān)鍵比較函數(shù) __gmpz_cmp
int mpz_cmp (const mpz t op1, const mpz t op2) [Function]
Compare op1 and op2. Return a positive value if op1 > op2, zero if op1 = op2, or a negative
value if op1 < op2.
程序中比較之前 mpz_powm 運(yùn)算的結(jié)果與程序中硬編碼的值是否相等,如果相等則輸出 tql琳猫【瓢Γ看到這里應(yīng)該可以基本確定這是一道已知密文求解RSA明文的題目。
根據(jù)RSA的實(shí)現(xiàn)過程沸移,首先來計(jì)算密鑰。第一步是獲得大整數(shù) N 侄榴,根據(jù)程序可得
N = 103461035900816914121390101299049044413950405173712170434161686539878160984549
值得注意的是雹锣,這里的 N 是十進(jìn)制的。
接下來對(duì)它進(jìn)行大整數(shù)的因數(shù)分解癞蚕,這里借助 yafu 工具蕊爵。
>yafu-x64.exe
factor(103461035900816914121390101299049044413950405173712170434161686539878160984549)
fac: factoring 103461035900816914121390101299049044413950405173712170434161686539878160984549
fac: using pretesting plan: normal
fac: no tune info: using qs/gnfs crossover of 95 digits
starting SIQS on c78: 103461035900816914121390101299049044413950405173712170434161686539878160984549
==== sieving in progress (1 thread): 36224 relations needed ====
==== Press ctrl-c to abort and save state ====
SIQS elapsed time = 1.8809 seconds.
Total factoring time = 1.9647 seconds
***factors found***
P39 = 366669102002966856876605669837014229419
P39 = 282164587459512124844245113950593348271
ans = 1
至此我們得到了 p 和 q
p = 366669102002966856876605669837014229419
q = 282164587459512124844245113950593348271
再從程序中得知 e 的值為
e = 65537
接下來就可以求出私鑰 d,并通過私鑰 d桦山,求出明文 m攒射,再將其轉(zhuǎn)化成 ASCII 即可得到 flag
import gmpy2
p = 366669102002966856876605669837014229419
q = 282164587459512124844245113950593348271
N = 103461035900816914121390101299049044413950405173712170434161686539878160984549
c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
e = 65537
d = gmpy2.invert(e,(p-1)*(q-1))
m = gmpy2.powmod(c,d,p*q)
print hex(m)[2:].decode('hex')
Rev
一個(gè) 64 位的沒有加殼的程序
先看字符串
.rdata:0000000140005428 0000000E C You lost sth.
.rdata:0000000140005438 00000006 C pause
.rdata:0000000140005448 00000009 C You win!
.rdata:00000001400054C8 00000010 C string too long
看到 You win ! 和 You lost sth,執(zhí)行一下程序恒水,輸入 inputflagtest 猜測應(yīng)該返回 You lost sth
D:\CTF\SUCTF2019\Rev>cpp.exe
inputflagtest
D:\CTF\SUCTF2019\Rev>
實(shí)際上什么也沒有返回会放,說明程序在輸出 You lost sth 之前就停止了,動(dòng)態(tài)調(diào)試一下找到是在哪里停下的
調(diào)試之后發(fā)現(xiàn)
if ( v4 != 3 )
_exit(v4);
.text:00000001400016A0 cmp esi, 3
.text:00000001400016A3 jz short loc_1400016AE
.text:00000001400016A5 mov ecx, esi ; Code
.text:00000001400016A7 call cs:__imp__exit
我們首先得知道這個(gè) v4 是干什么用的钉凌,才有可能阻止程序停止咧最。于是往前看,找到一個(gè)循環(huán)
while ( 1 )
{ // while start
v129 = v83;
sub_140002120(v83, &v113);
sub_140002120(&v84, &v115);
v85 = v117;
v86 = BYTE2(v117);
v8 = *(&v112 + 1);
v129 = v87;
sub_140002120(v87, v83);
sub_140002120(&v88, &v84);
v89 = v85;
v90 = v86;
v91 = v8;
v92 = v8;
v95 = 0i64;
v96 = 15i64;
LOBYTE(v94) = 0;
v93 = 0;
sub_140001FC0(v83);
v7 |= 4u;
if ( v93 && BYTE8(v107) ) // 不進(jìn)
v9 = v91 != *(&v106 + 1) || v92 != v107;
else
v9 = v93 != BYTE8(v107); // v9 = 1
if ( v96 >= 0x10 ) // 不進(jìn)
{
v10 = v94;
if ( v96 + 1 >= 0x1000 )
{
v10 = *(v94 - 1);
if ( (v94 - v10 - 8) > 0x1F )
invalid_parameter_noinfo_noreturn();
}
j_j_free(v10);
}
v95 = 0i64;
v96 = 0xFi64;
LOBYTE(v94) = 0;
sub_140001FC0(v87);
if ( !v9 )
break;
if ( !BYTE8(v107) )
wassert(L"valid_", L"E:\\boost_1_69_0\\boost\\token_iterator.hpp", 0x3Bu);
v11 = &Src[32 * v4];
if ( v11 != Memory )
{
v12 = Memory;
if ( v109[1] >= 0x10 )
v12 = Memory[0];
sub_140002240(v11, v12, v109[0]);
}
++v4; // 在這里發(fā)現(xiàn)可以增加 v4
if ( !BYTE8(v107) )
wassert(L"valid_", L"E:\\boost_1_69_0\\boost\\token_iterator.hpp", 0x36u);
BYTE8(v107) = sub_140002B80(&Buf, &v106 + 1, v107, Memory);
} // while end
在這個(gè)循環(huán)之前程序?qū)?v4 初始化為 0,也就是說我們需要通過 ++v4 這條語句讓 v4 = 3 才能讓程序不退出矢沿。很自然的想到去找這個(gè)循環(huán)的出口滥搭。于是找到一個(gè)
if ( !v9 )
break;
再往前看與 v9 相關(guān)的代碼
v93 = 0;
...
if ( v93 && BYTE8(v107) )
v9 = v91 != *(&v106 + 1) || v92 != v107;
else
v9 = v93 != BYTE8(v107);
v93 已經(jīng)確定是 0 了。這里涉及到一個(gè) v107 的值捣鲸,它的值直接決定了 v9 會(huì)怎么變化瑟匆,于是往前找到
BYTE8(v107) = sub_140002B80(&Buf, &v106 + 1, v5, Memory);
發(fā)現(xiàn) v107 的值是由函數(shù) sub_140002B80 決定的。于是現(xiàn)在的問題就變成了研究這個(gè)函數(shù)的作用
這個(gè)函數(shù)的邏輯跳轉(zhuǎn)比較復(fù)雜栽惶,但是經(jīng)過動(dòng)態(tài)調(diào)試之后愁溜,可以總結(jié)出來的是
strEnd = a3;
strI = a2;
v35 = *strI + 1; // 取下一個(gè)字符
*strI = v35;
if ( v35 == strEnd )
return 1;
該函數(shù)的第二個(gè)參數(shù)是我們輸入的字符串指針,它會(huì)移動(dòng)該指針媒役,去取字符串中的每一位字符祝谚,取到空白符或者標(biāo)點(diǎn)符就會(huì)返回 1 并且指針的位置也會(huì)保存下來,換言之它起到了一個(gè)分割字符串的作用酣衷,只不過分割的字符可以是任意的標(biāo)點(diǎn)符或空白符交惯。主要借助的是以下兩個(gè)函數(shù)來實(shí)現(xiàn)
isspace()
ispunct()
知道了這個(gè)函數(shù)的作用我們就能弄清楚剛剛的循環(huán)的作用了,很顯然是在分割字符串穿仪,而 v3 = 3 也就意味著有三個(gè)部分
我們把輸入重新改成 input_flag_test席爽,再動(dòng)態(tài)調(diào)試一遍,發(fā)現(xiàn)程序在此處停止了
if ( v122 != 10 ) // 第一部分的長度要等于10才行
{
v14 = sub_140002090(Src);
_exit(v14);
}
v122 變量存儲(chǔ)的是第一部分字符串的長度啊片,于是我們更改輸入為 input2nput_flag_test 繼續(xù)調(diào)試
發(fā)現(xiàn)了以下循環(huán)
do
{
v20 = &Dst;
if ( v18 >= 0x10 )
v20 = v19;
if ( (*(v20 + v17) ^ 0xAB) != *(&v129 + v17) )
{
Sleep(0xD4A51000);
_exit(0);
}
v21 = &Dst;
if ( v18 >= 0x10 )
v21 = v19;
sub_140001020("%c", *(v21 + v17));
++v15;
++v17;
}
while ( v15 < v16 );
此時(shí) v16 的值是 10 v15與v17 保存了當(dāng)前的字符下標(biāo)只锻,我們發(fā)現(xiàn) Dst 的每一位必須要與 0xAB 異或,且得到的值要與 v129 表中的值要相等紫谷。往前看到
sub_140002690(&Dst, Src, &v76); // 在一個(gè)字符串中搜索另一個(gè)字符串齐饮,并去掉另一個(gè)字符串
LODWORD(v129) = 0xDFC8DED8; // 異或之后 是 suctf
WORD2(v129) = 0xCD;
分析一下 sub_140002690,首先從看它傳入的參數(shù) v76 被固定成了 0x31笤昨,即 '1'
動(dòng)態(tài)調(diào)試后祖驱,發(fā)現(xiàn)它的作用很簡單 sub_140002690(d,s,a) ,在 s 中去除 a 字符串并拷貝給 d
寫個(gè)腳本跑一下
v129 = "CD DF C8 DE D8".split(" ")
r = ""
for i in v129 :
r += chr(int(i,16)^0xAB)
print(r)
# suctf
由于只有 5 個(gè)字符瞒窒,但前面得到的信息是第一部分要有 10 個(gè)字符捺僻,這說明有 5 個(gè)字符是 '1',在 sub_140002690 中被去除了崇裁。于是第一部分就可以確定下來是 11111suctf
輸入 11111suctf_flag_test 匕坯,繼續(xù)調(diào)試遇到了
if ( Size != 4 ) // 第二段的長度得 = 4 否則就 you lost
goto LABEL_149;
這里的 Size 剛好保存了第二部分的數(shù)據(jù)長度,我們這里剛剛好是 4 個(gè)
if ( v25 != v24 ) // 判斷字符是否取到尾了
{
while ( (*v25 - 97) <= 6u || (*v25 - 65) <= 6u )// [a-gA-G]
{
if ( ++v25 == v24 )
goto LABEL_55;
}
goto LABEL_149; // You lost
}
...
LABEL_149:
v71 = sub_140002810(std::cout, "You lost sth.");
這里的循環(huán)主要限定了第二部分的字符范圍為 [a-gA-G]
于是我們轉(zhuǎn)換一下輸入為 11111suctf_abcd_test 繼續(xù)調(diào)試
do
{
v37 = *v33; // 取一個(gè)字符給 v37
v38 = sub_1400023D0(&v78);
*v33 = std::ctype<char>::toupper(v38, v37);// 替換 v38~v37 范圍內(nèi)的小寫字母為大寫字母
v33 = (v33 + 1);
}
while ( (v33 - v35) != v36 ); // 是否處理完字符
這里將剛才輸入的字符串第二部分的字母從小寫轉(zhuǎn)到大寫
if ( Size == v100 && !memcmp(v41, v40, Size) ) // 原來就得是大寫
這里 memcmp 將剛才轉(zhuǎn)化成大寫之后的字符串與原先轉(zhuǎn)化之前的字符串進(jìn)行了比較拔稳,換句話說葛峻,我原先輸入的第二部分字符串就必須是大寫否則這里就無法進(jìn)入分支
于是改變輸入 11111suctf_ABCD_test 繼續(xù)調(diào)試
v44 = 0i64;
do
{
v45 = Buf2;
if ( v30 >= 0x10 )
v45 = v29;
v46 = Buf2;
if ( v30 >= 0x10 )
v46 = v29;
if ( *(v45 + v44) + 2 != *(v46 + v44 + 1) )// 后一個(gè)字符要和前一個(gè)字符相差2
v22 = 1;
++v43;
++v44;
}
while ( v43 < (v42 - 1) );
這一段的主要作用是告訴我們第二段的字符串內(nèi)容,每兩個(gè)字符之間后一個(gè)字符要和前一個(gè)字符相差 2
結(jié)合 [a-gA-G] 的范圍得出結(jié)果 11111suctf_ACEG_test 繼續(xù)調(diào)試
while ( 1 )
{
v53 = *v52; // 取了一個(gè)字符 v52 = "test" v53=0x74
v54 = Buf2[0]; // 字符串長度 Buf[0] = 4 v54 = 4
if ( !(v54 & *(*(sub_1400023D0(&Buf2[1]) + 24) + 2 * v53)) )// *(sub_1400023D0(&Buf2[1]) + 24) = 000000000043EA00
// *(000000000043EA00) = 0x20
// 判斷是否是純數(shù)字
break;
v52 = (v52 + 1);
if ( v52 == v73 )
goto LABEL_104;
}
這里用了一種很奇妙的方法來判斷第三部分的字符是否是純數(shù)字的
我們?nèi)?nèi)存中 dump 出 0x0000000000500210 這個(gè)位置往下某一塊區(qū)域的表
debug013:0000000000500210 db 84h
debug013:0000000000500211 db 0
debug013:0000000000500212 db 84h
debug013:0000000000500213 db 0
debug013:0000000000500214 db 84h
debug013:0000000000500215 db 0
debug013:0000000000500216 db 84h
debug013:0000000000500217 db 0
debug013:0000000000500218 db 84h
debug013:0000000000500219 db 0
debug013:000000000050021A db 84h
debug013:000000000050021B db 0
debug013:000000000050021C db 84h
debug013:000000000050021D db 0
debug013:000000000050021E db 84h
debug013:000000000050021F db 0
debug013:0000000000500220 db 84h
debug013:0000000000500221 db 0
debug013:0000000000500222 db 84h
debug013:0000000000500223 db 0
正好 10 個(gè) 0x84 代表著 0-9 巴比,經(jīng)過運(yùn)算只有落在這部分區(qū)域里才有可能讓 v54(=4) 與 0x84 與運(yùn)算泞歉,才有可能不觸發(fā) break逼侦,所以這里的作用就是要求第三部分的所有字符都是數(shù)字字符
最后關(guān)鍵的一部分
v62 = v61 + v127 - v59; // 取第三部分長度
if ( v59 > (v61 + v127) )
v62 = 0i64;
if ( v62 )
{
do
{
v3 = *v60 + 2 * (5 * v3 - 24); // 把字符串型的數(shù)字轉(zhuǎn)為數(shù)值型的數(shù)字 例如 '3'=>3
v60 = (v60 + 1); // 取下一個(gè)字符
}
while ( v60 - v59 != v62 );
}
if ( !(v3 & 1)
&& ((1234 * v3 + 5678) / 4396 ^ 0xABCDDCBA) == 0xABCDB8B9
&& ((2334 * v3 + 9875) / 7777 ^ 0x12336790) == 0x1233FC70 )
{
v63 = std::basic_ostream<char,std::char_traits<char>>::operator<<(std::cout, v3, v60);
v64 = sub_140002810(v63, "}");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v64, sub_1400029E0);
}
v65 = sub_140002810(std::cout, "You win!");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v65, sub_1400029E0);
直接分析可能很難理解,但是通過動(dòng)態(tài)調(diào)試觀察可以發(fā)現(xiàn)是先將數(shù)字字符轉(zhuǎn)化為數(shù)值型數(shù)據(jù)腰耙,然后滿足一個(gè)表達(dá)式才能輸出 '}'榛丢,這里我們用 z3 來計(jì)算 v3 的確定值
from z3 import *
x = BitVec('x',32)
s = Solver()
s.add(x&1==0)
s.add((1234 * x + 5678) / 4396 ^ 0xABCDDCBA == 0xABCDB8B9)
s.add((2334 * x + 9875) / 7777 ^ 0x12336790 == 0x1233FC70)
if s.check() == sat :
print(s.model())
# [x = 31415926]
至此,我們就得到了最終的答案 11111suctf_ACEG_31415926
hardCPP
這題用了OLLVM混淆(控制流平坦化)挺庞,我在分析前選擇了基于angr框架的符號(hào)執(zhí)行來實(shí)現(xiàn)去除控制流平坦化晰赞。
$ python deflat.py ~/Desktop/hardCpp 0x4007E0
*******************relevant blocks************************
prologue: 0x4007e0
main_dispatcher: 0x400876
pre_dispatcher: 0x4012e3
retn: 0x40114a
relevant_blocks: ['0x400caa', '0x400dd3', '0x400d71', '0x400ba6', '0x400d15', '0x4010ea', '0x40107d', '0x400c30', '0x4012c4', '0x400d8d', '0x401172', '0x400bea', '0x400f40', '0x400ccf', '0x401155', '0x401037', '0x400ff1', '0x4010a4', '0x400bb0', '0x40108c', '0x400d62', '0x400f5e', '0x401188', '0x4012b5']
*******************symbolic execution*********************
-------------------dse 0x400caa---------------------
-------------------dse 0x400dd3---------------------
CRITICAL | 2019-10-08 06:28:05,385 | angr.sim_state | The name state.se is deprecated; please use state.solver.
-------------------dse 0x400d71---------------------
-------------------dse 0x400ba6---------------------
-------------------dse 0x400d15---------------------
-------------------dse 0x4010ea---------------------
-------------------dse 0x40107d---------------------
-------------------dse 0x400c30---------------------
-------------------dse 0x4012c4---------------------
-------------------dse 0x400d8d---------------------
-------------------dse 0x401172---------------------
-------------------dse 0x400bea---------------------
-------------------dse 0x400f40---------------------
-------------------dse 0x400ccf---------------------
-------------------dse 0x401155---------------------
-------------------dse 0x401037---------------------
-------------------dse 0x400ff1---------------------
-------------------dse 0x4010a4---------------------
-------------------dse 0x400bb0---------------------
-------------------dse 0x40108c---------------------
-------------------dse 0x400d62---------------------
-------------------dse 0x400f5e---------------------
-------------------dse 0x401188---------------------
-------------------dse 0x4012b5---------------------
-------------------dse 0x4007e0---------------------
************************flow******************************
0x400caa: ['0x400ccf']
0x4010ea: ['0x40114a', '0x4012c4']
0x400d71: ['0x400d8d', '0x4010a4']
0x4010a4: ['0x4010ea', '0x4012c4']
0x400ba6: ['0x400dd3']
0x400bb0: ['0x400bea']
0x40108c: ['0x400d71']
0x4007e0: ['0x400bb0']
0x401155: ['0x400c30']
0x400d62: ['0x400d71']
0x400d15: ['0x400d62', '0x401172']
0x401188: ['0x400dd3']
0x400ff1: ['0x401037', '0x4012b5']
0x40107d: ['0x40108c']
0x400c30: ['0x400caa', '0x401155']
0x4012c4: ['0x4010ea']
0x401172: ['0x400d15']
0x400dd3: ['0x400f40', '0x400f40']
0x400bea: ['0x400c30', '0x401155']
0x400d8d: ['0x400dd3', '0x401188']
0x400f40: ['0x400f5e', '0x400ff1']
0x400ccf: ['0x400d15', '0x401172']
0x4012b5: ['0x401037']
0x401037: ['0x40107d', '0x4012b5']
0x40114a: []
************************patch*****************************
Successful! The recovered file: /home/puret/Desktop/hardCpp_recovered
相關(guān)資料可以看:
bird 大佬最早發(fā)布的腳本 https://github.com/SnowGirls/deflat
后來有大佬改成 python3版本的 https://github.com/cq674350529/deflat
我直接拿來用的時(shí)候發(fā)現(xiàn)有一些小bug,fork了之后修正了小bug 對(duì)這道題的處理上不會(huì)再出現(xiàn)問題了 https://github.com/Pure-T/deflat
不過這題不用去除控制流平坦化也是可以做的选侨,問題不大掖鱼。
這是我去控制流平坦化之后的結(jié)果
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // al
char v4; // al
char v5; // al
char v6; // al
char v8; // al
char v9; // al
char v10; // al
char v11; // al
char v12; // [rsp+A0h] [rbp-90h]
char v13; // [rsp+A8h] [rbp-88h]
char v14; // [rsp+B0h] [rbp-80h]
char v15; // [rsp+B8h] [rbp-78h]
char v16; // [rsp+C0h] [rbp-70h]
char v17; // [rsp+C8h] [rbp-68h]
char v18; // [rsp+CFh] [rbp-61h]
int v19; // [rsp+D0h] [rbp-60h]
int v20; // [rsp+D4h] [rbp-5Ch]
int v21; // [rsp+D8h] [rbp-58h]
int v22; // [rsp+DCh] [rbp-54h]
char s[24]; // [rsp+E0h] [rbp-50h]
char v24; // [rsp+F8h] [rbp-38h]
char v25; // [rsp+100h] [rbp-30h]
char v26; // [rsp+108h] [rbp-28h]
char v27; // [rsp+110h] [rbp-20h]
int v28; // [rsp+114h] [rbp-1Ch]
const char **v29; // [rsp+118h] [rbp-18h]
int v30; // [rsp+120h] [rbp-10h]
int v31; // [rsp+124h] [rbp-Ch]
int v32; // [rsp+128h] [rbp-8h]
bool v33; // [rsp+12Eh] [rbp-2h]
bool v34; // [rsp+12Fh] [rbp-1h]
v31 = 0;
v30 = argc;
v29 = argv;
v28 = time(0LL);
puts("func(?)=\"01abfc750a0c942167651c40d088531d\"?");// #
s[0] = getchar();
fgets(&s[1], 21, stdin);
v22 = time(0LL);
v21 = v22 - v28;
v32 = v22 - v28;
if ( y >= 10 && (((_BYTE)x - 1) * (_BYTE)x & 1) != 0 )
goto LABEL_14;
while ( 1 )
{
v20 = strlen(s);
v33 = v20 != 21;
if ( y < 10 || (((_BYTE)x - 1) * (_BYTE)x & 1) == 0 )
break;
LABEL_14:
v20 = strlen(s);
}
if ( y >= 10 && (((_BYTE)x - 1) * (_BYTE)x & 1) != 0 )
goto LABEL_15;
while ( 1 )
{
v19 = 1;
if ( y < 10 || (((_BYTE)x - 1) * (_BYTE)x & 1) == 0 )
break;
LABEL_15:
v19 = 1;
}
while ( v19 < 21 ) // v19 [1,20]
{
if ( y >= 10 && (((_BYTE)x - 1) * (_BYTE)x & 1) != 0 )
{
v18 = v21 ^ s[v19];
v17 = main::$_0::operator() const((__int64)&v26, v18);
v16 = main::$_1::operator() const((__int64)&v24, s[v21 - 1 + v19]);
v8 = main::$_1::operator() const(char)::{lambda(int)#1}::operator() const(&v16, 7);
v18 = main::$_0::operator() const(char)::{lambda(char)#1}::operator() const((__int64)&v17, v8);
v15 = main::$_2::operator() const(&v27, (unsigned int)v18);
v14 = main::$_2::operator() const(&v27, (unsigned int)s[v21 - 1 + v19]);
v9 = main::$_2::operator() const(char)::{lambda(char)#1}::operator() const(&v14, 18LL);
v13 = main::$_3::operator() const((__int64)&v25, v9);
v10 = main::$_3::operator() const(char)::{lambda(char)#1}::operator() const(&v13, 3LL);
v12 = main::$_0::operator() const((__int64)&v26, v10);
v11 = main::$_0::operator() const(char)::{lambda(char)#1}::operator() const((__int64)&v12, 2);
v18 = main::$_2::operator() const(char)::{lambda(char)#1}::operator() const(&v15, (unsigned int)v11);
}
do
{
v18 = v21 ^ s[v19];
v17 = main::$_0::operator() const((__int64)&v26, v18);
v16 = main::$_1::operator() const((__int64)&v24, s[v21 - 1 + v19]);
v3 = main::$_1::operator() const(char)::{lambda(int)#1}::operator() const(&v16, 7);
v18 = main::$_0::operator() const(char)::{lambda(char)#1}::operator() const((__int64)&v17, v3);
v15 = main::$_2::operator() const(&v27, (unsigned int)v18);
v14 = main::$_2::operator() const(&v27, (unsigned int)s[v21 - 1 + v19]);
v4 = main::$_2::operator() const(char)::{lambda(char)#1}::operator() const(&v14, 18LL);
v13 = main::$_3::operator() const((__int64)&v25, v4);
v5 = main::$_3::operator() const(char)::{lambda(char)#1}::operator() const(&v13, 3LL);
v12 = main::$_0::operator() const((__int64)&v26, v5);
v6 = main::$_0::operator() const(char)::{lambda(char)#1}::operator() const((__int64)&v12, 2);
v18 = main::$_2::operator() const(char)::{lambda(char)#1}::operator() const(&v15, (unsigned int)v6);
v34 = enc[v19 - 1] != v18;
}
while ( v34 );
while ( y >= 10 && (((_BYTE)x - 1) * (_BYTE)x & 1) != 0 )
;
++v19;
}
if ( y >= 10 && (((_BYTE)x - 1) * (_BYTE)x & 1) != 0 )
goto LABEL_17;
while ( 1 )
{
puts("You win");
if ( y < 10 || (((_BYTE)x - 1) * (_BYTE)x & 1) == 0 )
break;
LABEL_17:
puts("You win");
}
return 0;
}
去除的并不是很完美,還是有很多地方?jīng)]有處理好援制,后來對(duì)比原程序有丟失一些信息戏挡,問題主要發(fā)生在去除控制流平坦化時(shí),識(shí)別返回塊的處理上晨仑,這里不展開褐墅。
先給了一個(gè) md5 值
puts("func(?)=\"01abfc750a0c942167651c40d088531d\"?");
反查 md5 知道是 '#' 字符
s[0] = getchar();
fgets(&s[1], 21, stdin);
這種寫法應(yīng)該是在暗示我第一個(gè)字符是 '#',之后還要輸入20個(gè)字符洪己,一共21個(gè)字符
在往后他記錄了一個(gè)時(shí)間差
v28 = time(0LL);
puts("func(?)=\"01abfc750a0c942167651c40d088531d\"?");// #
s[0] = getchar();
fgets(&s[1], 21, stdin);
v22 = time(0LL);
v21 = v22 - v28;
v32 = v22 - v28;
將開始輸入前的時(shí)間和開始輸入之后的時(shí)間差記錄成了一個(gè)變量妥凳,后來我注意到在原程序中判斷了這個(gè)時(shí)間差,若大于0就退出程序答捕,在我這里被去除控制流平坦化的腳本給刪去了逝钥。當(dāng)時(shí)我選擇先不管這個(gè)變量,往后看看拱镐,也許不影響解題艘款。
再往后看,有一些多余的流程沃琅,忽略就好了哗咆。
v20 = strlen(s);
v33 = v20 != 21;
這里獲取了輸入字符串的長度,并記錄下長度是否等于 21
v19 = 1;
初始化 v19 = 1阵难,緊接著一個(gè)大循環(huán)
while ( v19 < 21 ) // v19 [1,20]
{
if ( y >= 10 && (((_BYTE)x - 1) * (_BYTE)x & 1) != 0 )
{
v18 = v21 ^ s[v19];
v17 = main::$_0::operator() const((__int64)&v26, v18);
v16 = main::$_1::operator() const((__int64)&v24, s[v21 - 1 + v19]);
v8 = main::$_1::operator() const(char)::{lambda(int)#1}::operator() const(&v16, 7);
v18 = main::$_0::operator() const(char)::{lambda(char)#1}::operator() const((__int64)&v17, v8);
v15 = main::$_2::operator() const(&v27, (unsigned int)v18);
v14 = main::$_2::operator() const(&v27, (unsigned int)s[v21 - 1 + v19]);
v9 = main::$_2::operator() const(char)::{lambda(char)#1}::operator() const(&v14, 18LL);
v13 = main::$_3::operator() const((__int64)&v25, v9);
v10 = main::$_3::operator() const(char)::{lambda(char)#1}::operator() const(&v13, 3LL);
v12 = main::$_0::operator() const((__int64)&v26, v10);
v11 = main::$_0::operator() const(char)::{lambda(char)#1}::operator() const((__int64)&v12, 2);
v18 = main::$_2::operator() const(char)::{lambda(char)#1}::operator() const(&v15, (unsigned int)v11);
}
do
{
v18 = v21 ^ s[v19];
v17 = main::$_0::operator() const((__int64)&v26, v18);
v16 = main::$_1::operator() const((__int64)&v24, s[v21 - 1 + v19]);
v3 = main::$_1::operator() const(char)::{lambda(int)#1}::operator() const(&v16, 7);
v18 = main::$_0::operator() const(char)::{lambda(char)#1}::operator() const((__int64)&v17, v3);
v15 = main::$_2::operator() const(&v27, (unsigned int)v18);
v14 = main::$_2::operator() const(&v27, (unsigned int)s[v21 - 1 + v19]);
v4 = main::$_2::operator() const(char)::{lambda(char)#1}::operator() const(&v14, 18LL);
v13 = main::$_3::operator() const((__int64)&v25, v4);
v5 = main::$_3::operator() const(char)::{lambda(char)#1}::operator() const(&v13, 3LL);
v12 = main::$_0::operator() const((__int64)&v26, v5);
v6 = main::$_0::operator() const(char)::{lambda(char)#1}::operator() const((__int64)&v12, 2);
v18 = main::$_2::operator() const(char)::{lambda(char)#1}::operator() const(&v15, (unsigned int)v6);
v34 = enc[v19 - 1] != v18;
}
while ( v34 );
while ( y >= 10 && (((_BYTE)x - 1) * (_BYTE)x & 1) != 0 )
;
++v19;
}
注意到循環(huán)的條件是 v19 < 21,猜測這里 v19 代表的是當(dāng)前處理的字符串中字符的下標(biāo)芒填,也就是說它是從字符串的第二個(gè)字符開始處理的呜叫,這與之前猜測的以 '#' 開頭呼應(yīng)了
這個(gè)循環(huán)里還是有冗余的代碼,我們把它處理一下去除不會(huì)執(zhí)行的地方
while ( v19 < 21 ) // v19 [1,20]
{
do
{
v18 = v21 ^ s[v19];
v17 = main::$_0::operator() const((__int64)&v26, v18);
v16 = main::$_1::operator() const((__int64)&v24, s[v21 - 1 + v19]);
v3 = main::$_1::operator() const(char)::{lambda(int)#1}::operator() const(&v16, 7);
v18 = main::$_0::operator() const(char)::{lambda(char)#1}::operator() const((__int64)&v17, v3);
v15 = main::$_2::operator() const(&v27, (unsigned int)v18);
v14 = main::$_2::operator() const(&v27, (unsigned int)s[v21 - 1 + v19]);
v4 = main::$_2::operator() const(char)::{lambda(char)#1}::operator() const(&v14, 18LL);
v13 = main::$_3::operator() const((__int64)&v25, v4);
v5 = main::$_3::operator() const(char)::{lambda(char)#1}::operator() const(&v13, 3LL);
v12 = main::$_0::operator() const((__int64)&v26, v5);
v6 = main::$_0::operator() const(char)::{lambda(char)#1}::operator() const((__int64)&v12, 2);
v18 = main::$_2::operator() const(char)::{lambda(char)#1}::operator() const(&v15, (unsigned int)v6);
v34 = enc[v19 - 1] != v18;
} while ( v34 );
++v19;
}
邏輯更加清晰了殿衰,在 do While 循環(huán)的最后朱庆,判斷前面經(jīng)過處理之后的結(jié)果 v18 是否等于 enc 表中的值,如果不等于就繼續(xù)處理變化闷祥。換句話說娱颊,我們要控制我們的輸入使得經(jīng)過它規(guī)定的算法變化之后傲诵,等于 enc 表中的值,即可得到 flag
一個(gè)一個(gè)函數(shù)分析箱硕,我們隨便輸入 #input_flag_test_6789 調(diào)試看看
char __fastcall main::$_0::operator() const(__int64 a1, char a2)
{
return a2;
}
.text:00000000004012F0 push rbp
.text:00000000004012F1 mov rbp, rsp
.text:00000000004012F4 mov al, sil
.text:00000000004012F7 mov [rbp+var_10], rdi
.text:00000000004012FB mov [rbp+var_11], al
.text:00000000004012FE mov al, [rbp+var_11]
.text:0000000000401301 mov [rbp+var_8], al
.text:0000000000401304 mov al, [rbp+var_8]
.text:0000000000401307 pop rbp
.text:0000000000401308 retn
main::$_0 很顯然就做了一件事情返回參數(shù) a2拴竹,怕偽代碼出錯(cuò),我還特意看了一眼匯編
char __fastcall main::$_1::operator() const(__int64 a1, char a2)
{
return a2;
}
main::_0 一樣
__int64 __fastcall main::$_1::operator() const(char)::{lambda(int)#1}::operator() const(char *a1, int a2)
{
return (unsigned int)(char)(*a1 % a2);
}
該函數(shù)取 a1 數(shù)組的第一個(gè)字符值與 a2 進(jìn)行取模運(yùn)算
__int64 __fastcall main::$_0::operator() const(char)::{lambda(char)#1}::operator() const(__int64 a1, char a2)
{
signed int v2; // eax
signed int v3; // eax
__int64 v5; // [rsp+0h] [rbp-40h]
int v6; // [rsp+4h] [rbp-3Ch]
int v7; // [rsp+8h] [rbp-38h]
int v8; // [rsp+Ch] [rbp-34h]
int v9; // [rsp+10h] [rbp-30h]
int v10; // [rsp+14h] [rbp-2Ch]
__int64 v11; // [rsp+18h] [rbp-28h]
char v12; // [rsp+23h] [rbp-1Dh]
int v13; // [rsp+24h] [rbp-1Ch]
bool v14; // [rsp+2Ah] [rbp-16h]
bool v15; // [rsp+2Bh] [rbp-15h]
unsigned int v16; // [rsp+2Ch] [rbp-14h]
v14 = (((_BYTE)x_5 - 1) * (_BYTE)x_5 & 1) == 0;// v14 = 1
v15 = y_6 < 10; // v15 = 1
v13 = 1023500310;
v12 = a2;
v11 = a1;
do
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
v10 = v13;
v9 = v13 + 0x796B2E2C;
if ( v13 != 0x8694D1D4 )
break;
*(&v5 - 2) = v11;
*((_BYTE *)&v5 - 16) = v12;
v13 = 0xAB705FC8;
}
v8 = v10 + 0x548FA038;
if ( v10 != 0xAB705FC8 )
break;
v3 = 0x8694D1D4;
*(&v5 - 2) = v11;
*((_BYTE *)&v5 - 16) = v12;
v16 = *((char *)&v5 - 16) + *(char *)*(&v5 - 2);// v16 = v11 + v12
if ( y_6 < 10 || (((_BYTE)x_5 - 1) * (_BYTE)x_5 & 1) == 0 )
v3 = 0x56CAF6B1;
v13 = v3;
}
v7 = v10 - 0x3D016016;
if ( v10 != 0x3D016016 )
break;
v2 = 0x8694D1D4;
if ( v15 || v14 )
v2 = 0xAB705FC8;
v13 = v2;
}
v6 = v10 - 0x56CAF6B1;
}
while ( v10 != 0x56CAF6B1 );
return v16;
}
這個(gè)函數(shù)看上去代碼那么多剧罩。栓拜。其實(shí)都是混淆,真正執(zhí)行的就一句話
v16 = v11 + v12
這里的 v11 v12 就是傳入的參數(shù) a1 a2
下一個(gè)函數(shù)也是一樣...
char __fastcall main::$_2::operator() const(__int64 a1, char a2)
{
signed int v2; // eax
signed int v3; // eax
char v5; // [rsp+0h] [rbp-50h]
int v6; // [rsp+Ch] [rbp-44h]
int v7; // [rsp+10h] [rbp-40h]
int v8; // [rsp+14h] [rbp-3Ch]
int v9; // [rsp+18h] [rbp-38h]
int v10; // [rsp+1Ch] [rbp-34h]
__int64 v11; // [rsp+20h] [rbp-30h]
char v12; // [rsp+2Fh] [rbp-21h]
int v13; // [rsp+30h] [rbp-20h]
bool v14; // [rsp+35h] [rbp-1Bh]
bool v15; // [rsp+36h] [rbp-1Ah]
char v16; // [rsp+37h] [rbp-19h]
v14 = (((_BYTE)x_11 - 1) * (_BYTE)x_11 & 1) == 0;
v15 = y_12 < 10;
v13 = -1990873412;
v12 = a2;
v11 = a1;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
v10 = v13;
v9 = v13 + 1990873412;
if ( v13 != -1990873412 )
break;
v2 = -1373097315;
if ( v15 || v14 )
v2 = 1457028246;
v13 = v2;
}
v8 = v10 + 1373097315;
if ( v10 != -1373097315 )
break;
*((_QWORD *)&v5 - 2) = v11;
*(&v5 - 16) = v12;
v5 = *(&v5 - 16);
v13 = 1457028246;
}
v7 = v10 + 961146335;
if ( v10 == -961146335 )
break;
v6 = v10 - 1457028246;
if ( v10 == 1457028246 )
{
v3 = -1373097315;
*((_QWORD *)&v5 - 2) = v11;
*(&v5 - 16) = v12;
v5 = *(&v5 - 16);
v16 = v5;
if ( y_12 < 10 || (((_BYTE)x_11 - 1) * (_BYTE)x_11 & 1) == 0 )
v3 = -961146335;
v13 = v3;
}
}
return v16;
}
也是一樣虛胖...看上去很復(fù)雜惠昔,實(shí)際上執(zhí)行的就三句話
*((_QWORD *)&v5 - 2) = v11;
*(&v5 - 16) = v12;
v5 = *(&v5 - 16);
v16 = v5;
其實(shí)就是把第二個(gè)參數(shù) a2 返回
__int64 __fastcall main::$_2::operator() const(char)::{lambda(char)#1}::operator() const(_BYTE *a1, char a2)
{
return (unsigned int)(char)(a2 ^ *a1);
}
這個(gè)函數(shù)也比較友好...異或一下
char __fastcall main::$_3::operator() const(__int64 a1, char a2)
{
signed int v2; // eax
signed int v3; // eax
char v5; // [rsp+0h] [rbp-50h]
int v6; // [rsp+Ch] [rbp-44h]
int v7; // [rsp+10h] [rbp-40h]
int v8; // [rsp+14h] [rbp-3Ch]
int v9; // [rsp+18h] [rbp-38h]
int v10; // [rsp+1Ch] [rbp-34h]
__int64 v11; // [rsp+20h] [rbp-30h]
char v12; // [rsp+2Fh] [rbp-21h]
int v13; // [rsp+30h] [rbp-20h]
bool v14; // [rsp+35h] [rbp-1Bh]
bool v15; // [rsp+36h] [rbp-1Ah]
char v16; // [rsp+37h] [rbp-19h]
v14 = (((_BYTE)x_15 - 1) * (_BYTE)x_15 & 1) == 0;
v15 = y_16 < 10;
v13 = -538471561;
v12 = a2;
v11 = a1;
while ( 1 )
{
while ( 1 )
{
v10 = v13;
v9 = v13 + 2065325572;
if ( v13 != -2065325572 )
break;
*((_QWORD *)&v5 - 2) = v11;
*(&v5 - 16) = v12;
v5 = *(&v5 - 16);
v13 = 975002192;
}
v8 = v10 + 983538015;
if ( v10 == -983538015 )
break;
v7 = v10 + 538471561;
if ( v10 == -538471561 )
{
v2 = -2065325572;
if ( v15 || v14 )
v2 = 975002192;
v13 = v2;
}
else
{
v6 = v10 - 975002192;
if ( v10 == 975002192 )
{
v3 = -2065325572;
*((_QWORD *)&v5 - 2) = v11;
*(&v5 - 16) = v12;
v5 = *(&v5 - 16);
v16 = v5;
if ( y_16 < 10 || (((_BYTE)x_15 - 1) * (_BYTE)x_15 & 1) == 0 )
v3 = -983538015;
v13 = v3;
}
}
}
return v16;
}
返回第二個(gè)參數(shù) a2
__int64 __fastcall main::$_3::operator() const(char)::{lambda(char)#1}::operator() const(char *a1, char a2)
{
return (unsigned int)(a2 * *a1);
}
第二個(gè)參數(shù)跟第一個(gè)參數(shù)的第一個(gè)字符相乘
最后給出一個(gè) enc 數(shù)組
.data:0000000000602060 public enc
.data:0000000000602060 ; char enc[20]
.data:0000000000602060 enc db 0F3h ; DATA XREF: main+70A↑r
.data:0000000000602061 db 2Eh ; .
.data:0000000000602062 db 18h
.data:0000000000602063 db 36h ; 6
.data:0000000000602064 db 0E1h
.data:0000000000602065 db 4Ch ; L
.data:0000000000602066 db 22h ; "
.data:0000000000602067 db 0D1h
.data:0000000000602068 db 0F9h
.data:0000000000602069 db 8Ch
.data:000000000060206A db 40h ; @
.data:000000000060206B db 76h ; v
.data:000000000060206C db 0F4h
.data:000000000060206D db 0Eh
.data:000000000060206E db 0
.data:000000000060206F db 5
.data:0000000000602070 db 0A3h
.data:0000000000602071 db 90h
.data:0000000000602072 db 0Eh
.data:0000000000602073 db 0A5h
.data:0000000000602073 _data ends
剛好 20 個(gè)值
將上面的函數(shù)邏輯整理一下幕与,大概就是下面的代碼,可能語法會(huì)有點(diǎn)問題不影響理解
v18 = v21 ^ s[v19];
v17 = v18
v16 = s[v21 - 1 + v19]
v3 = v16[0] % 7
v18 = v17 + v3
v15 = v18
v14 = s[v21 - 1 + v19]
v4 = 18 ^ v14[0]
v13 = v4
v5 = v13[0] * 3
v12 = v5
v6 = v12[0] + 2
v18 = v15[0] ^ v6
// 最后整理成
v18 = ((v21 ^ s[v19]) + (s[v21 - 1 + v19] % 7)) ^ ((18 ^ s[v21 - 1 + v19]) * 3 + 2)
c = ((time ^ input[i]) + (input[time - 1 + i] % 7)) ^ ((18 ^ input[time - 1 + i]) * 3 + 2)
由于這里 time 預(yù)期為 0 所以
c = ((0 ^ input[i]) + (input[i-1] % 7)) ^ ((18 ^ input[i-1]) * 3 + 2)
已知 c 和 i 的情況下镇防,很顯然這個(gè)算法是可逆的
flag = [0 for i in range(21)]
flag[0] = 0x23
enc = 'F3 2E 18 36 E1 4C 22 D1 F9 8C 40 76 F4 0E 00 05 A3 90 0E A5'.split(' ')
c = [int(i,16) for i in enc]
for i in range(1,len(c)+1) :
flag[i] = (((c[i-1] ^ ((flag[i-1] ^ 18) * 3 + 2)) - (flag[i-1]%7)) ^ 0) & 0xff
print(''.join(map(chr,flag)))
最后得到答案 #flag{mY-CurR1ed_Fns}
babyunic
考察的是 unicorn
CPU 模擬器
func 文件里放的是機(jī)器指令啦鸣,babyunic 會(huì)借助 un.so.1 模擬執(zhí)行 func 里的指令
__int64 __fastcall main(int a1, char **a2, char **a3)
{
const void *s1; // ST10_8
const char *v4; // ST18_8
__int64 v5; // ST00_8
if ( a1 == 2 )
{
puts("SUCTF 2019");
printf("input your flag:", a2);
s1 = malloc(0x200uLL);
v4 = (const char *)malloc(0x200uLL);
__isoc99_scanf("%50s", v4);
sub_CBA(v4, (__int64)s1, *(const char **)(v5 + 8));
if ( !memcmp(s1, &unk_202020, 168uLL) )
puts("congratuation!");
else
puts("fail!");
}
else
{
puts("no input files");
}
return 0LL;
}
給了一張常量表 unk_202020,與 s1 比較相等就說明輸入的 flag 是正確的
將輸入的內(nèi)容傳入 sub_CBA 處理
uc_open(3LL, 0x40000004LL, &uc_engine);
借助 uc_open
函數(shù)我們可以確定 func 的指令架構(gòu)和位數(shù)
在 unicorn/include/unicorn/unicorn.h
位置處来氧,我們可以找到
UNICORN_EXPORT
uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **uc);
/*
Close a Unicorn engine instance.
NOTE: this must be called only when there is no longer any
usage of @uc. This API releases some of @uc's cached memory, thus
any use of the Unicorn API with @uc after it has been closed may
crash your application. After this, @uc is invalid, and is no
longer usable.
@uc: pointer to a handle returned by uc_open()
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
知道了三個(gè)參數(shù)的作用诫给,我們再去找 arch
和 mode
的值定義
// Architecture type
typedef enum uc_arch {
UC_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2)
UC_ARCH_ARM64, // ARM-64, also called AArch64
UC_ARCH_MIPS, // Mips architecture
UC_ARCH_X86, // X86 architecture (including x86 & x86-64)
UC_ARCH_PPC, // PowerPC architecture (currently unsupported)
UC_ARCH_SPARC, // Sparc architecture
UC_ARCH_M68K, // M68K architecture
UC_ARCH_MAX,
} uc_arch;
// Mode type
typedef enum uc_mode {
UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode)
UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode
// arm / arm64
UC_MODE_ARM = 0, // ARM mode
UC_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2)
UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series (currently unsupported)
UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM (currently unsupported)
// mips
UC_MODE_MICRO = 1 << 4, // MicroMips mode (currently unsupported)
UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (currently unsupported)
UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (currently unsupported)
UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA
UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA
// x86 / x64
UC_MODE_16 = 1 << 1, // 16-bit mode
UC_MODE_32 = 1 << 2, // 32-bit mode
UC_MODE_64 = 1 << 3, // 64-bit mode
// ppc
UC_MODE_PPC32 = 1 << 2, // 32-bit mode (currently unsupported)
UC_MODE_PPC64 = 1 << 3, // 64-bit mode (currently unsupported)
UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (currently unsupported)
// sparc
UC_MODE_SPARC32 = 1 << 2, // 32-bit mode
UC_MODE_SPARC64 = 1 << 3, // 64-bit mode
UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported)
// m68k
} uc_mode;
因此 uc_open 中的 3 對(duì)應(yīng)的是 UC_ARCH_MIPS
描焰,0x40000004
其實(shí)等于 0x40000000 + 0x4
對(duì)應(yīng) UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN
知道了架構(gòu)和位數(shù)我們就可以反匯編它了摊册,這里我借助一個(gè)神器 ghidra
直接反編譯
由于代碼量有點(diǎn)大就不全部貼出來了
void UndefinedFunction_00000000(byte *param_1,int *param_2)
{
int iStack16;
int iStack12;
iStack12 = 0;
while (param_1[iStack12] != 0) {
iStack12 = iStack12 + 1;
}
iStack16 = 0;
while (iStack16 < iStack12) {
param_1[iStack16] = (param_1[iStack16] << 3 | param_1[iStack16] >> 5) ^ (byte)iStack16;
iStack16 = iStack16 + 1;
}
*param_2 = ((((((((((((((((((((((((((((((((uint)*param_1 + (uint)param_1[1] + (uint)param_1[2]) -
(uint)param_1[3]) + (uint)param_1[4]) - (uint)param_1[5])
- (uint)param_1[6]) - (uint)param_1[7]) - (uint)param_1[8])+
(uint)param_1[9] + (uint)param_1[10]) - (uint)param_1[0xb]) +
(uint)param_1[0xc]) - (uint)param_1[0xd]) - (uint)param_1[0xe])+
(uint)param_1[0xf]) - (uint)param_1[0x10]) - (uint)param_1[0x11]) +
(uint)param_1[0x12] + (uint)param_1[0x13]) - (uint)param_1[0x14]) +
(uint)param_1[0x15] + (uint)param_1[0x16] + (uint)param_1[0x17] +
(uint)param_1[0x18]) - (uint)param_1[0x19]) + (uint)param_1[0x1a]) -
(uint)param_1[0x1b]) + (uint)param_1[0x1c] + (uint)param_1[0x1d]) -
(uint)param_1[0x1e]) - (uint)param_1[0x1f]) + (uint)param_1[0x20]) -
(uint)param_1[0x21]) + (uint)param_1[0x22] + (uint)param_1[0x23]) -
(uint)param_1[0x24]) - (uint)param_1[0x25]) + (uint)param_1[0x26]) -
(uint)param_1[0x27]) + (uint)param_1[0x28] + (uint)param_1[0x29];
param_2[1] = (((((((((((((((((((((((((((((((((((uint)*param_1 - (uint)param_1[1]) +
(uint)param_1[2]) - (uint)param_1[3]) -
(uint)param_1[4]) + (uint)param_1[5]) -
(uint)param_1[6]) - (uint)param_1[7]) -(uint)param_1[8])
- (uint)param_1[9]) + (uint)param_1[10]) -
(uint)param_1[0xb]) + (uint)param_1[0xc]) -(uint)param_1[0xd]
) - (uint)param_1[0xe]) + (uint)param_1[0xf]) -
(uint)param_1[0x10]) - (uint)param_1[0x11]) +(uint)param_1[0x12])
- (uint)param_1[0x13]) + (uint)param_1[0x14] + (uint)param_1[0x15])-
(uint)param_1[0x16]) - (uint)param_1[0x17]) - (uint)param_1[0x18]) +
(uint)param_1[0x19]) - (uint)param_1[0x1a]) + (uint)param_1[0x1b]) -
(uint)param_1[0x1c]) - (uint)param_1[0x1d]) + (uint)param_1[0x1e] +
(uint)param_1[0x1f] + (uint)param_1[0x20] + (uint)param_1[0x21] +
(uint)param_1[0x22] + (uint)param_1[0x23]) - (uint)param_1[0x24]) -
(uint)param_1[0x25]) - (uint)param_1[0x26]) - (uint)param_1[0x27]) -
(uint)param_1[0x28]) + (uint)param_1[0x29];
param_2[2] = ((((((((((((((((((((((((((((((uint)*param_1 - (uint)param_1[1]) + (uint)param_1[2] +
(uint)param_1[3]) - (uint)param_1[4]) + (uint)param_1[5])
- (uint)param_1[6]) - (uint)param_1[7]) + (uint)param_1[8])-
(uint)param_1[9]) - (uint)param_1[10]) - (uint)param_1[0xb]) -
(uint)param_1[0xc]) - (uint)param_1[0xd]) + (uint)param_1[0xe]) -
(uint)param_1[0xf]) - (uint)param_1[0x10]) + (uint)param_1[0x11] +
(uint)param_1[0x12] + (uint)param_1[0x13] + (uint)param_1[0x14] +
(uint)param_1[0x15]) - (uint)param_1[0x16]) + (uint)param_1[0x17] +
(uint)param_1[0x18] + (uint)param_1[0x19] + (uint)param_1[0x1a]) -
(uint)param_1[0x1b]) + (uint)param_1[0x1c]) - (uint)param_1[0x1d]) +
(uint)param_1[0x1e]) - (uint)param_1[0x1f]) + (uint)param_1[0x20] +
(uint)param_1[0x21]) - (uint)param_1[0x22]) - (uint)param_1[0x23]) +
(uint)param_1[0x24] + (uint)param_1[0x25] + (uint)param_1[0x26]) -
(uint)param_1[0x27]) + (uint)param_1[0x28]) - (uint)param_1[0x29];
param_2[3] = ...
......
這里第一個(gè)參數(shù) param_1
是我們輸入的字符串, param_2
最后得到的值要等于那個(gè)常量表
這里借助 z3 一把梭褒傅,不過要注意的是 enc 那個(gè)常量是以補(bǔ)碼形式表示的
from z3 import *
import ctypes
e = [0xFFFFFF94,0xFFFFFF38,0x00000126,0xFFFFFF28,0xFFFFFC10,0x00000294,0xFFFFFC9E,0x000006EA,0x000000DC,0x00000006,0xFFFFFF0C,0xFFFFFDF6,0xFFFFFA82,0xFFFFFCD0,0x00000182,0x000003DE,0x0000014E,0x000002B2,0xFFFFF8D8,0x00000174,0xFFFFFAA6,0xFFFFF9D4,0x000001C2,0xFFFFF97C,0x0000035A,0x00000146,0xFFFFFF3C,0xFFFFFA14,0x000001CE,0x000007DC,0xFFFFFD48,0x00000098,0x0000085E,0xFFFFFDB0,0xFFFFFFBC,0x0000036E,0xFFFFFF4E,0xFFFFF836,0x000005C0,0x000006AE,0x00000694,0x00000022]
en = map(lambda x: ctypes.c_int32(x).value,e)
enc = [IntVal(i) for i in en]
# enc = map(lambda x: ctypes.c_int32(x).value, enc)
c = [Int('c%d' % i) for i in range(42)]
flag = []
solver = Solver()
for v in c :
solver.add(v >= 0x0)
solver.add(v <= 0xff)
solver.add(enc[0]==(((((((((((((((((((((((((((((((c[0]+c[1]+c[2])-c[3])+c[4])-c[5])-c[6])-c[7])-c[8])+c[9]+c[10])-c[0xb])+c[0xc])-c[0xd])-c[0xe])+c[0xf])-c[0x10])-c[0x11])+c[0x12]+c[0x13])-c[0x14])+c[0x15]+c[0x16]+c[0x17]+c[0x18])-c[0x19])+c[0x1a])-c[0x1b])+c[0x1c]+c[0x1d])-c[0x1e])-c[0x1f])+c[0x20])-c[0x21])+c[0x22]+c[0x23])-c[0x24])-c[0x25])+c[0x26])-c[0x27])+c[0x28]+c[0x29])
solver.add(enc[1]==((((((((((((((((((((((((((((((((((c[0]-c[1])+c[2])-c[3])-c[4])+c[5])-c[6])-c[7])-c[8])-c[9])+c[10])-c[0xb])+c[0xc])-c[0xd])-c[0xe])+c[0xf])-c[0x10])-c[0x11])+c[0x12])-c[0x13])+c[0x14]+c[0x15])-c[0x16])-c[0x17])-c[0x18])+c[0x19])-c[0x1a])+c[0x1b])-c[0x1c])-c[0x1d])+c[0x1e]+c[0x1f]+c[0x20]+c[0x21]+c[0x22]+c[0x23])-c[0x24])-c[0x25])-c[0x26])-c[0x27])-c[0x28])+c[0x29])
solver.add(enc[2]==(((((((((((((((((((((((((((((c[0]-c[1])+c[2]+c[3])-c[4])+c[5])-c[6])-c[7])+c[8])-c[9])-c[10])-c[0xb])-c[0xc])-c[0xd])+c[0xe])-c[0xf])-c[0x10])+c[0x11]+c[0x12]+c[0x13]+c[0x14]+c[0x15])-c[0x16])+c[0x17]+c[0x18]+c[0x19]+c[0x1a])-c[0x1b])+c[0x1c])-c[0x1d])+c[0x1e])-c[0x1f])+c[0x20]+c[0x21])-c[0x22])-c[0x23])+c[0x24]+c[0x25]+c[0x26])-c[0x27])+c[0x28])-c[0x29])
solver.add(enc[3]==(((((((((((((((((((((((((((((((c[0]-c[1])-c[2])-c[3])-c[4])-c[5])+c[6]+c[7])-c[8])-c[9])-c[10])-c[0xb])+c[0xc])-c[0xd])+c[0xe])-c[0xf])+c[0x10])-c[0x11])+c[0x12]+c[0x13]+c[0x14])-c[0x15])+c[0x16]+c[0x17]+c[0x18])-c[0x19])-c[0x1a])+c[0x1b])-c[0x1c])+c[0x1d]+c[0x1e])-c[0x1f])-c[0x20])-c[0x21])+c[0x22])-c[0x23])+c[0x24]+c[0x25]+c[0x26])-c[0x27])+c[0x28]+c[0x29])
solver.add(enc[4]==(((((((((((((((((((((((((((((((((c[0]-c[1])-c[2])+c[3])-c[4])-c[5])+c[6]+c[7]+c[8]+c[9])-c[10])+c[0xb]+c[0xc])-c[0xd])+c[0xe])-c[0xf])+c[0x10]+c[0x11])-c[0x12])+c[0x13])-c[0x14])+c[0x15])-c[0x16])-c[0x17])-c[0x18])+c[0x19])-c[0x1a])-c[0x1b])-c[0x1c])+c[0x1d]+c[0x1e]+c[0x1f])-c[0x20])+c[0x21])-c[0x22])-c[0x23])+c[0x24])-c[0x25])+c[0x26])-c[0x27])-c[0x28])-c[0x29])
solver.add(enc[5]==((((((((((((((((((((((((((((c[0]+c[1]+c[2]+c[3]+c[4]+c[5]+c[6]+c[7]+c[8])-c[9])-c[10])-c[0xb])-c[0xc])-c[0xd])-c[0xe])+c[0xf])-c[0x10])+c[0x11])-c[0x12])+c[0x13]+c[0x14])-c[0x15])+c[0x16])-c[0x17])+c[0x18])-c[0x19])+c[0x1a]+c[0x1b])-c[0x1c])+c[0x1d])-c[0x1e])+c[0x1f]+c[0x20]+c[0x21])-c[0x22])-c[0x23])-c[0x24])+c[0x25])-c[0x26])-c[0x27])+c[0x28]+c[0x29])
solver.add(enc[6]==(((((((((((((((((((((((((((c[0]-c[1])+c[2]+c[3]+c[4])-c[5])+c[6]+c[7]+c[8]+c[9])-c[10])+c[0xb]+c[0xc])-c[0xd])+c[0xe]+c[0xf]+c[0x10]+c[0x11])-c[0x12])-c[0x13])-c[0x14])-c[0x15])-c[0x16])-c[0x17])+c[0x18]+c[0x19])-c[0x1a])+c[0x1b]+c[0x1c]+c[0x1d])-c[0x1e])-c[0x1f])-c[0x20])-c[0x21])-c[0x22])-c[0x23])+c[0x24]+c[0x25])-c[0x26])-c[0x27])+c[0x28])-c[0x29])
solver.add(enc[7]==((((((((((((((((((((((((((((((c[0]+c[1])-c[2])-c[3])-c[4])+c[5]+c[6])-c[7])+c[8]+c[9])-c[10])+c[0xb])-c[0xc])+c[0xd])-c[0xe])+c[0xf])-c[0x10])+c[0x11])-c[0x12])-c[0x13])+c[0x14])-c[0x15])+c[0x16])-c[0x17])-c[0x18])+c[0x19])-c[0x1a])+c[0x1b]+c[0x1c]+c[0x1d]+c[0x1e]+c[0x1f]+c[0x20])-c[0x21])+c[0x22])-c[0x23])+c[0x24]+c[0x25]+c[0x26]+c[0x27])-c[0x28])-c[0x29])
solver.add(enc[8]==(((((((((((((((((((((((((((((c[0]-c[1])-c[2])+c[3]+c[4])-c[5])+c[6]+c[7]+c[8]+c[9]+c[10])-c[0xb])-c[0xc])+c[0xd])-c[0xe])+c[0xf]+c[0x10]+c[0x11]+c[0x12])-c[0x13])+c[0x14]+c[0x15])-c[0x16])-c[0x17])+c[0x18]+c[0x19]+c[0x1a])-c[0x1b])+c[0x1c])-c[0x1d])-c[0x1e])-c[0x1f])-c[0x20])-c[0x21])+c[0x22])-c[0x23])-c[0x24])+c[0x25])-c[0x26])-c[0x27])+c[0x28])-c[0x29])
solver.add(enc[9]==(((((((((((((((((((((((((((((c[0]+c[1]+c[2])-c[3])+c[4]+c[5]+c[6])-c[7])-c[8])-c[9])-c[10])+c[0xb]+c[0xc]+c[0xd])-c[0xe])+c[0xf]+c[0x10])-c[0x11])-c[0x12])+c[0x13]+c[0x14])-c[0x15])-c[0x16])-c[0x17])+c[0x18])-c[0x19])-c[0x1a])-c[0x1b])+c[0x1c]+c[0x1d]+c[0x1e])-c[0x1f])+c[0x20]+c[0x21])-c[0x22])-c[0x23])-c[0x24])-c[0x25])+c[0x26])-c[0x27])+c[0x28]+c[0x29])
solver.add(enc[10]==((((((((((((((((((((((((((((((((c[0]-c[1])+c[2]+c[3])-c[4])-c[5])+c[6]+c[7])-c[8])-c[9])-c[10])-c[0xb])+c[0xc]+c[0xd]+c[0xe])-c[0xf])+c[0x10])-c[0x11])+c[0x12]+c[0x13]+c[0x14])-c[0x15])+c[0x16])-c[0x17])-c[0x18])-c[0x19])+c[0x1a])-c[0x1b])-c[0x1c])+c[0x1d])-c[0x1e])+c[0x1f]+c[0x20])-c[0x21])-c[0x22])+c[0x23])-c[0x24])-c[0x25])+c[0x26])-c[0x27])+c[0x28]+c[0x29])
solver.add(enc[0xb]==((((((((((((((((((((((((((((((c[0]-c[1])+c[2]+c[3]+c[4])-c[5])+c[6]+c[7])-c[8])+c[9]+c[10])-c[0xb])-c[0xc])-c[0xd])-c[0xe])+c[0xf])-c[0x10])-c[0x11])-c[0x12])+c[0x13]+c[0x14])-c[0x15])+c[0x16])-c[0x17])+c[0x18]+c[0x19]+c[0x1a]+c[0x1b])-c[0x1c])+c[0x1d]+c[0x1e])-c[0x1f])-c[0x20])-c[0x21])-c[0x22])-c[0x23])+c[0x24]+c[0x25])-c[0x26])-c[0x27])-c[0x28])-c[0x29])
solver.add(enc[0xc]==(((((((((((((((((((((((((((((((((((((c[0]-c[1])-c[2])-c[3])+c[4])-c[5])-c[6])+c[7]+c[8])-c[9])+c[10])-c[0xb])-c[0xc])-c[0xd])+c[0xe])-c[0xf])+c[0x10])-c[0x11])+c[0x12])-c[0x13])-c[0x14])-c[0x15])-c[0x16])+c[0x17])-c[0x18])+c[0x19])-c[0x1a])+c[0x1b])-c[0x1c])+c[0x1d])-c[0x1e])-c[0x1f])+c[0x20]+c[0x21]+c[0x22])-c[0x23])-c[0x24])-c[0x25])-c[0x26])+c[0x27])-c[0x28])-c[0x29])
solver.add(enc[0xd]==(((((((((((((((((((((((((((((((((c[0]-c[1])+c[2])-c[3])+c[4])-c[5])+c[6])-c[7])+c[8])-c[9])+c[10])-c[0xb])+c[0xc]+c[0xd]+c[0xe]+c[0xf])-c[0x10])-c[0x11])-c[0x12])+c[0x13]+c[0x14]+c[0x15])-c[0x16])-c[0x17])+c[0x18]+c[0x19])-c[0x1a])-c[0x1b])+c[0x1c]+c[0x1d])-c[0x1e])-c[0x1f])-c[0x20])+c[0x21])-c[0x22])-c[0x23])+c[0x24])-c[0x25])-c[0x26])-c[0x27])+c[0x28])-c[0x29])
solver.add(enc[0xe]==(((((((((((((((((((((((((c[0]+c[1]+c[2])-c[3])-c[4])-c[5])+c[6])-c[7])+c[8]+c[9]+c[10])-c[0xb])+c[0xc])-c[0xd])-c[0xe])+c[0xf]+c[0x10]+c[0x11])-c[0x12])-c[0x13])-c[0x14])-c[0x15])+c[0x16]+c[0x17]+c[0x18])-c[0x19])+c[0x1a]+c[0x1b]+c[0x1c])-c[0x1d])-c[0x1e])-c[0x1f])+c[0x20]+c[0x21]+c[0x22]+c[0x23]+c[0x24]+c[0x25]+c[0x26])-c[0x27])-c[0x28])-c[0x29])
solver.add(enc[0xf]==((((((((((((((((((((((((((c[0]-c[1])+c[2]+c[3]+c[4]+c[5])-c[6])+c[7])-c[8])-c[9])-c[10])+c[0xb]+c[0xc]+c[0xd])-c[0xe])-c[0xf])-c[0x10])+c[0x11])-c[0x12])-c[0x13])-c[0x14])-c[0x15])+c[0x16]+c[0x17]+c[0x18]+c[0x19]+c[0x1a]+c[0x1b])-c[0x1c])-c[0x1d])-c[0x1e])-c[0x1f])+c[0x20])-c[0x21])+c[0x22]+c[0x23]+c[0x24]+c[0x25])-c[0x26])+c[0x27]+c[0x28])-c[0x29])
solver.add(enc[0x10]==((((((((((((((((((((((((((((((c[0]-c[1])+c[2]+c[3])-c[4])-c[5])+c[6]+c[7]+c[8]+c[9]+c[10])-c[0xb])+c[0xc])-c[0xd])+c[0xe]+c[0xf]+c[0x10])-c[0x11])+c[0x12])-c[0x13])+c[0x14])-c[0x15])-c[0x16])-c[0x17])-c[0x18])-c[0x19])+c[0x1a]+c[0x1b]+c[0x1c]+c[0x1d])-c[0x1e])-c[0x1f])+c[0x20])-c[0x21])-c[0x22])+c[0x23])-c[0x24])+c[0x25])-c[0x26])+c[0x27])-c[0x28])+c[0x29])
solver.add(enc[0x11]==((((((((((((((((((((((((((((((((c[0]+c[1]+c[2]+c[3]+c[4])-c[5])+c[6]+c[7]+c[8])-c[9])-c[10])+c[0xb])-c[0xc])+c[0xd]+c[0xe]+c[0xf])-c[0x10])+c[0x11])-c[0x12])-c[0x13])+c[0x14])-c[0x15])+c[0x16])-c[0x17])-c[0x18])+c[0x19])-c[0x1a])+c[0x1b])-c[0x1c])+c[0x1d])-c[0x1e])-c[0x1f])+c[0x20])-c[0x21])-c[0x22])+c[0x23])-c[0x24])+c[0x25])-c[0x26])+c[0x27]+c[0x28])-c[0x29])
solver.add(enc[0x12]==((((((((((((((((((((((((((((((((((c[0]-c[1])-c[2])-c[3])+c[4]+c[5])-c[6])+c[7])-c[8])+c[9]+c[10])-c[0xb])-c[0xc])-c[0xd])+c[0xe])-c[0xf])-c[0x10])+c[0x11]+c[0x12]+c[0x13])-c[0x14])-c[0x15])-c[0x16])-c[0x17])-c[0x18])-c[0x19])-c[0x1a])-c[0x1b])+c[0x1c]+c[0x1d]+c[0x1e])-c[0x1f])-c[0x20])-c[0x21])+c[0x22])-c[0x23])-c[0x24])-c[0x25])-c[0x26])-c[0x27])-c[0x28])+c[0x29])
solver.add(enc[0x13]==((((((((((((((((((((((((((c[0]+c[1]+c[2]+c[3])-c[4])-c[5])+c[6])-c[7])-c[8])-c[9])-c[10])-c[0xb])-c[0xc])-c[0xd])+c[0xe]+c[0xf]+c[0x10])-c[0x11])+c[0x12]+c[0x13]+c[0x14]+c[0x15])-c[0x16])+c[0x17]+c[0x18])-c[0x19])+c[0x1a]+c[0x1b])-c[0x1c])+c[0x1d]+c[0x1e]+c[0x1f]+c[0x20]+c[0x21])-c[0x22])+c[0x23])-c[0x24])-c[0x25])-c[0x26])+c[0x27]+c[0x28])-c[0x29])
solver.add(enc[0x14]==((((((((((((((((((((((((((((((((((((c[0]+c[1])-c[2])-c[3])-c[4])+c[5])-c[6])+c[7])-c[8])-c[9])+c[10]+c[0xb])-c[0xc])-c[0xd])+c[0xe])-c[0xf])-c[0x10])+c[0x11])-c[0x12])-c[0x13])+c[0x14]+c[0x15])-c[0x16])+c[0x17]+c[0x18])-c[0x19])-c[0x1a])-c[0x1b])-c[0x1c])-c[0x1d])-c[0x1e])-c[0x1f])-c[0x20])+c[0x21])-c[0x22])+c[0x23]+c[0x24])-c[0x25])+c[0x26])-c[0x27])+c[0x28])-c[0x29])
solver.add(enc[0x15]==(((((((((((((((((((((((((((((((((((c[0]-c[1])-c[2])-c[3])+c[4]+c[5]+c[6]+c[7])-c[8])-c[9])-c[10])-c[0xb])-c[0xc])-c[0xd])-c[0xe])-c[0xf])-c[0x10])+c[0x11])-c[0x12])-c[0x13])+c[0x14])-c[0x15])+c[0x16]+c[0x17]+c[0x18])-c[0x19])-c[0x1a])+c[0x1b])-c[0x1c])-c[0x1d])-c[0x1e])-c[0x1f])-c[0x20])-c[0x21])-c[0x22])-c[0x23])-c[0x24])+c[0x25])-c[0x26])-c[0x27])-c[0x28])+c[0x29])
solver.add(enc[0x16]==(((((((((((((((((((((((((((c[0]+c[1]+c[2]+c[3]+c[4]+c[5]+c[6]+c[7])-c[8])+c[9])-c[10])+c[0xb])-c[0xc])+c[0xd]+c[0xe]+c[0xf])-c[0x10])+c[0x11]+c[0x12])-c[0x13])-c[0x14])+c[0x15]+c[0x16])-c[0x17])+c[0x18])-c[0x19])-c[0x1a])+c[0x1b])-c[0x1c])+c[0x1d]+c[0x1e]+c[0x1f])-c[0x20])+c[0x21])-c[0x22])-c[0x23])-c[0x24])-c[0x25])+c[0x26])-c[0x27])+c[0x28]+c[0x29])
solver.add(enc[0x17]==(((((((((((((((((((((((((((((((((((c[0]-c[1])+c[2]+c[3])-c[4])-c[5])-c[6])-c[7])+c[8])-c[9])-c[10])+c[0xb]+c[0xc])-c[0xd])-c[0xe])+c[0xf])-c[0x10])-c[0x11])+c[0x12]+c[0x13])-c[0x14])-c[0x15])+c[0x16])-c[0x17])+c[0x18]+c[0x19])-c[0x1a])+c[0x1b])-c[0x1c])+c[0x1d]+c[0x1e])-c[0x1f])-c[0x20])-c[0x21])-c[0x22])-c[0x23])-c[0x24])-c[0x25])+c[0x26])-c[0x27])-c[0x28])-c[0x29])
solver.add(enc[0x18]==(((((((((((((((((((((((((c[0]+c[1])-c[2])+c[3]+c[4])-c[5])+c[6]+c[7])-c[8])+c[9]+c[10])-c[0xb])-c[0xc])-c[0xd])-c[0xe])+c[0xf]+c[0x10]+c[0x11])-c[0x12])+c[0x13]+c[0x14]+c[0x15]+c[0x16]+c[0x17]+c[0x18]+c[0x19])-c[0x1a])-c[0x1b])-c[0x1c])+c[0x1d]+c[0x1e])-c[0x1f])+c[0x20]+c[0x21]+c[0x22])-c[0x23])-c[0x24])-c[0x25])-c[0x26])+c[0x27]+c[0x28])-c[0x29])
solver.add(enc[0x19]==((((((((((((((((((((((((((((((((c[0]-c[1])+c[2]+c[3])-c[4])+c[5]+c[6])-c[7])+c[8]+c[9]+c[10])-c[0xb])-c[0xc])+c[0xd])-c[0xe])+c[0xf])-c[0x10])+c[0x11]+c[0x12]+c[0x13])-c[0x14])-c[0x15])+c[0x16]+c[0x17])-c[0x18])-c[0x19])+c[0x1a])-c[0x1b])+c[0x1c])-c[0x1d])+c[0x1e])-c[0x1f])-c[0x20])+c[0x21])-c[0x22])-c[0x23])-c[0x24])-c[0x25])+c[0x26])-c[0x27])+c[0x28]+c[0x29])
solver.add(enc[0x1a]==(((((((((((((((((((((((((((((c[0]+c[1]+c[2]+c[3]+c[4])-c[5])-c[6])+c[7])-c[8])-c[9])-c[10])-c[0xb])+c[0xc])-c[0xd])+c[0xe])-c[0xf])+c[0x10])-c[0x11])+c[0x12])-c[0x13])-c[0x14])+c[0x15]+c[0x16]+c[0x17]+c[0x18]+c[0x19])-c[0x1a])-c[0x1b])-c[0x1c])-c[0x1d])+c[0x1e]+c[0x1f])-c[0x20])-c[0x21])-c[0x22])+c[0x23]+c[0x24])-c[0x25])-c[0x26])+c[0x27]+c[0x28]+c[0x29])
solver.add(enc[0x1b]==(((((((((((((((((((((((((((((((c[0]-c[1])+c[2])-c[3])+c[4])-c[5])-c[6])-c[7])-c[8])-c[9])-c[10])-c[0xb])+c[0xc]+c[0xd])-c[0xe])+c[0xf]+c[0x10]+c[0x11]+c[0x12]+c[0x13])-c[0x14])-c[0x15])-c[0x16])-c[0x17])+c[0x18]+c[0x19]+c[0x1a])-c[0x1b])+c[0x1c]+c[0x1d]+c[0x1e])-c[0x1f])-c[0x20])-c[0x21])-c[0x22])+c[0x23])-c[0x24])-c[0x25])-c[0x26])-c[0x27])-c[0x28])-c[0x29])
solver.add(enc[0x1c]==((((((((((((((((((((((((((((((c[0]-c[1])+c[2]+c[3]+c[4])-c[5])+c[6]+c[7])-c[8])-c[9])+c[10]+c[0xb])-c[0xc])+c[0xd])-c[0xe])+c[0xf])-c[0x10])+c[0x11]+c[0x12]+c[0x13])-c[0x14])-c[0x15])+c[0x16])-c[0x17])-c[0x18])-c[0x19])-c[0x1a])+c[0x1b])-c[0x1c])-c[0x1d])-c[0x1e])+c[0x1f])-c[0x20])-c[0x21])+c[0x22]+c[0x23]+c[0x24])-c[0x25])-c[0x26])+c[0x27]+c[0x28]+c[0x29])
solver.add(enc[0x1d]==((((((((((((((((((((((((((c[0]+c[1])-c[2])-c[3])-c[4])+c[5]+c[6]+c[7])-c[8])+c[9])-c[10])-c[0xb])+c[0xc])-c[0xd])+c[0xe]+c[0xf])-c[0x10])+c[0x11]+c[0x12])-c[0x13])+c[0x14]+c[0x15]+c[0x16]+c[0x17])-c[0x18])+c[0x19]+c[0x1a])-c[0x1b])+c[0x1c]+c[0x1d]+c[0x1e]+c[0x1f]+c[0x20])-c[0x21])-c[0x22])+c[0x23]+c[0x24])-c[0x25])+c[0x26]+c[0x27])-c[0x28])+c[0x29])
solver.add(enc[0x1e]==((((((((((((((((((((((((((((((((c[0]+c[1]+c[2]+c[3])-c[4])-c[5])-c[6])-c[7])+c[8]+c[9])-c[10])-c[0xb])-c[0xc])+c[0xd])-c[0xe])-c[0xf])+c[0x10])-c[0x11])-c[0x12])-c[0x13])+c[0x14])-c[0x15])-c[0x16])+c[0x17]+c[0x18])-c[0x19])-c[0x1a])+c[0x1b])-c[0x1c])-c[0x1d])-c[0x1e])-c[0x1f])-c[0x20])-c[0x21])-c[0x22])+c[0x23]+c[0x24]+c[0x25])-c[0x26])+c[0x27]+c[0x28]+c[0x29])
solver.add(enc[0x1f]==((((((((((((((((((((((((((((((c[0]+c[1])-c[2])+c[3]+c[4])-c[5])-c[6])+c[7]+c[8]+c[9]+c[10]+c[0xb]+c[0xc])-c[0xd])-c[0xe])-c[0xf])+c[0x10]+c[0x11]+c[0x12]+c[0x13])-c[0x14])+c[0x15])-c[0x16])+c[0x17])-c[0x18])-c[0x19])+c[0x1a]+c[0x1b])-c[0x1c])+c[0x1d])-c[0x1e])-c[0x1f])-c[0x20])+c[0x21])-c[0x22])+c[0x23])-c[0x24])+c[0x25])-c[0x26])+c[0x27])-c[0x28])-c[0x29])
solver.add(enc[0x20]==(((((((((((((((((((((((((((c[0]-c[1])+c[2]+c[3])-c[4])+c[5]+c[6]+c[7]+c[8])-c[9])+c[10]+c[0xb])-c[0xc])+c[0xd]+c[0xe])-c[0xf])+c[0x10])-c[0x11])+c[0x12]+c[0x13]+c[0x14])-c[0x15])-c[0x16])+c[0x17])-c[0x18])+c[0x19]+c[0x1a]+c[0x1b])-c[0x1c])-c[0x1d])-c[0x1e])-c[0x1f])-c[0x20])-c[0x21])+c[0x22]+c[0x23]+c[0x24]+c[0x25])-c[0x26])+c[0x27])-c[0x28])+c[0x29])
solver.add(enc[0x21]==(((((((((((((((((((((((((((((((c[0]-c[1])-c[2])+c[3]+c[4]+c[5]+c[6])-c[7])-c[8])+c[9]+c[10]+c[0xb])-c[0xc])-c[0xd])+c[0xe]+c[0xf])-c[0x10])+c[0x11])-c[0x12])+c[0x13])-c[0x14])+c[0x15]+c[0x16]+c[0x17])-c[0x18])-c[0x19])+c[0x1a]+c[0x1b])-c[0x1c])+c[0x1d])-c[0x1e])-c[0x1f])-c[0x20])-c[0x21])-c[0x22])-c[0x23])+c[0x24])-c[0x25])+c[0x26])-c[0x27])-c[0x28])-c[0x29])
solver.add(enc[0x22]==((((((((((((((((((((((((((((((c[0]+c[1])-c[2])+c[3])-c[4])-c[5])-c[6])+c[7]+c[8]+c[9]+c[10]+c[0xb])-c[0xc])-c[0xd])-c[0xe])+c[0xf])-c[0x10])+c[0x11])-c[0x12])+c[0x13])-c[0x14])-c[0x15])+c[0x16]+c[0x17])-c[0x18])-c[0x19])+c[0x1a]+c[0x1b]+c[0x1c]+c[0x1d])-c[0x1e])-c[0x1f])-c[0x20])-c[0x21])-c[0x22])-c[0x23])-c[0x24])+c[0x25]+c[0x26]+c[0x27])-c[0x28])-c[0x29])
solver.add(enc[0x23]==((((((((((((((((((((((((((c[0]-c[1])+c[2]+c[3]+c[4])-c[5])-c[6])+c[7]+c[8])-c[9])-c[10])+c[0xb]+c[0xc]+c[0xd])-c[0xe])-c[0xf])+c[0x10])-c[0x11])+c[0x12]+c[0x13])-c[0x14])-c[0x15])-c[0x16])+c[0x17]+c[0x18])-c[0x19])-c[0x1a])+c[0x1b]+c[0x1c])-c[0x1d])-c[0x1e])+c[0x1f]+c[0x20])-c[0x21])+c[0x22]+c[0x23]+c[0x24]+c[0x25]+c[0x26]+c[0x27])-c[0x28])-c[0x29])
solver.add(enc[0x24]==(((((((((((((((((((((((((c[0]+c[1]+c[2])-c[3])-c[4])-c[5])-c[6])+c[7]+c[8]+c[9])-c[10])+c[0xb]+c[0xc])-c[0xd])+c[0xe]+c[0xf]+c[0x10]+c[0x11]+c[0x12]+c[0x13]+c[0x14]+c[0x15])-c[0x16])-c[0x17])+c[0x18])-c[0x19])-c[0x1a])-c[0x1b])-c[0x1c])+c[0x1d]+c[0x1e]+c[0x1f]+c[0x20])-c[0x21])-c[0x22])-c[0x23])-c[0x24])+c[0x25])-c[0x26])+c[0x27]+c[0x28])-c[0x29])
solver.add(enc[0x25]==((((((((((((((((((((((((((((((((((((((c[0]-c[1])-c[2])+c[3])-c[4])+c[5])-c[6])-c[7])-c[8])-c[9])+c[10])-c[0xb])-c[0xc])-c[0xd])-c[0xe])-c[0xf])-c[0x10])+c[0x11]+c[0x12])-c[0x13])-c[0x14])-c[0x15])+c[0x16])-c[0x17])+c[0x18])-c[0x19])-c[0x1a])+c[0x1b])-c[0x1c])-c[0x1d])+c[0x1e]+c[0x1f])-c[0x20])+c[0x21])-c[0x22])+c[0x23])-c[0x24])-c[0x25])+c[0x26])-c[0x27])-c[0x28])-c[0x29])
solver.add(enc[0x26]==((((((((((((((((((((((((((c[0]+c[1]+c[2]+c[3])-c[4])+c[5]+c[6]+c[7])-c[8])-c[9])-c[10])+c[0xb]+c[0xc]+c[0xd])-c[0xe])-c[0xf])-c[0x10])-c[0x11])-c[0x12])-c[0x13])+c[0x14]+c[0x15])-c[0x16])+c[0x17]+c[0x18]+c[0x19]+c[0x1a]+c[0x1b])-c[0x1c])-c[0x1d])+c[0x1e]+c[0x1f])-c[0x20])-c[0x21])+c[0x22])-c[0x23])-c[0x24])-c[0x25])+c[0x26]+c[0x27]+c[0x28])-c[0x29])
solver.add(enc[0x27]==(((((((((((((((((((((((((c[0]-c[1])-c[2])-c[3])-c[4])+c[5])-c[6])-c[7])-c[8])+c[9])-c[10])+c[0xb])-c[0xc])+c[0xd]+c[0xe])-c[0xf])-c[0x10])-c[0x11])+c[0x12]+c[0x13]+c[0x14]+c[0x15]+c[0x16])-c[0x17])+c[0x18]+c[0x19]+c[0x1a]+c[0x1b]+c[0x1c])-c[0x1d])+c[0x1e]+c[0x1f]+c[0x20]+c[0x21]+c[0x22])-c[0x23])-c[0x24])+c[0x25]+c[0x26]+c[0x27])-c[0x28])+c[0x29])
solver.add(enc[0x28]==((((((((((((((((((((((c[0]-c[1])-c[2])-c[3])+c[4]+c[5]+c[6])-c[7])+c[8]+c[9])-c[10])+c[0xb])-c[0xc])-c[0xd])-c[0xe])+c[0xf]+c[0x10]+c[0x11]+c[0x12]+c[0x13]+c[0x14]+c[0x15]+c[0x16])-c[0x17])+c[0x18]+c[0x19])-c[0x1a])+c[0x1b]+c[0x1c])-c[0x1d])+c[0x1e]+c[0x1f]+c[0x20])-c[0x21])-c[0x22])+c[0x23]+c[0x24])-c[0x25])+c[0x26]+c[0x27]+c[0x28]+c[0x29])
solver.add(enc[0x29]==(((((((((((((((((((((((((((((((c[0]+c[1]+c[2]+c[3]+c[4]+c[5]+c[6])-c[7])-c[8])-c[9])+c[10]+c[0xb])-c[0xc])+c[0xd])-c[0xe])-c[0xf])-c[0x10])-c[0x11])-c[0x12])-c[0x13])+c[0x14])-c[0x15])+c[0x16])-c[0x17])-c[0x18])+c[0x19]+c[0x1a]+c[0x1b]+c[0x1c])-c[0x1d])-c[0x1e])-c[0x1f])-c[0x20])-c[0x21])-c[0x22])-c[0x23])-c[0x24])-c[0x25])-c[0x26])-c[0x27])-c[0x28])+c[0x29])
if solver.check() == sat :
r = solver.model()
for i in range(42) :
flag.append(r[c[i]].as_long()^i)
flag = ''.join(map(lambda x : chr(((x >> 3) | (x << 5)) & 0xff),flag))
print(flag)
# SUCTF{Un1c0rn_Engin3_Is_@_P0wer7ul_TO0ls!}
Akira Homework
程序有反調(diào)試这敬,nop
掉 call cs:IsDebuggerPresent
鸳址,還開了 ASLR
呀非,用 010editor 關(guān)閉 ASLR
v1 = 0;
memset(&v4, 0, 0x13ui64);
memset(&Dst, 0, 0x6Dui64);
memcpy(&Dst, aJLJLJL1pzxcp6b, 0x6Cui64);
for ( i = 0; (unsigned __int64)i < 0x6C; ++i )
aJLJLJL1pzxcp6b[i] ^= byte_140015F20[0];
puts(aJLJLJL1pzxcp6b);
sub_140009FF0((__int64)"%18s", &v4, 19i64);
調(diào)試到這唧瘾,發(fā)現(xiàn)程序很多的字符串應(yīng)該都是加密了功戚,而 key
就是 byte_140015F20
sub_140009FF0
應(yīng)該是一個(gè)輸入函數(shù)僚楞,但是每次調(diào)試到那程序都自動(dòng)退出勤晚,不知道是反調(diào)試還是本身有 bug
只好 nop
掉它,手工修改內(nèi)存
v13 = a1;
memset(Dst, 0, 0x13ui64);
memcpy(Dst, &unk_140015E38, 0x12ui64);
for ( i = 0; (unsigned __int64)i < 19; ++i )
Dst[i] ^= byte_140015F20[1];
memset(v12, 0, 0x13ui64);
for ( j = 0; (unsigned __int64)j < 18; ++j )
v12[j] = j ^ *(_BYTE *)(v13 + j);
v5 = 1;
v6 = 5;
v7 = 4;
v8 = 2;
v9 = 3;
v10 = 0;
for ( k = 0; (unsigned __int64)k < 0x12; ++k )
{
if ( Dst[k] != v12[6 * (k / 6) + *(&v5 + k % 6)] )
return 0;
}
return 1;
第一段算法很好懂泉褐,逆向一下
s = [0 for i in range(18)]
v5 = [1,5,4,2,3,0]
dst =[int(i,16) for i in '6A 5A 65 6B 71 41 72 68 55 7C 39 67 3E 30 4F 7D 7C 64 45'.split(" ")]
for k in range(0x12) :
s[6 * (int(k / 6)) + v5[k % 6] ] = dst[k]
for j in range(18) :
s[j] = s[j] ^ j
print(''.join(map(chr,s)))
# Akira_aut0_ch3ss_!
得到一段字符串
繼續(xù)調(diào)試然后程序執(zhí)行到了
sub_140008300((__int64)&v9, 3i64, 1048578);
return sub_140006C10(qword_140016178, v9, 0i64);
跟進(jìn) sub_140006C10
函數(shù)看看
發(fā)現(xiàn)這里對(duì)大量的數(shù)據(jù)進(jìn)行解密操作
char __fastcall sub_140008910(__int64 a1, const char *a2)
{
int i; // [rsp+20h] [rbp-28h]
int v4; // [rsp+24h] [rbp-24h]
char *Str; // [rsp+28h] [rbp-20h]
Str = (char *)a2;
v4 = strlen(a2);
for ( i = 0; i < dword_140011194; ++i )
{
if ( !(i % 3) )
byte_1400111A0[i] ^= Str[i / 3 % v4]; // .data:00000001400111A0
}
SetEvent(Handles[0]); // 設(shè)置事件 Handles[0] 為激發(fā)狀態(tài)
return 1;
}
密鑰就是我們剛才傳入的字符串赐写,這說明 sub_140006C10
是一個(gè)解密函數(shù),交叉引用看下有三處調(diào)用膜赃,結(jié)合前面有一段創(chuàng)建三個(gè)空事件的代碼挺邀,猜測可能是要進(jìn)行三次解密之后將這塊數(shù)據(jù) dump
出來
調(diào)試到 sub_1400093B0
獲取了當(dāng)前進(jìn)程的路徑
hFile = CreateFileW(&Filename, 0x80000000, 1u, 0i64, 3u, 0, 0i64);
并且讀取了 filePath:signature
交換數(shù)據(jù)流
sub_140007DD0(&Buffer, v2, (__int64)v26); // md5
v10 = 0xFCu;
v11 = 0xAEu;
v12 = 0xEBu;
v13 = 0x6E;
v14 = 0x34;
v15 = 0xB4u;
v16 = 0x30;
v17 = 0x3E;
v18 = 0x99u;
v19 = 0xB9u;
v20 = 0x12;
v21 = 6;
v22 = 0xBDu;
v23 = 0x32;
v24 = 0x5F;
v25 = 0x2B;
并且經(jīng)過 md5
運(yùn)算然后比較
反查 FCAEEB6E34B4303E99B91206BD325F2B
得到 Overwatch
添加交換數(shù)據(jù)流信息
echo Overwatch > WinRev.exe:signature
還要注意在內(nèi)存中把截?cái)喾由希蝗?md5 值不一樣
Stack[00002EE4]:000000000014FC2B db 4Fh ; O
Stack[00002EE4]:000000000014FC2C db 76h ; v
Stack[00002EE4]:000000000014FC2D db 65h ; e
Stack[00002EE4]:000000000014FC2E db 72h ; r
Stack[00002EE4]:000000000014FC2F db 77h ; w
Stack[00002EE4]:000000000014FC30 db 61h ; a
Stack[00002EE4]:000000000014FC31 db 74h ; t
Stack[00002EE4]:000000000014FC32 db 63h ; c
Stack[00002EE4]:000000000014FC33 db 68h ; h
Stack[00002EE4]:000000000014FC34 db 0
最后一樣也是會(huì)執(zhí)行到解密函數(shù) sub_140006C10
if ( (unsigned __int64)k >= 0x10 )
{
sub_140008300((__int64)&v9, 3i64, 1048578);
return sub_140006C10(qword_140016178, v9, 0i64);
}
之后程序就開啟 sleep
了跳座,應(yīng)該還遺漏了什么端铛,回頭去檢查發(fā)現(xiàn)
v6 = beginthreadex(0i64, 0, (unsigned int (__stdcall *)(void *))StartAddress, &ArgList, 0, 0i64);
這個(gè)函數(shù)啟動(dòng)了一個(gè)子線程,調(diào)試一下子線程看看它做了什么
在調(diào)試的過程中疲眷,要注意這里有個(gè) TLS
回調(diào)函數(shù)
__int64 TlsCallback_0()
{
__int64 result; // rax
int i; // [rsp+20h] [rbp-18h]
int j; // [rsp+24h] [rbp-14h]
int k; // [rsp+28h] [rbp-10h]
int l; // [rsp+2Ch] [rbp-Ch]
for ( i = 0; (unsigned __int64)i < 0x1A; ++i )
ProcName[i] ^= byte_140015F20[6]; // NtQueryInformationProcess
for ( j = 0; (unsigned __int64)j < 0x19; ++j )
asc_140015EA8[j] ^= byte_140015F20[7]; // ZwQueryInformationThread
for ( k = 0; (unsigned __int64)k < 0x11; ++k )
asc_140015EC8[k] ^= byte_140015F20[8]; // NtQueueApcThread
for ( l = 0; ; ++l )
{
result = l;
if ( (unsigned __int64)l >= 0xA )
break;
byte_140015E78[l] ^= byte_140015F20[5]; // ntdll.dll
}
return result;
}
主要用來解密出三個(gè)函數(shù)的名字禾蚕,由于這題開了多線程,TLS
回調(diào)函數(shù)會(huì)被多次執(zhí)行狂丝。换淆。我們在這里下個(gè)斷點(diǎn)哗总,讓它只解密一次。倍试。之后斷到這里都直接修改 RIP
跳過
接著繼續(xù)分析我們的子線程
v4 = Process32FirstW(hSnapshot, &pe);
if ( !v4 )
return 0i64;
while ( v4 )
{
memset(v9, 0, 0x21ui64);
memset(Str1, 0, 0x42ui64);
v1 = wcslen(pe.szExeFile);
sub_140007DD0(pe.szExeFile, v1, (__int64)v9);
for ( i = 0; i < 16; ++i )
sub_140008DF0((__int64)&Str1[2 * i], 3i64, (__int64)L"%02x", (unsigned __int8)v9[i]);
for ( j = 0; (unsigned __int64)j < 0x31; ++j )
{
if ( !wcscmp((const wchar_t *)Str1, &a438078d884693c[33 * j]) )
exit(-1);
}
v4 = Process32NextW(hSnapshot, &pe); // 列進(jìn)程 判斷進(jìn)程 md5 是否有跟常量表中相同的 相同就退出
}
這里建了一個(gè)循環(huán)獲取所有進(jìn)程名的 md5
跟常量表的 md5
比較讯屈,相同就退出程序,猜測是反調(diào)試易猫。F9
一運(yùn)行程序果然停止了耻煤,應(yīng)該是檢測到了 IDA
的進(jìn)程 (我用 IDA 調(diào)試的)
看到后面又運(yùn)行了
if ( !byte_140016158 )
{
sub_140006C10(qword_140016178, v7, (__int64)&v5);
byte_140016158 = 1;
}
這里我直接修改 RIP
到 sub_140006C10
,執(zhí)行解密函數(shù)准颓,到這里應(yīng)該要開始考慮解密函數(shù)的調(diào)用順序問題哈蝇,應(yīng)該是子線程先執(zhí)行,接著是 Akira_aut0_ch3ss_!
密鑰解密攘已,最后是數(shù)據(jù)流密碼的解密炮赦。
由于子線程還沒執(zhí)行完,我們繼續(xù)跟蹤下去
sub_140008500(qword_140016188);
這個(gè)函數(shù)傳了一個(gè)全局變量样勃,該全局變量指向 TLS
回調(diào)函數(shù)中解密出來的三個(gè)函數(shù)地址
NtQueryInformationProcess
ZwQueryInformationThread
NtQueueApcThread
單步進(jìn)去發(fā)現(xiàn)各種反調(diào)試函數(shù)...
sub_140008D20(qword_140016188, *v1, (__int64)sub_140009850);
這里通過調(diào)用 NtQueueApcThread
將 sub_140009850
函數(shù)加入 APC
隊(duì)列 (此處涉及 windows 內(nèi)核理解不是很深吠勘,不到位的地方煩請大佬補(bǔ)充)
接下來進(jìn)入了 sub_140009850
WaitForMultipleObjects(3u, Handles, 1, 5000u);
這里等待 5 秒,需要三個(gè)事件都處于激活狀態(tài)才能往下執(zhí)行峡眶。
到這里我選擇重新調(diào)試剧防,先暫停主線程,將子線程運(yùn)行到 WaitForMultipleObjects
處辫樱,并暫停子線程峭拘,恢復(fù)主線程,再按照前面說的順序把主線程的解密函數(shù)執(zhí)行完狮暑,然后暫停主線程鸡挠,恢復(fù)子線程,此時(shí)三個(gè)事件已經(jīng)都激活了
往下調(diào)發(fā)現(xiàn)在校驗(yàn)文件頭
if ( *v11 == 'M' && *((_BYTE *)v5 + 1) == 'Z' )
{
sub_140007D80((__int64)v5, v4, 0, (__int64)v8);
v10 = v0;
if ( v0 )
v2 = 0;
}
接著就可以單步進(jìn)入 sub_140007D80
了
這個(gè)函數(shù)粗略看下做了一些拷貝的工作和釋放內(nèi)存的操作搬男,我們把解密出來的 DLL
導(dǎo)出拣展,然后再拖入 IDA
分析
if ( Src )
{
CloseHandle(hFileMappingObject);
Dst = malloc(0x8000ui64);
memset(Dst, 0, 0x8000ui64);
memcpy(Dst, Src, 0x8000ui64);
strcpy(&v7, "Ak1i3aS3cre7K3y");
memset(&Str1, 0, 0x11ui64);
sub_7FFFDA782800(&v7, &Str1, Dst);
if ( !strcmp(&Str1, &Str2) )
sub_7FFFDA7826F0("Get finally answer!\n");
else
sub_7FFFDA7826F0("wow... game start!\n");
result = 1;
}
sub_7FFFDA782800
函數(shù)往里走會(huì)發(fā)現(xiàn) AES
S盒代換表,因此猜測是 AES
加密缔逛,并且密鑰是 Ak1i3aS3cre7K3y
按這個(gè)密鑰來看應(yīng)該是 128 bit 的長度
密文在子線程中
Src = 0x94u;
v16 = 0xBFu;
v17 = 0x7A;
v18 = 0xC;
v19 = 0xA4u;
v20 = 0x35;
v21 = 0x50;
v22 = 0xD1u;
v23 = 0xC2u;
v24 = 0x15;
v25 = 0xECu;
v26 = 0xEFu;
v27 = 0x9Du;
v28 = 0x9Au;
v29 = 0xAAu;
v30 = 0x56;
最后得到 flag{Ak1rAWin!}