利用Unicorn和Idaemu輔助解決Geekpwn SecretCode


    在前面的些文章里,我提到了怎么交叉編譯Unicorn-engine,以及在windows上使用Unicorn python bindings進行分析程序。這一次我介紹下如何使用Unicorn-engine和Idaemu來解決一個ollvm混淆的android逆向,也就是Geekpwn SecretCode 150。

    拿到題目,java的代碼很簡單,用戶輸入一個數字,程序調用native方法Java_net_bluelotus_tomorrow_easyandroid_MainActivity_stringFromJNI來進行驗證。使用IDA打開so,發現代碼經過了o-llvm混淆,控制流非常復雜。

    這個時候如果直接逆的話,難度很大,所以我在這里借助了idaemu來對某些函數模擬執行,然后通過函數的輸入和輸出來猜測函數的功能。首先要知道的是,一個double類型的數據是要兩個寄存器存儲的,使用的是IEEE 754標准將數字存放到寄存器當中。直接對Java_net_bluelotus_tomorrow_easyandroid_MainActivity_stringFromJNI進行f5,可以看到里面除了復雜的控制流外,調用了幾個別的函數,比如aeabi_dadd,fixdfsi,瀏覽函數列表的時候還發現了f0, f1, f2, f3函數,只從這幾個函數名上就知道這是用戶寫的函數,並且在Java_net_bluelotus_tomorrow_easyandroid_MainActivity_stringFromJNI也發現調用了f1和f2,f2又調用了四次f0。

    根據名字我大概覺得aeabi_dadd是浮點數的加法,我先google了aeabi_dadd 和fixdfsi,知道了fixdfsi是將浮點數轉換為整數。我使用了idaemu模擬了幾次aeabi_dadd,證實了自己的看法。用到的部分腳本如下:

a = Emu(UC_ARCH_ARM, UC_MODE_THUMB)
#0xBFF0000000000000
#add -1
def aeabi_dadd(f):
    for i in f:
        p = struct.pack('>d',i)
        arg0 = int(binascii.hexlify(p[4:]),16)
        arg1 = int(binascii.hexlify(p[:4]),16)
        arg = []
        arg.append(arg0)
        arg.append(arg1)
        arg.append(0)
        arg.append(0xBFF00000)
        r0 = a.eFunc(0x4188, None, arg)
        r1 = a.curUC.reg_read(UC_ARM_REG_R1)
        value = (r1<<32)|r0
        print 'handling: '+ str(i))
        result = hex(value)[2:-1].rjust(16,'0')
        print(result)
        result = binascii.unhexlify(result)
        print('final result: ' + str(struct.unpack('>d',result)[0]))
aeabi_dadd([1,2,4,100])
View Code

    通過這段腳本的運行,我知道了j_j___aeabi_dadd(*(__int64 *)&in, 0xBFF0000000000000LL);就是將輸入的浮點數進行減一操作。通過這個方法,我也知道了j_j___aeabi_dadd(v3, 0xC1D0000000000000LL);的作用,就是將輸入的浮點數進行減去1073741824操作。

通過這幾個函數的分析,我知道了用戶輸入的數據首先減去1然后減去四個1073741824,最后將其從浮點數轉換成整數。轉換成整數后,我看到這個整數傳到了f2里面,因此,我現在要用Idaemu來模擬一下f2,用到的代碼如下:

def f2_1(i):
    arg = []
    arg.append(i)
    r0 = a.eFunc(0x1984, None, arg)
    return r0

def walkf2(start, end):
    i=start
    while i<end:
        print(str(i)+ ' '+ str(f2_1(i)))
        i+=1
walkf2(0,200)
View Code

    通過這段腳本的運行,我發現在0到200之間,只有5,11,101,191的返回結果是1,而其他的返回結果都是0,我就在google上搜索序列5,11,101,191發現這是四胞胎素數的序列,所以我就猜測f2則是判斷數字是不是四胞胎素數。進入f2后,我發現了4處對f0的調用,因此我又對f0進行了模擬,發現f0則是判斷輸入的數據是不是素數,對f0的四次調用恰好印證了f2則是用來判斷是不是四胞胎素數。用到的腳本:

def f0_1(i):
    arg = []
    arg.append(i)
    r0 = a.eFunc(0x1430, None, arg)
    return r0
def walkf0(start, end):
    i=start
    while i<end:
        print(str(i)+ ' '+ str(f0_1(i)))
        i+=1
walkf0(0,30)
View Code

    我也同樣的嘗試去模擬f1,但是並沒有發現規律。接下來,我開始動態地調試這個程序,發現雖說表面上看控制流很復雜,但是有相當大一部分是固定的跳轉,而且Ida調試Android經常會遇到SIGILL,很不方便,我就想着如果把程序運行過程中的trace給log下來,然后進行靜態的分析。用到的一些腳本如下:

a = Emu(UC_ARCH_ARM, UC_MODE_THUMB)
a.setTrace(TRACE_CODE)
a.setTrace(TRACE_FileLOG)
def StringFromJni(i):
    print 'handling: '+str(i)
    i += (1+1073741824*4)
    p = struct.pack('>d',i)
    print binascii.hexlify(p)
    arg0 = int(binascii.hexlify(p[4:]),16)
    arg1 = int(binascii.hexlify(p[:4]),16)
    print 'arg0: '+hex(arg0)
    print 'arg1: '+hex(arg1)

    arg = []
    arg.append(0)
    arg.append(0)
    arg.append(arg0)
    arg.append(arg1)

    r0 = a.eFunc(0x1C44, 0x1F7A, arg)
    print('return: ' + hex(r0))
#StringFromJni(500227331)
StringFromJni(5)
View Code

     腳本完成后,會生成tracelog文件,里面存放了所有的執行過程的指令。下一步我們就可以直接開始進行分析了,對着tracelog和IDA進行分析。因為很大一部分分支比較的代碼是無用的,而且有一部分是已知函數的執行序列也是無用的,所以在看tracelog的時候把這些刪除的話大概只剩下100多行,分析着還是蠻快的。無用的指令序列一般是下面這種形式的:

1760 LDR     R0, =0x61CB0C05            R0: 78d3a2ea;
1762 CMP     R1, R0         R1: 275f3da1; R0: 61cb0c05;
1764 BGT     loc_17D2
View Code

    我初步在代碼里看到的是:假設輸入的是x,記subdone = x-1-4*1073741824,在代碼分支里判斷的有subdone是不是一個四胞胎素數,根據判斷的結果將一個局部變量A置為0或1,以及subdone-1有沒有大於500000000,根據判斷的結果將一個局部變量B置為0xB29C1529(大於500000000)或者0x91CD6A9B(小於500000000),然后讓i從1開始一次求i的平方知道i的平方大於subdone-1,當subdone-1存在平方根的時候,會用到局部變量B。我嘗試通過hook的方式,在當要使用變量B的時候將變量B修改為0xB29C1529,則這個時候通過驗證。

    通過上面的推理與理解,我們可以得出:subdone是一個四胞胎素數,subdone要大於500000000,subdone-1要有平方根。根據此,我寫了一個C語言的程序來爆破這個數字,順利得到該數字,即512116901,所以輸入為:512116901+1+1073741824*4=4807084198。Flag就是flag{hohay3f4nmop}。

    其實我並沒有很嚴格地逆清楚這個算法,但是通過Idaemu和Unicorn的幫助,我可以通過模擬運行產生tracelog的方式來進行分析,里面有些推測,但是算是合理的推測。

    c語言的源代碼:http://files.cnblogs.com/files/wangaohui/secretcode.zip

    idaemu:https://github.com/36hours/idaemu 我在其基礎上進行了些修改,使其能將指令序列記錄到文件中,並且以一種友好的方式記錄寄存器的值


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM