感覺難度越來越大了吃溅,把進(jìn)階題分為幾篇來寫,以便查看鸯檬。
0x00 reverse-box
感覺都是沒有看到過的題型决侈,看了大佬WP,還是不懂喧务。赖歌。。先留著以后再看功茴。
要用GDB命令腳本先放個(gè)鏈接如何寫gdb命令腳本庐冯。
0x01 IgniteMe
這道題就非常的友好了,拖進(jìn)IDA坎穿。
檢查輸入的前四個(gè)字符為 ' EIS{ ' 最后一個(gè)為 ‘ } ’展父,然后進(jìn)入sub_4011c0() 關(guān)鍵函數(shù)。
這個(gè)函數(shù)就是交換大小寫玲昧,然后異或操作之后看與最后的字符串一不一樣栖茉。
首先雙擊unk_4420B0選中,按shift+e 把它提取出來(新學(xué)的操作孵延,好舒服奥榔!終于不用手動(dòng)碼了)隙袁。
然后就是寫腳本異或回去得到v4了,在轉(zhuǎn)換大小寫加上 EIS{ 和 } 就是flag了弃榨。
腳本如下:
a = "GONDPHyGjPEKruv{{pj]X@rF"
xor_string = [ 0x0D, 0x13, 0x17, 0x11, 0x02, 0x01, 0x20, 0x1D, 0x0C, 0x02,
0x19, 0x2F, 0x17, 0x2B, 0x24, 0x1F, 0x1E, 0x16, 0x09, 0x0F,
0x15, 0x27, 0x13, 0x26, 0x0A, 0x2F, 0x1E, 0x1A, 0x2D, 0x0C,
0x22, 0x04]
s = []
flag = ''
for i in range(len(a)):
s.append(ord(a[i])^ xor_string[i])
for i in range(len(s)):
s[i] -= 72
s[i] = s[i]^0x55
for i in s:
flag += chr(i)
print(flag.lower())
0x02 srm-50
這道題感覺我是非預(yù)期解啊菩收,都不分析什么,正解應(yīng)該是用OD破解找到注冊(cè)碼的鲸睛。我直接拖進(jìn)IDA,F12找到報(bào)錯(cuò)的字符串娜饵,看到if判斷語(yǔ)句,將v11[0],v11[1],v11[2],v11[3],v12,v13…………連續(xù)字符得到就是flag了官辈。箱舞。遍坟。。晴股。愿伴。
0x03 ReverseMe-120
拖進(jìn)IDA,看到主函數(shù)感覺很簡(jiǎn)單的亞子电湘。我們看到最后只判斷了v13是否與“you_know_how_to_remove_junk_code” 一樣隔节,往上分析v13經(jīng)過了sub_401000()然后與0x25異或。
跟進(jìn)sub_401000()寂呛,分析不出來是什么怎诫,,贷痪,看了大佬的WP ,這是base64的解密幻妓,還是太菜了,沒有看出特征劫拢。所以將“you_know_how_to_remove_junk_code” 與0x25異或后再base64加密得到flag肉津。
0x04 CRACKME
運(yùn)行exe,要調(diào)入注冊(cè)碼,隨便輸入有彈窗錯(cuò)誤尚镰。拖進(jìn)IDA,發(fā)現(xiàn)是MFC寫的程序阀圾。參考大佬文章,MessageBoxA函數(shù)是于創(chuàng)建狗唉、顯示并操作一個(gè)消息對(duì)話框的初烘,所以我們找到調(diào)用MessageBoxA的函數(shù)。
跟進(jìn)sub_401720 和sub_4016E0發(fā)現(xiàn)不了什么分俯,繼續(xù)查看他們的引用肾筐。在這里就可以猜測(cè)這兩個(gè)MessageBoxA一個(gè)是顯示錯(cuò)誤彈窗一個(gè)是正確彈窗。輸入的數(shù)據(jù)經(jīng)過sub_401630()之后才判斷對(duì)錯(cuò)缸剪。所以跟進(jìn)sub_401630()分析吗铐。
這里看了WP有點(diǎn)不懂,為什么偽隨機(jī)數(shù)種子一直都是1 杏节,偽隨機(jī)數(shù)不變唬渗。繼續(xù)分析 if 里面,就是每隔10個(gè)字符判斷是否相等奋渔。
這里將一個(gè)很長(zhǎng)的字符串復(fù)制給v2+96镊逝,看WP說v2就是上面的v3,這一點(diǎn)也有些疑惑,沒有看出來嫉鲸。也許是this指針的原因吧撑蒜。接下來這個(gè)長(zhǎng)字符串從第二個(gè)字符開始到330位,每隔10位取出一個(gè)字符。
腳本如下:
a = ";f1K3{c5:efl21t4;1t1zaxpim9}5+?gtux;=vc9v{v7+buhU{bT=-am2q}=fh[xk{y?xrqe{?}l5-sd2-Mo+:j{9=sY[dalvpx?z3{?no{[k5ll{zjsu5[kfla+r6Zg72o0skq6cGl5cw[=d?3v9q5-vkjSv{4sqtg=f0cz{+jurjfl[tb]lrfF1;2}udhb?0g8{om:T4dh;z:oz-Dn=m=ux;o[gs9{+zqx+sq-dsxctcvykUs2oddrt43pwv:f0;njkrb9los6g0{ih?rqantfx$sslqd:rvqixr;j{?o:sn+[i[yA11;gsmr8lm0?3};+iv+Tf:4Gtv2:-20upi0]7?77=;qzx{m-W;0vtueh]ko8d?=w:fbhd{E:;19?p=k:b+}doht6wpEq-z]2qbV1}dh416qw9:xm[;ed;:ecb-0:ni-s4u2kf6]2wn45amzjrun=ofkx-=hmgo-lz;j909=rmo7xcj4le0hxs[i]-vjl[?o12:sv4upio7ma1hRy7556+57krev:hLQ+1cx65z5v5];6n=[p83;n={zm{k2p"
for i in range(1,330,10):
print(a[i],end='')
0x05 tt3441810
這道題看不懂座菠,以為是手動(dòng)脫殼狸眼。搜了官方WP,也是一臉懵逼,這是雜項(xiàng)題嗎浴滴?
用IDA或者notepad++打開看到十六進(jìn)制數(shù)拓萌,在這里看到 ‘fl’ ,{ } 等字樣巡莹,然后把一些混淆字符去掉得到flag,還得提交括號(hào)里面的內(nèi)容司志。又看了里面自帶的WP,還是學(xué)到了xxd使用降宅。
flag{poppopret}
0x06 zorropub
先拖進(jìn)IDA,分析下程序流程骂远,題的意思很簡(jiǎn)單,主要是輸入飲料的數(shù)量和飲料ID經(jīng)過下面函數(shù)在計(jì)算MD5腰根,判斷其MD5是否相等激才。
v15 = __readfsqword(0x28u);
seed = 0;
puts("Welcome to Pub Zorro!!");
printf("Straight to the point. How many drinks you want?", a2);
__isoc99_scanf("%d", &v5);
if ( v5 <= 0 )
{
printf("You are too drunk!! Get Out!!", &v5);
exit(-1);
}
printf("OK. I need details of all the drinks. Give me %d drink ids:", (unsigned int)v5);
for ( i = 0; i < v5; ++i )
{
__isoc99_scanf("%d", &v6); //循環(huán)輸入飲料ID
if ( v6 <= 16 || v6 > 0xFFFF ) //飲料ID在 [16,65535]范圍內(nèi)。
{
puts("Invalid Drink Id.");
printf("Get Out!!", &v6);
exit(-1);
}
seed ^= v6; //隨機(jī)種子
}
i = seed;
v9 = 0;
while ( i )
{
++v9;
i &= i - 1;
}
if ( v9 != 10 )
{
puts("Looks like its a dangerous combination of drinks right there.");
puts("Get Out, you will get yourself killed");
exit(-1);
}
srand(seed);
MD5_Init((__int64)&v10);
for ( i = 0; i <= 29; ++i )
{
v9 = rand() % 1000;
sprintf(&s, "%d", v9);
v3 = strlen(&s);
MD5_Update((__int64)&v10, (__int64)&s, v3);
v12[i] = v9 ^ LOBYTE(dword_6020C0[i]); //LOBYTE()得到一個(gè)16bit數(shù)最低(最右邊)那個(gè)字節(jié)
}
v12[i] = 0;
MD5_Final(v11, &v10);
for ( i = 0; i <= 15; ++i )
sprintf(&s1[2 * i], "%02x", (unsigned __int8)v11[i]);
if ( strcmp(s1, "5eba99aff105c9ff6a1a913e343fec67") )
{
puts("Try different mix, This mix is too sloppy");
exit(-1);
}
return printf("\nYou choose right mix and here is your reward: The flag is nullcon{%s}\n", v12);
}
這里我們有兩種思路额嘿,1.爆破MD5瘸恼,2.逆向算法。
1.爆破MD5:
先根據(jù)飲料ID范圍在 [16,65535]册养,和下面check來從中篩選出符合ID的值东帅。
while ( i )
{
++v9;
i &= i - 1;
}
if ( v9 != 10 )
在使用subprocess庫(kù),(想使用pwntools的球拦,結(jié)果沒有搜到pwntools如何殺死進(jìn)程的函數(shù)靠闭,看了大佬文章才知道還有subprocess的),將滿足ID的數(shù)使用subprocess里的communicate()帶進(jìn)去試一試坎炼,看能否返回flag愧膀。
腳本如下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from subprocess import *
#這里是找出符合條件的數(shù)
a=[]
for i in range(16,65535):
v9 = 0
s=i
while i:
v9 +=1
i &= i-1
if v9 == 10:
a.append(s)
#循環(huán)輸入符合條件的數(shù),爆破flag
for i in a:
proc = Popen(['./zorro_bin'],stdin=PIPE,stdout=PIPE)
out = proc.communicate(('1\n%s\n' % i).encode('utf-8'))[0]#這里communicate返回的是一個(gè)元組谣光,但是元組只有一個(gè)元素檩淋,所以要加上偏移0。
if "nullcon".encode('utf-8') in out:
print(out)
print(i)
2.逆向算法
這是在官方WP復(fù)制的萄金。利用libc.so.6動(dòng)態(tài)鏈接庫(kù)得到生成的偽隨機(jī)數(shù)蟀悦。然后對(duì)著IDA的函數(shù)復(fù)現(xiàn)一遍。得到結(jié)果也是輸入59306的時(shí)候正確氧敢。
import ctypes
import os
import sys
import hashlib
libsystem = ctypes.CDLL('libc.so.6')
# Extracted by hand
encryption_key = [
0x03C8, 0x0032, 0x02CE, 0x0302, 0x007F,
0x01B8, 0x037E, 0x0188, 0x0349, 0x027F,
0x005E, 0x0234, 0x0354, 0x01A3, 0x0096,
0x0340, 0x0128, 0x02FC, 0x0300, 0x028E,
0x0126, 0x001B, 0x032A, 0x02F5, 0x015F,
0x0368, 0x01EB, 0x0079, 0x011D, 0x024E
]
need_md5 = '5eba99aff105c9ff6a1a913e343fec67'
mc = 0
while True:
for drink_id_count in range(17, 0xFFFE):
mc += 1
if mc % 1000 == 0:
sys.stdout.write('.')
sys.stdout.flush()
for counter in range(5):
input_1 = counter
seed = 0
drink_ids = []
for x in range(input_1):
drink_id = drink_id_count
drink_ids.append(drink_id)
if drink_id <= 16 or drink_id > 0xFFFF:
continue
else:
seed ^= drink_id
count = seed
some_num = 0
while count > 1:
some_num += 1
count &= (count - 1)
if some_num != 10:
continue
else:
pass
libsystem.srand(seed)
flag = ""
h = hashlib.md5()
for x in range(30):
ran = libsystem.rand()
rand_number = ran % 1000
h.update("%d" % rand_number)
flag += chr((rand_number ^ encryption_key[x])&0xFF)#LOBYTE()得到一個(gè)16bit數(shù)最低(最右邊)那個(gè)字節(jié)日戈,所以 & 0xff
if h.hexdigest() == need_md5:
print "\nHash -> %s" % h.hexdigest()
print "Found it! Drinks:%d, Drink IDs:%s" % (counter, drink_ids)
raw_input()
0x07 Reversing-x64Elf-100
IDA打開,主函數(shù)福稳。分析得到主要邏輯函數(shù)為sub_4006FD(),非常簡(jiǎn)單的邏輯涎拉,寫腳本得到flag。
signed __int64 __fastcall sub_4006FD(__int64 a1)
{
signed int i; // [rsp+14h] [rbp-24h]
const char *v3; // [rsp+18h] [rbp-20h]
const char *v4; // [rsp+20h] [rbp-18h]
const char *v5; // [rsp+28h] [rbp-10h]
v3 = "Dufhbmf";
v4 = "pG`imos";
v5 = "ewUglpt";
for ( i = 0; i <= 11; ++i )
{
if ( (&v3)[i % 3][2 * (i / 3)] - *(char *)(i + a1) != 1 )
return 1LL;
}
return 0LL;
}
腳本如下:
arr = [
['D','u','f','h','b','m','f'],
['p','G','`','i','m','o','s'],
['e','w','U','g','l','p','t']
]
for i in range(12):
key = arr[i%3][2*int(i/3)]
print(chr(ord(key)-1),end='')
0x08 gametime
這道題主要靠細(xì)心的圆,先運(yùn)行一下看看鼓拧,在拖進(jìn)IDA,分析下邏輯。這個(gè)游戲就是靠手快越妈,剛好出現(xiàn)規(guī)定字符時(shí)季俩,按一下對(duì)應(yīng)字符進(jìn)入下一關(guān),越來越快梅掠,這單身一輩子也沒有這手速啊酌住。。所以拖進(jìn)OD,找到些關(guān)鍵跳轉(zhuǎn)下斷點(diǎn)阎抒,耐心的調(diào)試耐心的調(diào)試耐心的調(diào)試耐心的調(diào)試酪我,就出來key了。
這里總結(jié)下OD的使用:
- 找到關(guān)鍵字符且叁,在其上方第一個(gè)跳轉(zhuǎn)一般為關(guān)鍵跳轉(zhuǎn)都哭,越過正確字符的跳轉(zhuǎn)為關(guān)鍵跳轉(zhuǎn)。
- 在關(guān)鍵跳轉(zhuǎn)處下斷點(diǎn)逞带,運(yùn)行到斷點(diǎn)時(shí)看將要跳轉(zhuǎn)到哪里去欺矫,在判斷是否跳轉(zhuǎn),比如有幾個(gè)跳轉(zhuǎn)都將向同一個(gè)地址跳轉(zhuǎn)展氓,那么這個(gè)跳轉(zhuǎn)就改成不跳轉(zhuǎn)穆趴,因?yàn)橛螒虺鲥e(cuò)有一點(diǎn)錯(cuò)誤就退出,所以這里一定是指向錯(cuò)誤的輸出函數(shù)遇汞,正確的只有一條路未妹,不可能有多個(gè)跳轉(zhuǎn)指向它。
- 遇到JMP跳轉(zhuǎn)一般不要改勺疼,因?yàn)檫@個(gè)是無條件跳轉(zhuǎn)教寂,無論你輸入的正確與否,它都將跳轉(zhuǎn)执庐。
- 出現(xiàn)需要循環(huán)運(yùn)行時(shí)酪耕,不用一個(gè)個(gè)的按F8,在跳轉(zhuǎn)的下面一行下斷點(diǎn)轨淌,然后F9直接運(yùn)行迂烁,將會(huì)斷在剛剛下斷點(diǎn)處。
- 邊調(diào)試邊看運(yùn)行結(jié)果递鹉,看是否正確輸出盟步。
0x09 easyre-153
首先UPX脫殼。IDA打開躏结,看到pipe()和fork()却盘。
pipe函數(shù)可用于創(chuàng)建一個(gè)管道,以實(shí)現(xiàn)進(jìn)程間的通信。
fork函數(shù)通過系統(tǒng)調(diào)用創(chuàng)建一個(gè)與原來進(jìn)程幾乎完全相同的進(jìn)程黄橘,其返回值是進(jìn)程號(hào)兆览。
v8 = __readgsdword(0x14u);
pipe(pipedes);
v5 = fork();
if ( !v5 )
{
puts("\nOMG!!!! I forgot kid's id");
write(pipedes[1], "69800876143568214356928753", 0x1Du);
puts("Ready to exit ");
exit(0);
}
read(pipedes[0], &buf, 0x1Du);
__isoc99_scanf("%d", &v6);
if ( v6 == v5 )
{
if ( (*(_DWORD *)((_BYTE *)lol + 3) & 0xFF) == 204 )
{
puts(":D");
exit(1);
}
printf("\nYou got the key\n ");
lol(&buf);
}
wait(0);
return 0;
}
但是這里就算輸入正確抬探,lol函數(shù)也會(huì)返回 'flag_is_not_here',所以我們需要使用IDA動(dòng)態(tài)調(diào)試
去修改匯編改變跳轉(zhuǎn)得到flag,之前的進(jìn)程號(hào)也可以隨便輸然后改變后面的關(guān)鍵跳轉(zhuǎn)即可帆赢。
得到flag,要加上RCTF小压。太坑了,我以為我的不是flag,看了WP才知道椰于。