記錄《加密與解密》中一道很腦洞的RE題
首先,這道題在《加密與解密》中有完整的分析,那我為什麼要再寫一遍? 因為這題的腦洞你不自己做過是感受不到的。
文章位於《加密與解密》的5.5 KeyFile保護
介面:
首先,上面的那個編輯框不是給你輸入用的,所以考慮是從登錄檔,ini或是其他型別檔案讀入。
觀察程式的api呼叫,
發現有ReadFile,雙擊過去下斷,上面發現CreateFileA,發現是從一個叫做KwazyWeb.bit
的檔案讀入。
這裡我沒有書上那麼好的水平,我用ida分析。(以下程式碼已經重新命名過)
- 在call 0x4012CF中有如下一段:
從而得知,註冊檔案由三部分組成,一位元組的len_name,len_name位元組的name,18位元組的key。
- 以下為calc_name()中的程式碼:
得知,name_calc為name每一位所對應的ascii數相加以後與0xFF做&(與)運算。
- 以下為sub_4010C9check函式的程式碼:
這裡的string1和string2都是一串很長的字串,這裡我把他dump出來:
****************C*......*...****.*.****...*....*.*..**********.*..*....*...*...**.****.*.*...****.*....*.*******..*.***..*.....*.*..***.**.***.*... ****....*X..*****************
off_403184是一個
後面call sub_40101D,我把程式碼也粘上:
程式碼很好懂,用name_calc對key每一位異或加密。
來看下面的迴圈,首先i = 0
,後面有++i != 18
,大迴圈一共18次;然後對i累加的條件是j = 0
,而j = 8
,j -= 2
,所以小迴圈是4次,一共迴圈18*4 = 72次。
(key[i] >> j) & 3
是傳入key的每2bits。傳入的資料只有0,1,2,3四種可能。
- 再看show_msg()函式:
根據傳入的choice,pt_long_str的值會±1,±16。如果指標指向的字元為’*’,直接失敗;如果指標指向的字元為’X’,則成功。後面兩句賦值無關緊要。
程式碼逆向分析完了,
首先我們根據show_msg函式的判斷逆出異或後的key_enc,然後根據自己輸入的name算出name_calc,然後得到key = name_calc ^ key_enc。然後按照格式建立註冊資訊檔案。
難就難在如何逆出key_enc。一開始我的思路斷了。後來在思考為什麼是±1和±16的時候恍然大悟,我們只要將之前那一長串字串按照16位一組然後回車換行,我們就會得到:
****************
C*······*···****
·*·****···*····*
·*··**********·*
··*····*···*···*
*·****·*·*···***
*·*····*·*******
··*·***··*·····*
·*··***·**·***·*
···****····*X··*
****************
這就是一個迷宮遊戲,從c走到x。1就是向右走,-1就是向左走,16就是向下,-16就是向上。
再根據判斷條件編碼一下,得到順序:
move=[2,2,2,1,2,2,2,3,2,2,1,1,0,1,0,0,1,1,1,0,0,3,3,3,0,3,0,0,1,1,1,1,1,2,1,1,0,1,1,2,1,1,1,2,2,3,3,2,3,3,0,3,3,2,2,2,3,2,2,1,1,1,0,0,1,1,1,1,2,2,3,3]
剩下的就輕鬆多了,直接上python指令碼:
def calc_name(name_str):
tmp = 0
for ch in name_str:
tmp+=ord(ch)
return tmp&255
def generate_key_enc():
key_e_list=[]
move=[2,2,2,1,2,2,2,3,2,2,1,1,0,1,0,0,1,1,1,0,0,3,3,3,0,3,0,0,1,1,1,1,1,2,1,1,0,1,1,2,1,1,1,2,2,3,3,2,3,3,0,3,3,2,2,2,3,2,2,1,1,1,0,0,1,1,1,1,2,2,3,3]
for i in range(0,len(move),4):
key_e_list.append((move[i]<<6 | move[i+1]<<4 | move[i+2]<<2 | move[i+3])&255)
return key_e_list
def generate_key(key_enc,name_calc):
key=[]
for i in range(len(key_enc)):
key.append(key_enc[i] ^ name_calc)
return key
key_len = 18
name='veritas501'
len_name = len(name)
name_calc = calc_name(name)
key_enc = generate_key_enc()
key = generate_key(key_enc,name_calc)
key_asc=''
for i in range(len(key)):
key_asc+=chr(key[i])
content = chr(len_name)+name+key_asc
fp = open('KwazyWeb.bit','wb')
fp.write(content)
fp.close()
破解成功!
這次逆向最開腦洞的地方就是如何把資料隱藏起來不被輕鬆dump,之前我做到題都是用演算法來隱藏,而這題的思路非常創新,值得我學習。