生活随笔
收集整理的這篇文章主要介紹了
REVERSE-COMPETITION-HGAME2022-Week4
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
REVERSE-COMPETITION-HGAME2022-Week4
( WOW )
32位exe,ida打開
觀察偽代碼邏輯,上面的紅框中,輸入經過加密,密文放入Buf2中,然后Buf2和已知的密文res比較
比較完,打印"win"或者"error"
發現下面還有一部分對Buf2處理的代碼邏輯,而且幾乎和上面對輸入加密的結構是一樣的
于是猜測下面的紅框中就是解密邏輯
起調試,隨便構造一個長度為32的輸入
在調用memcmp前下斷,將內存中Buf2的數據patch成已知的密文res
進行下面對Buf2的解密,解密后的input即為flag
server
64位exe,用ida7.7打開
(52pojie上有最新的全插件綠色版ida7.7可供下載)
從main函數中知道監聽本地的9090端口
回調函數為main_HttpHandleFunc,調試發現,輸入經過main_encrypt處理后,與已知的res比較
進入main_encrypt函數,偽代碼看不出什么,玄機在匯編指令
可以發現main_encrypt的前半部分實際上是個RSA加密,并且已知p,q,e
輸入經rsa加密后,將十進制密文的每一個數字轉成字符存入rsa_cipher中
然后進行后半部分的兩輪異或運算,異或后的結果與已知的res比較
這里舉一個例子來理解后半部分的兩輪異或運算,以便能夠更好地逆向
import gmpy2
num1
=92582184765240663364795767694262273105045150785272129481762171937885924776597
num2
=107310528658039985708896636559112400334262005367649176746429531274300859498993
num1_mul_num2
=num1
*num2
num3
=950501
my_in_s
="hgame{abcdefghijklmnopqrstuvwxy}"
my_in
=0x6867616d657b6162636465666768696a6b6c6d6e6f707172737475767778797d
my_in_cipher
=gmpy2
.powmod
(my_in
,num3
,num1_mul_num2
)
my_in_cipher_s
=str(my_in_cipher
)
print("RSA加密后的十進制密文:")
print(my_in_cipher_s
)
print("RSA加密后的十進制密文長度:")
print(len(my_in_cipher_s
))
res
=[]
for i
in range(len(my_in_cipher_s
)-1):res
.append
(ord(my_in_cipher_s
[i
]))
print("十進制密文的每一個數字轉成字符:")
print(res
)
v22
=102
index
=0
while index
<153:tmp
=res
[index
]res
[index
]^=v22v22
=tmpindex
+=1
print("第一輪異或運算結果:")
print(res
)
index
=0
while index
<153:tmp
=res
[index
]res
[index
]^=v22v22
=tmpindex
+=1
print("第二輪異或運算結果:")
print(res
)
我們可以發現
round_1[0]=102 xor cipher[0],round_1[1]=cipher[0] xor cipher[1],依次類推(這里的102是已知的
round_2[0]=52 xor round_1[0],round_2[1]=round_1[0] xor round_1[1],依次類推
這里的52實際上等于cipher[len(cipher)-1],也就是cipher的最后一個值
但是由于cipher是RSA加密后的密文,所以其實cipher[len(cipher)-1]是未知的
想要逆向兩輪異或運算從而得到正確的RSA密文
需要爆破cipher[len(cipher)-1],范圍是十進制數字字符
import gmpy2
from Crypto
.Util
.number
import long_to_bytes
res
=[0x63,0x55,0x4,0x3,0x5,0x5,0x5,0x3,0x7,0x7,0x2,0x8,0x8,0xb,0x1,0x2,0xa,0x4,0x2,0xd,0x8,0x9,0xc,0x9,0x4,0xd,0x8,0x0,0xe,0x0,0xf,0xd,0xe,0xa,0x2,0x2,0x1,0x7,0x3,0x5,0x6,0x4,0x6,0x7,0x6,0x2,0x2,0x5,0x3,0x3,0x9,0x6,0x0,0xb,0xd,0xb,0x0,0x2,0x3,0x8,0x3,0xb,0x7,0x1,0xb,0x5,0xe,0x5,0x0,0xa,0xe,0xf,0xd,0x7,0xd,0x7,0xe,0x1,0xf,0x1,0xb,0x5,0x6,0x2,0xc,0x6,0xa,0x4,0x1,0x7,0x4,0x2,0x6,0x3,0x6,0xc,0x5,0xc,0x3,0xc,0x6,0x0,0x4,0xf,0x2,0xe,0x7,0x0,0xe,0xe,0xc,0x4,0x3,0x4,0x2,0x0,0x0,0x2,0x6,0x2,0x3,0x6,0x4,0x4,0x4,0x7,0x1,0x2,0x3,0x9,0x2,0xc,0x8,0x1,0xc,0x3,0xc,0x2,0x0,0x3,0xe,0x3,0xe,0xc,0x9,0x1,0x7,0xf,0x5,0x7,0x2,0x2,0x4]
p
=92582184765240663364795767694262273105045150785272129481762171937885924776597
q
=107310528658039985708896636559112400334262005367649176746429531274300859498993
n
=p
*q
e
=950501
phin
=(p
-1)*(q
-1)
d
=gmpy2
.invert
(e
,phin
)
for i
in range(0x30,0x3a):tmp1
=[res
[0]^i
] for j
in range(1,len(res
)):tmp1
.append
(res
[j
]^tmp1
[j
-1])tmp2
=[tmp1
[0]^102]for j
in range(1,len(tmp1
)):tmp2
.append
(tmp1
[j
]^tmp2
[j
-1])wrong
=0for m
in tmp2
: if m
<0x30 or m
>0x39:wrong
=1breakif wrong
:continueif i
==tmp2
[len(tmp2
)-1]:cipher
=""for k
in tmp2
:cipher
+=chr(k
)cipher_num
=int(cipher
)m
=gmpy2
.powmod
(cipher_num
,d
,n
)print(long_to_bytes
(m
))
ezvm
vm,調試確定輸入的長度應為32
對輸入的處理邏輯為:輸入的每個字符,左移1位,和一個值異或,然后比較
構造一個長度為32的輸入,得到經程序處理后的結果
逆向對輸入的處理邏輯,得到輸入的每個字符左移1位后,要去異或的值的集合
最后由真正的密文爆破出flag
s
="hgame{abcdefghijklmnopqrstuvwxy}"print(hex((ord("h")<<1)^0x5e))
print(hex((ord("g")<<1)^0x46))
my_res
=[0x8e,0x88,0xa3,0x99,0xc4,0xa5,0x8b,0xdb,0x97,0x96,0xfc,0xfb,0xe7,0x91,0xb1,0xef,0xb2,0xe3,0xcf,0xc4,0x85,0xde,0xc0,0xb4,0xa0,0xb6,0xdf,0xa2,0xad,0xd3,0x92,0xc1]
xor_box
=[]
for i
in range(len(s
)):tmp
=(ord(s
[i
])<<1)&0xffxor_box
.append
(tmp
^my_res
[i
])
res
=[0x8e,0x88,0xa3,0x99,0xc4,0xa5,0xc3,0xdd,0x19,0xec,0x6c,0x9b,0xf3,0x1b,0x8b,0x5b,0x3e,0x9b,0xf1,0x86,0xf3,0xf4,0xa4,0xf8,0xf8,0x98,0xab,0x86,0x89,0x61,0x22,0xc1]
flag
=""
for i
in range(len(res
)):for j
in range(32,128):if (j
<<1)^xor_box
[i
]==res
[i
]:flag
+=chr(j
)
print(flag
)
hardasm
近7000行的匯編指令,應該是有什么地方設計得很巧妙
起調試,構造一個長度為32的輸入"hgame{abcdefghijklmnopqrstuvwxy}"
在如下位置下斷點
程序斷下后,觀察此時[rsp+70h+var_50]上的數據
可以發現,前6個數據均為非0的0xFF,而后面的數據均為0
這是因為我們構造的輸入的前6個字符"hgame{“是正確的,而后面湊長度的字符是錯誤的
于是我們可以知道
可以通過[rsp+70h+var_50]上連續的0xFF的個數n,確定我們的輸入前n個字符是正確的
那么怎么知道[rsp+70h+var_50]上連續的0xFF的個數n呢?我們需要能夠打印的函數
程序最后會打印"success"或者"error”,我們可以利用這個打印函數
從框住的匯編指令處可以觀察到,我們的目標數據地址是借rcx寄存器傳入打印函數的
于是我們patch程序,將[rsp+70h+var_50]借rcx傳入打印函數,如下
(這里不知道怎么能直接實現lea rcx,[rsp+70h+var_50],如果有師傅知道,請在評論區留言,謝謝)
這樣程序在最后就不會打印"success"或者"error",而會打印[rsp+70h+var_50]上的數據
將patch應用到程序后,再起調試,同樣的輸入,在調用打印函數printf后下斷,觀察打印出的內容
可以看到雖然沒有打印出具體的字符(因為0xFF不可見),但是長度是正確的,為6
或者可以用python來確認
import subprocess
flag
="hgame{abcdefghijklmnopqrstuvwxy}"
p
= subprocess
.Popen
(["D:\\ctfdownloadfiles\\hardasm.exe"], stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
p
.stdin
.write
(flag
.encode
())
p
.stdin
.close
()
out
= p
.stdout
.read
()
print(len(out
))
for c
in out
:print(hex(c
))
最后就是依次爆破每個位置上的字符
根據程序打印出的連續的0xFF的個數來確定某位置上的字符是否正確
import subprocess
real_flag
="hgame{"
cur_index
=6
while cur_index
<32:for i
in range(32,128):real_flag_arr
= [0] * 32for j
in range(len(real_flag
)):real_flag_arr
[j
]=ord(real_flag
[j
])real_flag_arr
[len(real_flag_arr
)-1]=ord("}")for j
in range(len(real_flag_arr
)-2,cur_index
,-1):real_flag_arr
[j
]=32real_flag_arr
[cur_index
]=ireal_flag_arr_s
="".join
(chr(k
) for k
in real_flag_arr
)p
= subprocess
.Popen
(["D:\\ctfdownloadfiles\\hardasm.exe"], stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)p
.stdin
.write
(real_flag_arr_s
.encode
())p
.stdin
.close
()out
= p
.stdout
.read
()if len(out
)>cur_index
:real_flag
+=chr(i
)cur_index
+=1print(real_flag
)break
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的REVERSE-COMPETITION-HGAME2022-Week4的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。