1. 程式人生 > 其它 >android逆向奇技淫巧二十六:基礎庫的hook&x音檢測frida方式(十一)

android逆向奇技淫巧二十六:基礎庫的hook&x音檢測frida方式(十一)

  1、萬丈高樓平地起,正常人都知道高樓大廈地基的重要性!現代產業鏈的成熟,讓產業分工越來越明細,很少有公司或團隊能完整地提供產業鏈上每個環節的產品,很多都是基於某個廠家上游的產品繼續做下游的開發,典型的如應用app開發,肯定要基於作業系統提供的api介面;同理,作業系統的開發需要基於cpu提供的彙編指令,不太可能有一家公司從cpu的製造、作業系統的開發到應用的開發大包大攬全都做(主要是管理人員沒那麼高超的管理能力運作幾十、甚至上百萬人的超大規模的團隊)!站在逆向的角度,只要抓住了底層作業系統或基礎庫提供的介面,是不是就抓住了上層應用的執行軌跡了?

  做業務應用開發,速度很重要,因為網際網路是個快魚吃慢魚的時代:app早上線1天運營,可能會比競爭對手多圈幾十萬使用者,牢牢樹立起各種壁壘,讓後來者很難追上!所以開發人員為了提高速度,大概率會用很多現成的基礎庫或API(避免無意義的重複造輪子);同時,由於基礎庫長時間被大量app使用,如果有嚴重的bug或效能缺陷,早就被爆出來修復了,所以直接用基礎庫也比自己另起爐灶單獨搞一套划算得多!

其中有個非常重要的基礎庫:libc.so! 這個庫包括了大量基礎的功能,分類如下:

  • 字串類的操作:strstr、strcmp、strcat、strlen.....
  • 檔案類的操作:open、read、write、close、fgetc.....
  • 網路IO類操作:send、recv......
  • 執行緒類操作:pthrea_create.....

  我們能在Java層面使用的各種功能,底層也都是靠這些基礎庫的api實現的!比如java的FileInputStream執行時,JVM肯定呼叫了libc庫的open、read、fgetc等api來實現;又比如java的socket、outputstream等函式在jvm層面肯定呼叫了sockt、connect、send/sendto等api。再說直白點:libart.so肯定呼叫了libc.so的api,所以hook這些基礎庫(包括java層和native層)的api時,肯定能在一定程度上追蹤、還原app的各種操作

  2、(1)先看看socket函式,linux/android用這個函式建立socket描述符,x音hook的結果如下(hook指令碼在文章末尾):

Entering =>  socket
args[0] =>  0xa
args[1] =>  0x2
args[2] =>  0x0

called from: 0xc7bf8f43 libopenjdkjvm.so!JVM_Socket+0x33 0xc79ede46 libopenjdk.so!PlainDatagramSocketImpl_datagramSocketCreate+0x86 0x7093ff0d boot.oat!oatexec+0xe5f0d

  第三個引數是protocol為0,作業系統核心會自動選擇type型別對應的預設協議;第二個引數是0x2,說明socket的type是SOCK_DGRAM,也就是UDP協議啦,個人猜測:這有可能是傳輸音視訊資料,也有可能是quic協議,需要進一步分析!還有,從呼叫棧的資料看,果然是上層的jvm在呼叫native層的socket函式!

      (2)繼續看重要的api:send和sendto,這個是hook的結果。能找到啥關鍵資訊麼?x音的X-Khronos、X-Gorgon都能看到

Entering =>  send
args[0] =>  0xf1
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
9aeea748  47 45 54 20 2f 74 6f 73 2d 63 6e 2d 70 2d 30 30  GET /tos-cn-p-00
9aeea758  31 35 2f 30 34 62 32 65 61 66 62 30 30 39 31 34  15/04b2eafb00914
9aeea768  65 61 63 61 63 39 35 32 32 65 64 63 63 34 63 37  eacac9522edcc4c7
9aeea778  64 65 63 5f 31 36 34 39 35 36 34 38 33 34 7e 74  dec_1649564834~t
9aeea788  70 6c 76 2d 6e 6f 6f 70 2e 69 6d 61 67 65 3f 78  plv-noop.image?x
9aeea798  2d 65 78 70 69 72 65 73 3d 31 36 34 39 35 37 39  -expires=1649579
9aeea7a8  37 30 37 26 78 2d 73 69 67 6e 61 74 75 72 65 3d  707&x-signature=
9aeea7b8  68 49 4e 64 79 31 5a 56 48 76 71 68 55 35 4c 4c  hINdy1ZVHvqhU5LL
9aeea7c8  48 72 4b 67 43 6a 53 6d 72 42 59 25 33 44 20 48  HrKgCjSmrBY%3D H
9aeea7d8  54 54 50 2f 31 2e 31 0d 0a 41 63 63 65 70 74 2d  TTP/1.1..Accept-
9aeea7e8  45 6e 63 6f 64 69 6e 67 3a 20 69 64 65 6e 74 69  Encoding: identi
9aeea7f8  74 79 0d 0a 52 61 6e 67 65 3a 20 62 79 74 65 73  ty..Range: bytes
9aeea808  3d 30 2d 0d 0a 58 2d 4b 68 72 6f 6e 6f 73 3a 20  =0-..X-Khronos:
9aeea818  31 36 34 39 35 37 36 30 31 30 0d 0a 58 2d 47 6f  1649576010..X-Go
9aeea828  72 67 6f 6e 3a 20 30 34 30 34 38 30 37 39 30 30  rgon: 0404807900
9aeea838  30 30 33 61 39 32 34 66 64 31 36 63 61 35 62 33  003a924fd16ca5b3
9aeea848  34 38 38 66 34 33 65 30 64 62 36 38 38 39 61 38  488f43e0db6889a8
9aeea858  31 66 61 39 30 63 37 35 38 34 0d 0a 48 6f 73 74  1fa90c7584..Host
9aeea868  3a 20 70 33 2d 73 69 67 6e 2e 64 6f 75 79 69 6e  : p3-sign.douyin
9aeea878  70 69 63 2e 63 6f 6d 0d 0a 43 6f 6e 6e 65 63 74  pic.com..Connect
9aeea888  69 6f 6e 3a 20 4b 65 65 70 2d 41 6c 69 76 65 0d  ion: Keep-Alive.
9aeea898  0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 6f 6b 68  .User-Agent: okh
9aeea8a8  74 74 70 2f 33 2e 31 30 2e 30 2e 31 0d 0a 0d 0a  ttp/3.10.0.1....
args[2] =>  0x170

 called from:
0xc79d8c42 libopenjdk.so!NET_Send+0x62
0xc79f2a83 libopenjdk.so!SocketOutputStream_socketWrite0+0x133
0x7094a7ed boot.oat!oatexec+0xf07ed

Entering =>  sendto
args[0] =>  0xf1
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
9aeea748  47 45 54 20 2f 74 6f 73 2d 63 6e 2d 70 2d 30 30  GET /tos-cn-p-00
9aeea758  31 35 2f 30 34 62 32 65 61 66 62 30 30 39 31 34  15/04b2eafb00914
9aeea768  65 61 63 61 63 39 35 32 32 65 64 63 63 34 63 37  eacac9522edcc4c7
9aeea778  64 65 63 5f 31 36 34 39 35 36 34 38 33 34 7e 74  dec_1649564834~t
9aeea788  70 6c 76 2d 6e 6f 6f 70 2e 69 6d 61 67 65 3f 78  plv-noop.image?x
9aeea798  2d 65 78 70 69 72 65 73 3d 31 36 34 39 35 37 39  -expires=1649579
9aeea7a8  37 30 37 26 78 2d 73 69 67 6e 61 74 75 72 65 3d  707&x-signature=
9aeea7b8  68 49 4e 64 79 31 5a 56 48 76 71 68 55 35 4c 4c  hINdy1ZVHvqhU5LL
9aeea7c8  48 72 4b 67 43 6a 53 6d 72 42 59 25 33 44 20 48  HrKgCjSmrBY%3D H
9aeea7d8  54 54 50 2f 31 2e 31 0d 0a 41 63 63 65 70 74 2d  TTP/1.1..Accept-
9aeea7e8  45 6e 63 6f 64 69 6e 67 3a 20 69 64 65 6e 74 69  Encoding: identi
9aeea7f8  74 79 0d 0a 52 61 6e 67 65 3a 20 62 79 74 65 73  ty..Range: bytes
9aeea808  3d 30 2d 0d 0a 58 2d 4b 68 72 6f 6e 6f 73 3a 20  =0-..X-Khronos:
9aeea818  31 36 34 39 35 37 36 30 31 30 0d 0a 58 2d 47 6f  1649576010..X-Go
9aeea828  72 67 6f 6e 3a 20 30 34 30 34 38 30 37 39 30 30  rgon: 0404807900
9aeea838  30 30 33 61 39 32 34 66 64 31 36 63 61 35 62 33  003a924fd16ca5b3
9aeea848  34 38 38 66 34 33 65 30 64 62 36 38 38 39 61 38  488f43e0db6889a8
9aeea858  31 66 61 39 30 63 37 35 38 34 0d 0a 48 6f 73 74  1fa90c7584..Host
9aeea868  3a 20 70 33 2d 73 69 67 6e 2e 64 6f 75 79 69 6e  : p3-sign.douyin
9aeea878  70 69 63 2e 63 6f 6d 0d 0a 43 6f 6e 6e 65 63 74  pic.com..Connect
9aeea888  69 6f 6e 3a 20 4b 65 65 70 2d 41 6c 69 76 65 0d  ion: Keep-Alive.
9aeea898  0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 6f 6b 68  .User-Agent: okh
9aeea8a8  74 74 70 2f 33 2e 31 30 2e 30 2e 31 0d 0a 0d 0a  ttp/3.10.0.1....
args[2] =>  0x170

 called from:
0xc4d4c6d7 libc.so!send+0x47
0xc79d8c42 libopenjdk.so!NET_Send+0x62
0xc79f2a83 libopenjdk.so!SocketOutputStream_socketWrite0+0x133
0x7094a7ed boot.oat!oatexec+0xf07ed


exit =>  sendto

  從呼叫棧看,send方法呼叫了sendto方法發資料!再往上就是java層的socketOutPutStream的socketWrite方法呼叫了native的send函式!所以理論上講,去hook java層的socket類、outputStream等也能得到傳送的資料

   (3)fgets函式:從檔案讀資料的。hook前面兩個引數部分日誌如下:這樣是不是很明顯在檢測frida了

Entering =>  fgets
args[0] =>  abda5000-abfe8000 rw-p 00000000 00:00 0          [anon:.bss]
args[1] =>  0x200
retval is =>  abfe8000-ad635000 r-xp 00000000 08:12 917529     /data/local/tmp/re.frida.server/frida-agent-32.so


 exit =>  fgets
Entering =>  fgets
args[0] =>  abfe8000-ad635000 r-xp 00000000 08:12 917529     /data/local/tmp/re.frida.server/frida-agent-32.so
args[1] =>  0x200
retval is =>  ad635000-ad681000 r--p 0164c000 08:12 917529     /data/local/tmp/re.frida.server/frida-agent-32.so


 exit =>  fgets
Entering =>  fgets
args[0] =>  ad635000-ad681000 r--p 0164c000 08:12 917529     /data/local/tmp/re.frida.server/frida-agent-32.so

args[1] =>  0x200
retval is =>  ad681000-ad68f000 rw-p 01698000 08:12 917529     /data/local/tmp/re.frida.server/frida-agent-32.so


 exit =>  fgets
Entering =>  fgets
args[0] =>  ad681000-ad68f000 rw-p 01698000 08:12 917529     /data/local/tmp/re.frida.server/frida-agent-32.so

args[1] =>  0x200
retval is =>  ad68f000-ad6c2000 rw-p 00000000 00:00 0          [anon:.bss]

  個人猜測檢測的程式碼可能是這樣寫的:

char line[0x200];
FILE* fp;
fp = fopen("/proc/self/maps", "r");
if (fp) {
    while (fgets(line, 0x200, fp)) {
        if (strstr(line, "frida")) { 
            /* Evil library is loaded. Do something… 檢測frida的程式碼邏輯*/
        }
    }
    fclose(fp);
    } else {
       /* Error opening /proc/self/maps. If this happens, something is off. */
    }
}

  順著這個思路,也可以hook strstr、strcmp等常見的函式看看有沒有檢測的邏輯!

  (4)hook的核心js程式碼:整體的思路很簡單,就是遍歷modules,找到libc.so;然後進一步找到遍歷庫的匯出表,找到關鍵的庫函式去hook!

function traceNativeExport(){

    var modules = Process.enumerateModules();
    for(var i = 0;i<modules.length;i++){
        var module = modules[i];
        //只hook libc.so
        if(module.name.indexOf("libc.so")<0){
            continue;
        }

        var exports = module.enumerateExports();
        for(var j = 0;j<exports.length;j++){
            //console.log("module name is =>",module.name," symbol name is =>",exports[j].name)
            //var path = "/sdcard/Download/so/"+module.name+".txt"
            // var path = "/data/data/com.ss.aweme/cache/"+module.name+".txt"
            // writeSomething(path,"type: "+exports[j].type+" function name :"+exports[j].name+" address : "+exports[j].address+" offset => 0x"+ ( exports[j].address.sub(modules[i].base) )+"\n")
            // if(exports[j].name.indexOf("strto")>=0)continue;
            // if(exports[j].name.indexOf("strco")>=0)continue;
            // if(exports[j].name.indexOf("_l")>=0)continue;
            // if(exports[j].name.indexOf("pthread")>=0)continue;
            
            //int socket(int domain, int type, int protocol);
            /*if(exports[j].name.indexOf("socket")>=0){
                 attach(exports[j].name,exports[j].address);
            }*/
            /*if(exports[j].name.indexOf("pthread_create")>=0){
                attach(exports[j].name,exports[j].address);
            }*/
            //int  connect(int  sockfd,  const struct sockaddr *serv_addr, socklen_t addrlen)
            /*if(exports[j].name.indexOf("connect")>=0){
                attach(exports[j].name,exports[j].address);
            }*/
            // if(exports[j].name.indexOf("read")>=0){
            //     attach(exports[j].name,exports[j].address);
            // }
            // if(exports[j].name.indexOf("write")>=0){
            //     attach(exports[j].name,exports[j].address);
            // }
            //ssize_t send(int sockfd, const void *buf, size_t len, int flags);
            /*if(exports[j].name.indexOf("send")>=0){
                 attach(exports[j].name,exports[j].address);
            }
            /*if(exports[j].name.indexOf("strstr")>=0){
                attach(exports[j].name,exports[j].address);
            }*/
            /*if(exports[j].name.indexOf("strcmp")>=0){
                attach(exports[j].name,exports[j].address);
            }*/
            if(exports[j].name.indexOf("fgets")>=0){
                attach(exports[j].name,exports[j].address);
            }
            // if(exports[j].name.indexOf("recv")>=0){
            //     attach(exports[j].name,exports[j].address);
            // }

        }
    }
}

function attach(name,address){
    console.log("attaching ",name);
    Interceptor.attach(address,{
        onEnter:function(args){
            console.log("Entering => " ,name)
            console.log("args[0] => ",args[0].readCString())
            console.log("args[1] => ",args[1])
            /*if(args[0].readCString().indexOf("frida")>=0
                ||args[0].readCString().indexOf("xpose")>=0
                ||args[1].readCString().indexOf("xpose")>=0
                ||args[1].readCString().indexOf("frida")>=0){
                    console.log("Entering => " ,name)
                    console.log("args[0] => ",args[0].readCString())
                    console.log("args[1] => ",args[1].readCString())
                    console.log('\n called from:\n' +Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
            }*/
            /*console.log("args[0] => ",args[0].readCString())
            console.log("args[1] => ",args[1].readCString())*/
            /*console.log( hexdump(args[0],{
                offset: 0,
                length: parseInt(args[1]),
                header: true,
                ansi: true
            }))*/
            
            //console.log("args[2] => ",args[2])
            //console.log('\n called from:\n' +Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');

        },onLeave:function(retval){
            console.log("retval is => ",retval.readCString())
            console.log("\n exit => ",name)
            // console.log("retval is => ",retval.readCString())
        }
    })

}

 

 

參考:

1、https://blog.csdn.net/zhangmiaoping23/article/details/109697329 多種特徵檢測frida