此文以記錄分析路程
實在是懶。比賽的時候分析到一半就不想分析了。耐心不夠啊!感覺質量還是很不錯!
Mainfest文件中定義在Android6.0SDK,在Android4.4.4中運行程序閃退。折騰了幾個小時下AVD在Android6.0中跑還是閃退,只能硬著頭皮看邏輯了。
Java層只有loadlibrary("cook"),分析so中JNI_ONLOAD,發現字符串全部被處理,使用的時候調用sub_1034 解密,直接動態調試,順便看下為什么會閃退。(自己編寫一個Loader加載so調試會比較方便)。發現mkdir會失敗。patch下(其實patch目的還是看后面字符串解密,寫IDA腳本發現沒法整理好棧中數據順序,還是功力不夠啊!)
由于看到ARM下堆棧傳遞的參數是亂序的直接試用x86的進行分析,腳本如下
def dec_string(enc_data): ret_len = len(enc_data) ret = "" chr1 = None chr2 = None """ *result = ~(( ~ cur_int&0xff | ((unsigned __int16)(cur_int & 0xFF00) >> 8)) & (~((unsigned __int16)(cur_int & 0xFF00) >> 8) | cur_int)); result[1] = HIBYTE(cur_int) ^ ((cur_int & 0xFF0000) >> 16); """ for i in range(ret_len): cur_int = enc_data chr1 = (~(((~cur_int & 0xff) | ((cur_int & 0xff00) >> 8)) & ((~(cur_int & 0xff00) >> 8) | cur_int))) & 0xff chr2 = ((cur_int & 0xff000000) >> 24) ^ ((cur_int & 0xff0000) >> 16) ret += chr(chr1) ret += chr(chr2) return ret pDecode = 0x680 for x in XrefsTo(pDecode,flags = 0): #MakeCode((x.frm & 0xFFFFFFFE)); addr = x.frm print hex(addr) while True: addr = PrevHead(addr) if "esp" in GetOpnd(addr,0): if "x" not in GetOpnd(addr,1): index = GetOperandValue(addr,1) break dict = [] count = 0 while count < index: addr = PrevHead(addr) if "esp" in GetOpnd(addr,0): dict.append(GetOperandValue(addr,1)) count += 1 MakeComm(x.frm,dec_string(dict))
解密出來可以看到大概的邏輯了

發現寫入 /data/data/com.google.ctf.food/files/d.dex
導出后JEB分析,發現com.google.ctf.food.F.cc 類被抽空了。那么后面肯定還有讀寫內存的操作,繼續分析下去,發現sub_1098中就有這種操作。

從/proc/self/maps中讀取d.dex基址,匹配''dex\n0',在偏移:0x720處寫入數據,數據被異或解密,如圖
修改dex文件,填充修改的部分
a = [0x49, 0x5E, 0x52, 0x5A, 0x79, 0x1B, 0x7B, 0x5A, 0x7C, 0x5B, 0x66, 0x5A, 0x5A, 0x5A, 0x48, 0x5A, 0x6F, 0x1A, 0x55, 0x5A, 0x12, 0x58, 0x5B, 0x5A, 0xE, 9, 0x5F, 0x5A, 0x12, 0x59, 0x59, 0x5A, 0xED, 0x68, 0xD7, 0x78, 0x15, 0x58, 0x5B, 0x5A, 0x82, 0x5A, 0x5A, 0x5B, 0x72, 0xA8, 0x78, 0x5A, 0x45, 0x5A, 0x2A, 0x7A, 0x7E, 0x5A, 0x4A, 0x5A, 0x40, 0x5B, 0x5A, 0x5A, 0x34, 0x7A, 0x7F, 0x5A, 0x4A, 0x5A, 0x50, 0x5A, 0x63, 0x5A, 0x47, 0x5A, 0xE, 0xA, 0x58, 0x5A, 0x34, 0x4A, 0x5B, 0x5A, 0x5A, 0x5A, 0x56, 0x5A, 0x78, 0x5B, 0x45, 0x5A, 0x38, 0x58, 0x5E, 0x5A, 0xE, 9, 0x5F, 0x5A, 0x2B, 0x7A, 0x78, 0x5A, 0x68, 0x5A, 0x56, 0x58, 0x2A, 0x7A, 0x7E, 0x5A, 0x7B, 0x5A, 0x48, 0x48, 0x2B, 0x6A, 0x4F, 0x5A, 0x4A, 0x58, 0x56, 0x5A, 0x34, 0x4A, 0x4C, 0x5A, 0x5A, 0x5A, 0x54, 0x5A, 0x5A, 0x59, 0x5B, 0x5A, 0x52, 0x5A, 0x5A, 0x5A, 0x40, 0x41, 0x44, 0x5E, 0x4F, 0x58, 0x48, 0x5D] b = [] for i in a: b.append(i^0x5A) f = open("d.dex",'rb') file = f.read() buf = list(file) for i in range(0x90): buf[0x720+i] = chr(b) f.close() f = open("f.dex",'wb') f.write("".join(buf)) f.close()
修復好了之后,分析dex,這里直接貼代碼,java邏輯層就很清楚了
import java.util.Arrays; public class test { public static void main(String[] argv){ byte[] v1 = new byte[]{26, 27, 30, 4, 21, 2, 18, 7}; byte[] v2 = new byte[]{0x13, 0x11, 0x13, 3, 4, 3, 1, 5}; for(int i=0;i<v1.length;i++){ v1 ^= v2; } byte[] flag = new byte[]{-19, 116, 58, 108, -1, 33, 9, 61, -61, -37, 108, -123, 3, 35, 97, -10, -15, 15, -85, -66, -31, -65, 17, 79, 31, 25, -39, 95, 93, 1, -110, -103, -118, -38, -57, -58, -51, -79}; System.out.println(new String(a(flag,v1))); } public static byte[] a(byte[] arg8, byte[] arg9) { int v7 = 256; byte[] v3 = new byte[v7]; byte[] v4 = new byte[v7]; int v0 = 0; int v1; for(v1 = 0; v1 != v7; ++v1) { v3[v1] = ((byte)v1); v4[v1] = arg9[v1 % arg9.length]; } int v2 = v1 ^ v1; v1 = 0; while(v2 != v7) { v1 = v1 + v3[v2] + v4[v2] & 255; v3[v1] = ((byte)(v3[v1] ^ v3[v2])); v3[v2] = ((byte)(v3[v2] ^ v3[v1])); v3[v1] = ((byte)(v3[v1] ^ v3[v2])); ++v2; } v4 = new byte[arg8.length]; v2 ^= v2; v1 ^= v1; while(v0 != arg8.length) { v2 = v2 + 1 & 255; v1 = v1 + v3[v2] & 255; v3[v1] = ((byte)(v3[v1] ^ v3[v2])); v3[v2] = ((byte)(v3[v2] ^ v3[v1])); v3[v1] = ((byte)(v3[v1] ^ v3[v2])); v4[v0] = ((byte)(arg8[v0] ^ v3[v3[v2] + v3[v1] & 255])); ++v0; } return v4; } }```
|