<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    bang加固簡單分析

    VSole2022-07-31 16:59:14

    一、dex

    dex加固,可以使用frida-dexdump可以直接dump下來。

    可以看到加載了SecShell進行脫殼調用,這個libSecShell.so是32位的。

    二、libSecShell.so

    export列表中看到了JNI_Onload,但是是加密的,分析不出來,修改代碼的話一定會調用mprotect,在mprotect處交叉引用,找不到調用,于是猜測可能是svc調用,用腳本跑了一下,發現了mprotect,腳本是之前論壇上看到的。

    system call : 7d 70addr : c0783Func Name : __NR_mprotect c0 70 a0 e3 00 00 00 ef
    

    在這里交叉引用發現都在sub_C0C30里調用。

    用frida去hook這個函數。

    var mprotect_cnt = 0//frida -U --no-pause -f com.testlinker.ty -l hook.jsfunction sleep(delay) {    var start = (new Date()).getTime();    while ((new Date()).getTime() - start < delay) {      continue;    }}  function hook_svc_mprotect() {    let base_svc_mprotect = Module.findBaseAddress("libSecShell.so");    if (base_svc_mprotect != null) {        console.log("base_svc_mprotect : " + base_svc_mprotect)    }else{        return ;    }    let svc_mprotect = base_svc_mprotect.add(0xC0778);//32位    Interceptor.attach(svc_mprotect, {        onEnter: function(args) {            console.log("==========================================")                        console.log("svc_mprotect: start = " + args[0] + " , len = " + args[1] + " , ATTRIBUTES = " + args[2])            mprotect_cnt += 1            console.log(hexdump(base_svc_mprotect.add(0x281B4)))        },        onLeave: function(){            console.log("svc_mprotect leave")            console.log("==========================================")        }    })}function dis(address, number) {    for (var i = 0; i < number; i++) {        var ins = Instruction.parse(address);        console.log("address:" + address + "--dis:" + ins.toString());        address = ins.next;    }}//libc->strstr()  從linker里面找到call_function的地址function hook() {//call_function("DT_INIT", init_func_, get_realpath());    var linkermodule    if (Process.pointerSize == 4) {        linkermodule = Process.findModuleByName("linker");    }else if (Process.pointerSize == 8) {        linkermodule = Process.findModuleByName("linker64");    }    // var linkermodule = Process.getModuleByName("linker");    var call_function_addr = null;    var symbols = linkermodule.enumerateSymbols();    for (var i = 0; i < symbols.length; i++) {        var symbol = symbols[i];        //LogPrint(linkername + "->" + symbol.name + "---" + symbol.address);        if (symbol.name.indexOf("__dl__ZL13call_functionPKcPFviPPcS2_ES0_") != -1) {            call_function_addr = symbol.address;            //LogPrint("linker->" + symbol.name + "---" + symbol.address)        }    }    Interceptor.attach(call_function_addr, {        onEnter: function (args) {            var type = ptr(args[0]).readUtf8String();            var address = args[1];            var sopath = ptr(args[2]).readUtf8String();            console.log("loadso:" + sopath + "--addr:" + address + "--type:" + type);            if (sopath.indexOf("libSecShell.so") != -1) {                var libnativemodule = Process.getModuleByName("libSecShell.so");//call_function正在加載目標so,這時就攔截下來                var base = libnativemodule.base;                hook_svc_mprotect()            }        }    })}function main() {    hook();}setImmediate(main)
    

    可以發現經過mprotect一次后,對應地址的值發生了變化。

    [Pixel 3::com.example.cryptotest ]-> base_svc_mprotect : 0xcfaea000==========================================svc_mprotect: start = 0xcfaea000 , len = 0xa1000 , ATTRIBUTES = 0x7           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEFcfb121b4  9b 66 a6 75 82 ab ba fb 1a 80 e6 75 d7 0e 7f 1b  .f.u.......u....svc_mprotect leave====================================================================================svc_mprotect: start = 0xcfb8b000 , len = 0x1f000 , ATTRIBUTES = 0x3           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEFcfb121b4  2d e9 f0 4f ad f6 ac 4d df f8 44 4e df f8 44 3e  -..O...M..DN..D>svc_mprotect leave====================================================================================svc_mprotect: start = 0xcfaea000 , len = 0xa1000 , ATTRIBUTES = 0x7           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEFcfb121b4  2d e9 f0 4f ad f6 ac 4d df f8 44 4e df f8 44 3e  -..O...M..DN..D>svc_mprotect leave==========================================
    

    用memdumper64(github上有,速度挺快) dump出so,用Sofixer修復so文件。

    打開跳轉到JNI_Onload(0x1E5DC),發現ida沒有自動創建函數,按p會報錯The function has undefined instruction/data at the specified address。

    用idapython強制創建函數。

    ida_funcs.add_func(0x281B4,0x2A5CC)
    

    隨便打開一個函數,發現是這樣的:

    ?

    .data:00085E24 DD F9 97 E4 off_85E24 DCD 0xE497F9DD ; DATA XREF: sub_DF08+8↑r
    

    cat /proc/18395/maps | grep e49看一下這個地址.

    發現是libc.so,把這個libc.so拖出來放到ida分析。

    計算一下0xE497F9DD-0xe494b000 = 0x349dd

    看一下libc.so,所以這個函數就是strcpy。

    感覺可以寫一個idapython腳本去修復一下。

    然后就寫了一下,先從libc.so中提取函數地址和函數名。

    from idautils import *from idaapi import *from idc import *f = open("./func.txt",'w')for func_addr in Functions(0,0x5B18BC):    func_name = get_func_name(func_addr)    print(func_addr , func_name)    f.write(str(func_addr) + "," + func_name + "")    # f.writelines()f.close()
    

    效果:

    然后從.data段中找到相應地址,相減得到libc.so中地址的偏移,然后對應起來,去修改函數名。

    from idautils import *from idaapi import *from idc import *f = open(r"CryptoTest_32\CryptoTest\lib\func.txt",'r')func_info = {}while True:    info = f.readline().strip('')    if not info:        break    addr, func_name = info.split(',')    # print(addr + func_name)    func_info[int(addr,10)] = func_name# print(func_info)f.close()textStart = 0xA2984textEnd = 0xC2000# textStart = 0xA2DE0# textEnd = 0xA2E04libc_dump_base = 0xe494b000for i in range(textStart,textEnd,4):    dword_ = get_dword(i)    if dword_ > libc_dump_base:        libc_func = dword_ - libc_dump_base        # print(dword_,libc_func)        func_name = func_info.get(libc_func)        if not func_name:            func_name = func_info.get(libc_func-1)  #thumb        if not func_name:            continue        raw_name_off = get_name(i)        patch_name_off = func_name + "_ptr_" + raw_name_off        set_name(i,patch_name_off)        xrefaddrs = XrefsTo(i, flags=0)        for xrefaddr in xrefaddrs:            raw_name = get_func_name(xrefaddr.frm)          #拿到函數原名稱            patch_fun_addr = get_name_ea_simple(raw_name)   #拿到函數地址            # print(get_func_name(xrefaddr.frm))            if raw_name and patch_fun_addr:                break        if raw_name and patch_fun_addr:            patch_name = func_name + "_" + raw_name            print("patch_name : ",patch_name)            set_name(patch_fun_addr,patch_name)        print(dword_,func_name)
    

    效果如下:

    這樣就容易分析得多,其實不止libc.so,還有libdl.so等,不過這個函數少,就手動恢復了。

    三、init_array

    地址:0x11720

    sub_13E48:打開libc.so,通過dlsym獲取了mprotect、mmap、munmap、fopen、fclose、fgets、fwrite、fread、sprintf、pthread_create函數指針。

    接著跟著frida的log,程序運行到了case 2。

    流程是case 2 -> case 5 -> case 4(讀cmdline) -> case 1 -> case 5 -> case 4循環讀取。

    這里主要是記錄包名的長度,存在v8里。

    最終執行到case 0,讀包名,然后和/system/bin/dex2oat對比,這里我包名和/system/bin/dex2oat不匹配,不進入下面的步驟(這個過程看不懂它要干啥)。

    然后進入到JNI_Onload。

    四、JNI_Onload

    字符串解密

    剛看到JNI_Onload,發現用了sub_12B94函數大量解密字符串。

    于是采用frida hook這個函數,打印出相應的信息(比如解密后的函數,返回地址),本來是只想解密字符串,但是字符串的解密順序其實幫助了分析流程的過程,解密字符串的函數不止一個,具體的可以看看附件,寫得很亂,需要注意的是這個hook的時機應該是在JNI_Onload解密之后,不然可能會出問題。

    function hook_decode_str(){    let base_secShell = Module.findBaseAddress("libSecShell.so");    let decode_str = base_secShell.add(0x12B94+1);    Interceptor.attach(decode_str, {        onEnter: function(args) {            console.log("=======decode_str========="+ " size = " + args[1] + " op = " + args[2]," return addr = " + this.context.lr.sub(base_secShell))            this.args0 = args[0]            this.args1 = args[1]        },        onLeave: function(){            console.log(hexdump(this.args0,{length:this.args1.toInt32()}))            // console.log(hexdump(args[0],{length:0x10}))        }    })}  function hook_svc_mprotect() {    let base_secShell = Module.findBaseAddress("libSecShell.so");    if (base_secShell != null) {        console.log("base_secShell : " + base_secShell)    }else{        return ;    }    let svc_mprotect = base_secShell.add(0xC0778);//32位    // let svc_mprotect = base_secShell.add(0x1541A0);//64位    //private native void jniLoadScriptFromAssets(AssetManager assetManager, String assetURL, boolean loadSynchronously);    Interceptor.attach(svc_mprotect, {        onEnter: function(args) {            console.log("==========================================")                        console.log("svc_mprotect: start = " + args[0] + " , len = " + args[1] + " , ATTRIBUTES = " + args[2])            mprotect_cnt += 1            console.log(hexdump(base_secShell.add(0x281B4)))        },        onLeave: function(){            console.log("svc_mprotect leave")            console.log("==========================================")                        if(mprotect_cnt == 2){                hook_decode_str()                // hook_elf_hook()                // sleep(1000000)            }        }    })}
    

    大概流程

    先執行case 0:初始化JNIEnv,解密得到com/SecShell/SecShell/H字符串

    然后case8(0x29e00):

    跳到sub_13E48,獲取libc.so一些函數指針,從java類獲取PKGNAME = "com.example.cryptotest",

    后面在case8里的case分支干了一些不知道在干啥,好像是在配置環境

    然后是case9:

    調用android/app/ActivityThread類的currentActivityThread方法

    調用ActivityThread對象的getSystemContext方法

    調用ContextImpl的getPackageManager方法

    調用PackageManager的getPackageInfo方法

    獲取PackageInfo對象的applicationInfo字段

    獲取ApplicationInfo對象的sourceDir字段

    獲取ApplicationInfo對象的nativeLibraryDir字段

    拼接出/proc/%d/fd/%d,遍歷fd找到base.apk路徑

    然后是case2:對小米手機進行適配

    然后是case3:創建了線程(沒執行到),驗證了簽名

    case1->case10

    case10:打開/proc/self/maps,找到lib/libart.so,比較是否是r-xp權限,通過格式化字符串%lx-%lx讀取地址

    case11:把libart.so改為可讀寫,兩個箭頭前后對比

    case13:拼接出各種字符串,比如/data/app/~~yqfNRTFBNC4L6gA2oycp-g==/com.example.cryptotest-VtwyTKkuWOlQLYKSpK7Z5Q==/oat/arm/base.odex

    然后到case13里面的case10,打開這個base.odex

    會打開打開classes.dve進行校驗

    然后會執行sub_260BC,這里會調用0x4D7DC(hook_libc_so_func),對hook部分說的那些函數進行hook,然后讀classes.jar寫到內存里的時候就調用這些函數進行解密(dex加載)

    case4:把libart.so權限改回去

    case12:弄了好多inlinehook,但是好像也沒有執行(不知道是不是我系統版本過高)

    具體見附件給的idb吧

    dex加載

    sub_1DFB0調用了com/SecShell/SecShell/H的f方法加載/data/user/0/com.example.cryptotest/.cache/classes.jar

    調用com/SecShell/SecShell/H的ff加載/data/user/0/com.example.cryptotest/.cache/v1filter.jar

    通過dump maps來比較加載前和加載后的差異

    可以直接把這個直接dump下來,發現解析不了,有點尷尬。

    于是比較一下frida-dexdump dump下來的文件,發現后面多了幾百個字節,刪掉就可以解析了。

    inlineHook

    地址:0x53E30是inline hook函數

    交叉引用可以看到很多hook的地方。

    比如hook了libc.so的pread64、ftruncate64、write、read、munmap、msync、__open、__openat、__mmap2。

    運行的時候發現其他的hook沒有觸發,之后用ida動態調試了一下確實是只hook了這些,其他地方不知道是不是有啥其他辦法能讓我斷不下來。

    這些沒有執行的地方就不過多分析了。

    函數p208CA25EFD02F087E334CA562B3F8423:

    檢測

    地址0x60C5C:(似乎沒有執行,發現這些check函數好像都沒有執行)

    xposed檢測,fart檢測等

    check_usb:0x25508

    check_root:0x17D9C

    其他

    函數地址:0x53E30,對華為和榮耀手機進行適配

    is_miuiinstaller_process對小米手機進行適配

    JNI_Onload里兼容性適配:

    JNI函數注冊:sub_16028(通過字符串解密log很容易發現)

    用ida動態調試的時候,可能會遇到函數不會自動解析成函數,在下面框框輸入這段腳本,然后用createFunction函數就可以創建函數了。

    def createFunction(start,end):    len_func = end - start    begin = start    del_items(start,0,len_func)         #先undefine    while len_func:        cnt = idc.create_insn(begin)        if cnt == 0:            break       #遇到比如off_31F40 DCD __stack_chk_guard_ptr - 0x31D78這種就不解析了,一般是連續的,如果不連續需要跳過,繼續解析        begin += cnt        len_func -= cnt        print(len_func)      #idc.create_insn(start)    return idc.add_func(start,end)
    

    【參考文獻】分析一下梆x加固:https://bbs.pediy.com/thread-266247.htm

    好像超過上傳大小了,兩個文件,apk傳不上去,所有文件放在百度網盤:

    鏈接:https://pan.baidu.com/s/1Wdjp431IhhoCbcICQCJRAg

    提取碼:kxuc

    函數調用
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    概述在windows系統上,涉及到內核對象的功能函數,都需要從應用層權限轉換到內核層權限,然后再執行想要的內核函數,最終將函數結果返回給應用層。本文就是用OpenProcess函數來觀察函數從應用層到內核層的整體調用流程。OpenProcess函數,根據指定的進程ID,返回進程句柄。NTSTATUS Status; //保存函數執行狀態。OBJECT_ATTRIBUTES Obja; //待打開對象的對象屬性。HANDLE Handle; //存儲打開的句柄。CLIENT_ID ClientId; //進程、線程ID. dwDesiredAccess, //預打開進程并獲取對應的權限。ObjectNamePresent = ARGUMENT_PRESENT ; //判斷對象名稱是否為空
    關于堆棧ShellCode操作:基礎理論002-利用fs寄存器尋找當前程序dll的入口:從動態運行的程序中定位所需dll003-尋找大兵LoadLibraryA:從定位到的dll中尋找所需函數地址004-被截斷的shellCode:加解密,解決shellCode的零字截斷問題
    反射式DLL注入實現
    2022-05-13 15:59:21
    反射式dll注入與常規dll注入類似,而不同的地方在于反射式dll注入技術自己實現了一個reflective loader()函數來代替LoadLibaryA()函數去加載dll,示意圖如下圖所示。藍色的線表示與用常規dll注入相同的步驟,紅框中的是reflective loader()函數行為,也是下面重點描述的地方。
    該漏洞發生的位置是在驅動文件Win32k.sys中的xxxHandleMenuMessage函數,產生的原因是沒有對該函數中調用的xxxMNFindWindowFromPoint函數的返回值進行合法性驗證,直接將其作為參數傳遞給后面的xxxSendMessage函數調用,從而造成了提權漏洞。
    Win32k組件最初的設計和編寫是完全建立的用戶層上的,但是微軟在 Windows NT 4.0 的改變中將 Win32k.sys 作為改變的一部分而引入,用以提升圖形繪制性能并減少 Windows 應用程序的內存需求。窗口管理器(User)和圖形設備接口(GDI)在極大程度上被移出客戶端/服務端運行時子系統(CSRSS)并被落實在它自身的一個內核模塊中。
    結構&拷貝與引用
    2023-05-10 11:27:04
    結構&拷貝與引用開始之前,我們約定數據塊也叫插槽,也就是storage。storage是永久存儲在區塊鏈上的地方。Stack 的最大深度為 1024 個元素,支持 256 位的字長。結構當定義局部變量時,它存儲在內存中,然后壓入堆棧以執行。1024棧深簡介EVM不是寄存器機而是堆棧機,所以所有的計算都在稱為堆棧的數據區域上進行。1024 是一個非常保守的值,以盡可能安全EVM 的設計方式往往會使更大的堆棧變得無用。EVM 只能訪問堆棧中前16個slot。
    可是當我們開啟了smap保護之后,內核態就沒有辦法訪問用戶態的數據,此時當我們再hijack tty_operation到我們的用戶態時,我們的kernel就會panic,更別說劫持執行流到用戶態上執行rop了。當我們調用msgsnd時,在linux內核中會調用do_msgsnd。
    本篇針對該JS中的字符串混淆進行還原。字符串是如何混淆的解密方式想要對字符串反混淆就要先分析該樣本是如何對字符串進行混淆的。而處于全局作用域的_0x1f1a68實際上也是對另一個函數的調用。
    當線程從等待狀態蘇醒后,會自動檢測自己得APC隊列中是否存在APC過程。所以只需要將目標進程的線程的APC隊列里面添加APC過程,當然為了提高命中率可以向進程的所有線程中添加APC過程。然后促使線程從休眠中恢復就可以實現APC注入。往線程APC隊列添加APC,系統會產生一個軟中斷。第二個參數表示插入APC的線程句柄,要求線程句柄必須包含THREAD_SET_CONTEXT訪問權限。第三個參數表示傳遞給執行函數的參數。如果直接傳入shellcode不設置第三個函數,可以直接執行shellcode。
    01高門檻,勿入在Cisco平臺上有一個很有用的Traceback log功能,實時記錄當前Code運行到特
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类