攻防世界新手練習題_REVERSE(逆向)
re1
題目描述:菜雞開始學習逆向工程,首先是最簡單的題目
做題實錄
雙擊點開,讓我輸入flag
隨便輸一個:
看樣子是要比較字符串的,放到OD里面看看,找到了比較字符串的地方,下斷點
隨便輸入什么字符之后繼續往下走,發現flag已經進入到了ESP中
分析總結
這個題非常簡單,沒什么好說的,其實放到OD里還可以直接搜索字符串,一步到位……
當然了,還可以用IDA直接看源碼
看到strcmp()
函數,輸入的是v9,和v5比較,所以數據就應該在v5里面,再看第10行,把xmmword_413E34
的值賦給v5,所以雙擊xmmword_413E34
看它的數據。把qword_413E44
和xmmword_413E34
的數據拼起來
你可以把qword_413E44
和xmmword_413E34
的數據拼起來用python把16進制的數轉化成文本然后反向寫出flag就可以了。
print ('7D4654435455443074656D30633165577B465443545544'.decode('hex'))
|
|
# 解碼結果: }FTCTUD0tem0c1eW{FTCTUD
|
或者光標點擊xmmword
的數據,按下a鍵,
確認就可以直接把它轉化成字符串。(我也是后來才知道的,看來ida還是要好好學)
game
題目描述:菜雞最近迷上了玩游戲,但它總是贏不了,你可以幫他獲勝嗎
做題實錄
一個exe文件,打開如下:
讓你玩游戲,贏了就有flag,於是玩三個小時玩通關拿到flag。放到OD里動態調試
一路走到這里讓你輸入n,估計接下來就是字符串比較了,隨便輸一個繼續調試
這部分一整段都在判斷燈的情況,調試的過程中發現只要判斷出燈沒有全亮,就會跳轉回003EF4FB,沒有進入game.003E7AB4,所以猜測是成功了就進入這里,於是進去看看
進來之后,果不其然,是成功的函數
接下來就運行這個函數,就可以得到flag
分析總結
這個題還是比較簡單的,沒啥好說的。再用IDA來試一下
進入main_0()
函數,看到判斷語句,邏輯很簡單
進入sub_457AB4()
這個程序就瘋狂跳轉,不知道要干啥
進去之后就是這樣
可以直接寫個腳本跑,在這里我選擇改程序的邏輯,讓它跳過判斷直接執行這個函數
這一部分就是判斷語句,只要把cmp edx, 1
改成cmp edx edx
那么條件就恆為真。
特別注意的是:
cmp edx, 1
占3個字節而cmp edx edx
占兩個字節,所以最后一個字節用nop
填充。否則會出錯
修改后如下:
保存之后雙擊運行,隨便輸入一個n,就可以得出flag
Hello,CTF
題目描述:菜雞發現Flag似乎並不一定是明文比較的
做題實錄
一個exe,運行結果如下:
要找序列號,IDA看一下:
看來flag就是這個綠色的字符串了,看到有字母有數字且字母不超過f,所以16進制解碼試一下,得到flag:
CrackMeJustForFun
分析總結
這個題比較簡單,我開始是用動態調試做的,都差不多,只要看到這個字符串基本上就可以解決了。
open-source
題目描述:菜雞學逆向學得頭皮發麻,終於它拿到了一段源代碼
做題實錄
源代碼如下:
|
|
|
|
int main(int argc, char *argv[]) {
|
|
if (argc != 4) {
|
|
printf("what?\n");
|
|
exit(1);
|
|
}
|
|
unsigned int first = atoi(argv[1]);
|
|
if (first != 0xcafe) {
|
|
printf("you are wrong, sorry.\n");
|
|
exit(2);
|
|
}
|
|
unsigned int second = atoi(argv[2]);
|
|
if (second % 5 == 3 || second % 17 != 8) {
|
|
printf("ha, you won't get it!\n");
|
|
exit(3);
|
|
}
|
|
if (strcmp("h4cky0u", argv[3])) {
|
|
printf("so close, dude!\n");
|
|
exit(4);
|
|
}
|
|
printf("Brr wrrr grr\n");
|
|
unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;
|
|
printf("Get your key: ");
|
|
printf("%x\n", hash);
|
|
return 0;
|
|
}
|
看到就是要輸入規定的參數運行就可以了,注意這個參數要在命令行中輸入。第一個參數直接跑個腳本:
print(str(
0xcafe))
|
|
#51966
|
第二個參數最小的是25,第三個參數h4cky0u
編譯之后cmd運行,得到flag:
分析總結
分析源碼,沒啥可說的。
simple-unpack
題目描述:菜雞拿到了一個被加殼的二進制文件
做題實錄
UltraEdit打開看一下,搜索flag:
去掉中間的亂碼,得到flag:
flag{Upx_1s_n0t_a_d3liv3r_c0mp4ny}
分析總結
直接查找確實是歪門邪道,畢竟這題是關於加殼的,所以UE看一下:
看到這是個upx的殼,那直接用upx脫殼就行了:
脫完殼查找flag即可:
logmein
題目描述:菜雞開始接觸一些基本的算法逆向了
做題實錄
IDA里看一下:
第26行就是加密算法,寫個解密算法就行了:
|
|
|
|
|
|
void main()
|
|
{
|
|
int v6;
|
|
int v9;
|
|
long long v7;
|
|
int i;
|
|
v9 =
0;
|
|
char * str = ":\"AL_RT^L*.?+6/46";
|
|
v7 =
28537194573619560LL;
|
|
v6 =
7;
|
|
for ( i = 0;i < 17; ++i )
|
|
{
|
|
printf("%c",*((char*)&v7 + i % v6)^str[i]);
|
|
}
|
|
}
|
|
// RC3-2016-XORISGUD
|
分析總結
這個題就是簡單的算法逆向,沒啥可說的。
insanity
題目描述:菜雞覺得前面的題目太難了,來個簡單的緩一下
做題實錄
IDA打開,查找flag,得到flag:
分析總結
實際上這個題就直接運行,等上幾秒它會隨機給你輸出一些字符串,我第一次運行就直接得到了flag……
no-strings-attached
題目描述:菜雞聽說有的程序運行就能拿Flag?
做題實錄
這是個32位的elf文件,ubuntu里跑一下,出來一堆不知道是啥的東西。
IDA打開
這個authenticate()
看着很可疑,進去看看:
看到一個decrypt(...)
,估計就是它了,進去看看
看樣子是一個解密函數,在decrypt(...)
下斷點進行動態 調試,進入while循環Hex View跟蹤EAX(因為函數返回值就存在EAX里),可以看到flag正在被解密出來,繼續往下走
解密完成,得出flag,去掉0x00就是最終的flag,由於題目來源9447CTF,所以沒有前面的1。
分析總結
我看了一下,基本上都差不多,不過很多都是用gdb調試的,沒用過gdb,正好趁這個機會學習一下,寫一下gdb調試的方法。
進入gdb后輸入 file no-strings-attached 開始調試程序
然后在decrypt()
下斷點,用命令 break decrypt
按n是單步步過,s是單步步進,啥也不輸直接按回車默認執行上一條指令
單步執行到這里發現EAX里面存了9,是flag的開頭
記下此時EAX的位置0x804cff0,輸入 finish 讓程序執行完當前的函數並返回
查看EAX的內容,輸入命令 x/100u $eax (該命令具體使用方式參見GDB下查看內存命令(x命令))
寫個腳本跑出來就行了
s=[
57,52,52,55,123,121,111,117,95,97,114,101,95,97,110,95,
|
|
105,110,116,101,114,110,97,116,105,111,110,97,108,95,109,121,
|
|
115,116,101,114,121,125]
|
|
flag=
''
|
|
for i in range(len(s)):
|
|
flag += chr(s[i])
|
|
print (flag)
|
|
#得出結果:9447{you_are_an_international_mystery}
|
getit
題目描述:菜雞發現這個程序偷偷摸摸在自己的機器上搞事情,它決定一探究竟
做題實錄
這是個elf文件,在ubuntu里跑了一下發現沒反應,放到IDA里看一下
看樣子估計是個解密flag的,看了看數據段發現了t是flag(IDA里面按a把ASCII碼變成字符串),但是內容不知道
我猜while循環應該就是解密的,於是動態調試一下。
可以看到在循環的過程中下面flag就已經出現了,繼續調試得出flag
分析總結
這個題其實也可以不用動態調試,因為while循環里轉換算法都寫好了,自己按照源碼寫個腳本跑就行了。
s =
'c61b68366edeb7bdce3c6820314b7498'
|
|
flag =
''
|
|
for i in range(len(s)):
|
|
if i & 1:
|
|
t =
1
|
|
else:
|
|
t =
-1
|
|
flag += chr(ord(s[i]) + t)
|
|
print (flag)
|
|
#得出結果: b70c59275fcfa8aebf2d5911223c6589
|
python-trade
題目描述:菜雞和菜貓進行了一場Py交易
做題實錄
下下來是pyc文件,放到ubuntu里面,用uncompyle反編譯(下載過程也踩了很多坑……)並保存到tes.py
uncompyle6 Py.pyc > tes.py
得到源碼:
# uncompyle6 version 3.3.5
|
|
# Python bytecode 2.7 (62211)
|
|
# Decompiled from: Python 2.7.15+ (default, Nov 27 2018, 23:36:35)
|
|
# [GCC 7.3.0]
|
|
# Embedded file name: 1.py
|
|
# Compiled at: 2017-06-02 19:20:43
|
|
import base64
|
|
def encode(message):
|
|
s =
''
|
|
for i in message:
|
|
x = ord(i) ^
32
|
|
x = x +
16
|
|
s += chr(x)
|
|
return base64.b64encode(s)
|
|
correct =
'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
|
|
flag =
''
|
|
print 'Input flag:'
|
|
flag = raw_input()
|
|
if encode(flag) == correct:
|
|
print 'correct'
|
|
else:
|
|
print 'wrong'
|
|
# okay decompiling Py.pyc
|
是一個加密的程序,那按照它的步驟解密就行了,是一個算法逆向的題。
解密代碼如下:
import base64
|
|
def decode(message):
|
|
message = base64.b64decode(message)
|
|
s =
''
|
|
for i in message:
|
|
x = ord(i)
-16
|
|
x = x ^
32
|
|
s += chr(x)
|
|
return s
|
|
message =
'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
|
|
print (decode(message))
|
|
#得出結果: nctf{d3c0mpil1n9_PyC}
|
分析總結
這題沒啥好說的,就是算法逆向。但是安裝使用uncompyle遇到點問題,也記下來吧。
pip install uncompyle
然后使用的時候需要輸compyle6,我在輸了之后一直提示我“command not found”,后來發現需要執行這兩條命令安裝環境變量
export PATH=$HOME/bin:/usr/local/bin:$PATH
|
|
PATH=$HOME/bin:/usr/local/python27/bin:/usr/local/bin:$PATH
|
csaw2013reversing2
題目描述:聽說運行就能拿到Flag,不過菜雞運行的結果不知道為什么是亂碼
做題實錄
拿到的是個exe文件,雙擊運行結果如圖:
正如描述所說,是一堆亂碼,放到OD里看一下。
發現這個位置就是彈出flag的位置。
在這兒設個斷點,然后進去仔細看。
進來之后看這個函數,發現有兩個MessageBox函數,覺得有蹊蹺,於是在00B01094處取消跳轉,讓它往下走看看有什么反應。
在走的時候把int3用nop填充,00B010A3處的jmp跳轉也用nop填充
走到第一個MessageBox,彈出了一個flag,但是什么也沒有
於是點擊忽略繼續往下走,又遇到了jmp跳轉,同樣nop填充
走到這flag已經加載進內存了。
分析總結
方法一:直接修改程序邏輯
參考【XCTF 攻防世界】 Reverse —— csaw2013reversing2
由於是個32位的PE文件(linux下用file命令查看),所以用ida pro打開,F5反編譯,生成類C語言的偽代碼如下:
lpMem就是flag的內容。看第10行if語句,如果sub_40102A()
或IsDebuggerPresent()
返回值為真則執行__debugbreak()
調試斷點函數、子函數sub_401000(...)
和退出進程函數ExitProcess(...)
函數,否則直接執行MessageBoxA(...)
函數彈出flag框。
打開sub_40102A()
函數看一下:
發現返回值恆等於零,那就是說只有在調試狀態下才會執行if里面的語句。由於直接雙擊運行沒有在調試環境下,彈出亂碼的flag,所以可以肯定if里面的語句是關鍵點,應該包含解密flag的函數。
打開sub_401000(...)
看一下:
看樣子是個解密函數,那應該就是它了。那么思路就很清晰了,我們要讓程序跳過if判斷,直接執行sub_401000(...)
函數,然后再跳過ExitProcess(...)
函數執行MessageBoxA(...)
函數彈出解密后的flag框。
既然思路已經清晰那么就開始修改程序吧。
這是原本的邏輯:
使用IDA修改指令的方法是將光標放在要修改的指令上,依次點擊Edit -> Patch program -> Assemble,彈出指令修改框進行修改。
修改之后邏輯如下:
紅色標注的就是修改過的地方。修改完成后要保存到文件,依次點擊 Edit -> Patch program -> Apply patches to input file…,彈出設置框,選擇待打補丁程序進行修改。
最后雙擊運行修改后的程序,直接彈出已解密的flag框。
方法二:DLL注入
maze
題目描述:菜雞想要走出菜狗設計的迷宮
做題實錄
打開IDA看了半天源碼,不知道和迷宮有啥關系,以前沒做過這種題,還以為是代碼不停跳轉什么的,還是最后看了wp才明白,真的是個迷宮(吐血)。還是要做題,提高自己的姿勢水平,不然就太naive了。
分析總結
這是個64位的elf文件,放IDA里看一下main函數
(請無視我剛開始看的時候做的注釋)首先看第13行,判斷輸入的字符串,從這里可以看出要求輸入的flag長度為24,且以 'nctf{' 開頭,以 '}' 結尾(最后的 '}' 本來是ASCII碼,在IDA里把光標選擇ASCII碼按R鍵就可以把ASCII碼變成字符了)。
由於是迷宮,一般是二維的,所以應該有一個記錄坐標的變量,看到v9,在判斷中有一個&v9
和(&v9)+1
(也就是v9跟着的的下一個字節的地址),那么可以猜想v9就是一個記錄位置坐標信息的二維數組。那么while循環就是在判斷輸入的字符來改變坐標信息,也就是用戶輸入字符來走迷宮。
正常來說,v9和v9+1對應着迷宮的行和列(別問我為啥不是列和行,我覺得正常講話就是行在前,所以自然而然想到行和列。就算不對那到時候再改嗎,反正就兩種情況。而且下面的分析可以確定就是對應的行和列)。
既然是走迷宮,當然就得判斷走沒走到終點,看到了第72行的“Congratulations!”,那看一下它前面第70行的判斷,判斷坐標所在,如果是“#”,就到終點了,說明“#”號就代表終點。而且看一下asc_601060
有個“#”,看來這應該就是迷宮的地圖了。我們的目標就是要走到“#”處。再回過頭來看8*v9+SHIDWORD(V9),既然是坐標,說明v9就代表行,而v9+1代表列,而且可以知道迷宮的地圖應該是8個字符一行的(因為行增加一,數就增加8)。
至於這個LABLE_15,每走一步都要進入這里,應該就是判斷有沒有越界什么的函數。
現在基本上就已經理清楚了這個迷宮,只要知道怎么走就行了。while循環里的函數如下:
//因為是&v9+1,所以是判斷列,也就是左右走
|
|
bool __fastcall sub_400650(_DWORD *a1)//(_DWORD *)&v9 + 1
|
|
{
|
|
//為'O'
|
|
int v1; // eax
|
|
v1 = (*a1)--;
//往左走
|
|
return v1 > 0;
|
|
}
|
|
bool __fastcall sub_400660(int *a1)//(int *)&v9 + 1
|
|
{
|
|
//為'o'
|
|
int v1; // eax
|
|
v1 = *a1 +
1; //往右走
|
|
*a1 = v1;
|
|
return v1 < 8;
|
|
}
|
|
//上下走
|
|
bool __fastcall sub_400670(_DWORD *a1)//&v9
|
|
{
|
|
//為'.'
|
|
int v1; // eax
|
|
v1 = (*a1)--;
//往上走
|
|
return v1 > 0;
|
|
}
|
|
bool __fastcall sub_400680(int *a1)//(int *)&v9
|
|
{
|
|
//為'0'
|
|
int v1; // eax
|
|
v1 = *a1 +
1; //往下走
|
|
*a1 = v1;
|
|
return v1 < 8;
|
|
}
|
總結一下:“.”往上走、“0”往下走、“O”往左走、“o”往右走
拿個腳本把地圖寫出來(python是真的好用)
map =
' ******* * **** * **** * *** *# *** *** *** *********'
|
|
for i in range(0, len(map), 8):
|
|
print (map[i: i + 8])
|
|
#得出結果:
|
|
'''
|
|
******
|
|
* * *
|
|
*** * **
|
|
** * **
|
|
* *# *
|
|
** *** *
|
|
** *
|
|
********
|
|
'''
|
要求步長為18,從左上角開始走,走到“#”的位置,走就行了,最后結果為
nctf{o0oo00O000oooo..OO}