百度加固逆向分析

 最近一直在研究百度壳,发现网上这方面的资料非常少。所以我把自己做的发出来跟大家分享,共同学习进步。

下面开始:

一、init_array 

我们发现init_array中存在多个函数地址,JNI_Onload为加密状态

动态调试在init_array上下断跟着进入一个大循环。发现他在此处对so进行了抹头操作。

之后遇到反调试崩溃退出。后来发现反调试检测了以下字段:

android_server

gdbserver

gdb

TracePid:

/proc/self/task/%s/status

isDebuggerConnected等。。

二、bypass

编写了一个loader程序调用该so中的JNI_Onload函数bypass壳代码和反调试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <jni.h>
int  main()
{
     JavaVM* vm;
     JNIEnv* env;
     jint res;
     
     JavaVMInitArgs vm_args;
     JavaVMOption options[1];
     options[0].optionString =  "-Djava.class.path=." ;
     vm_args.version=0x00010002;
     vm_args.options=options;
     vm_args.nOptions =1;
     vm_args.ignoreUnrecognized=JNI_TRUE;
     
     printf ( "[+] dlopen libdvm.so\n" );
     void  *handle = dlopen( "/system/lib/libdvm.so" , RTLD_LAZY); //RTLD_LAZY RTLD_NOW
     if (!handle){
     printf ( "[-] dlopen libdvm.so failed!!\n" );
     return  0;
     }
     //这里我先创建一个java虚拟机。因为JNI_ONload函数参数第一个参数为JavaVM。
     typedef  int  (*JNI_CreateJavaVM_Type)(JavaVM**, JNIEnv**,  void *);
     JNI_CreateJavaVM_Type JNI_CreateJavaVM_Func = (JNI_CreateJavaVM_Type)dlsym(handle,  "JNI_CreateJavaVM" );
     if (!JNI_CreateJavaVM_Func){
     printf ( "[-] dlsym failed\n" );
     return  0;
     }
     res=JNI_CreateJavaVM_Func(&vm,&env,&vm_args)
     void * si=dlopen( "/data/local/tmp/libbaiduprotect.so" ,RTLD_LAZY);
     if (si == NULL){
     printf ( "[-] dlopen err!\n" );
     return  0;
     }
     typedef  jint (*FUN)(JavaVM* vm, void * res);
     FUN func_onload=(FUN)dlsym(si, "JNI_OnLoad" );
     if (func_onload==NULL) //我将断点下在了这里可以正好获取到JNI_Onload的函数地址。
         return  0;
     func_onload(vm,NULL);
     return  0;
}

此时R0为dlsym返回的JNI_Onload地址.我们跳转过去,按C将字节码转成代码,发现此时JNI_Onload已经解密。

这时候我们将so从内存中dump出来。将原来的so头部和section修复回去。代码的解密已经完成。

3、字符串解密

这时候我们发现so的字符串还是加密状态,每个字符串对应一个解密函数。

经过分析发现。解密算法是一样的,只是异或的偏移地址不一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
_BYTE *__fastcall sub_17A28(_BYTE *a1)
{
   _BYTE *v1;  // r4@1
   int  v2;  // r0@1
   int  v3;  // r1@2
   unsigned  int  v4;  // r5@3
   int  v5;  // r3@3
   int  v6;  // r7@3
   _BYTE *v7;  // r4@4
   int  v8;  // r6@4
   _BYTE *v9;  // r5@4
   unsigned  int  v10;  // r2@5
   signed  int  v11;  // r0@5
   unsigned  int  v12;  // r1@7
   int  v13;  // r3@7
   int  v14;  // r7@7
   int  v15;  // r0@9
   int  v16;  // r2@9
   _BYTE *v18;  // [sp+4h] [bp-1Ch]@3
   v1 = a1;
   v2 = ( int )(a1 - 1);
   do
     v3 = *(_BYTE *)(v2++ + 1);
   while  ( v3 );
   v4 = (unsigned  int )(v2 - (_DWORD)v1) >> 1;
   v18 = j_j_malloc(v4 + 1);
   v5 = 0;
   v18[v4] = 0;
   v6 = *v1;
   if  ( *v1 )
   {
     v7 = v1 + 2;
     v8 = 0;
     v9 = v18;
     do
     {
       v10 = v7[~v5];
       v11 = 208;
       if  ( v10 >= 0x3A )
         v11 = 201;
       v12 = (unsigned  __int8 )v6;
       v13 = 16 * v6;
       v14 = 16 * v6 + 144;
       if  ( v12 >= 0x3A )
         v13 = v14;
       v15 = v13 + v11 + v10;
       v5 = 0;
       v16 = 0;
       if  ( v8 != 8 )
         v16 = v8;
       *v9 = byte_5FB25[v16] ^ v15; //此处byte数组值不同。
       v8 = v16 + 1;
       ++v9;
       v6 = *v7;
       v7 += 2;
     }
     while  ( v6 );
   }
   return  v18;
}

分析完解密算法后,写了个ida py解密脚本。由于初次写ida脚本。借鉴了一些网上的代码。写的很糟糕。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import  re
def  getDispatchAddress(ea):
     ea  =   FindText(ea, SEARCH_DOWN |SEARCH_NEXT | SEARCH_REGEX, 0 0 , "BL      sub" )
    
     #Message("Find in %x\n" % ea)
     if  ea  = =  BADADDR:
         #Message("Cann't find the Dispatch address")
         address  =  BADADDR
     else :
         address  =  GetOperandValue(ea, 0 )
         #Message("Dispatch address is %x\n" % address)
     return  address
def  getFunctionInstructions(addr):
     Instructions  =  []
     DispatchBeginAddress  =  getDispatchAddress(addr)
     if  DispatchBeginAddress  = =  BADADDR:
        Message( "Cann't find the Function Instructions List" )
        return  None
     DispatchEndAddress  =  GetFunctionAttr(DispatchBeginAddress,FUNCATTR_END)
     =   DispatchBeginAddress
     while  True :
       #Instructions.append(GetDisasm(i))
       Instructions.append(i)
       tmp  =  +  ItemSize(i)
       if  tmp < DispatchEndAddress:
          =  +  ItemSize(i)
       else :
          break
     address  =  i
     return  Instructions
def  getaddr(addr):
     for  in  XrefsTo(addr,flags  =  0 ):
         #print hex(x.frm)
         num = 0
         got = 0
         for  in  getFunctionInstructions(x.frm):
             mnem  =  GetMnem(i)
             if  "LDR"  in  mnem :
                 if  num  = =  5 :
                     #print hex(i)
                     op   =   GetOperandValue(i, 1 )
                     b3 = Byte(op + 3 )<< 24
                     b2 = Byte(op + 2 )<< 16
                     b1 = Byte(op + 1 )<< 8
                     b0 = Byte(op + 0 )
                     got = b0 + b1 + b2 + b3 + (i + 6 )
                 if  num  = =  6 :
                     #print hex(i)
                     op   =   GetOperandValue(i, 1 )
                     b3 = Byte(op + 3 )<< 24
                     b2 = Byte(op + 2 )<< 16
                     b1 = Byte(op + 1 )<< 8
                     b0 = Byte(op + 0 )
                     tmp = b0 + b1 + b2 + b3
                     off = got + tmp
                     #print hex(off&0xffffffff)
                     return  off& 0xffffffff
                     break
                 num = num + 1
         break      
def  decrypt1(addr, str ):
     len1 = len ( str )>> 1
     out = ''
     for  in  range ( 0 ,len1):
         v1 = 208
         v2 = 16 * ord ( str [ 2 * i])
         if  ord ( str [ 2 * i + 1 ])> = 0x3a :
             v1 = 201
         if  ord ( str [ 2 * i])> = 0x3a :
             v2 = 16 * ord ( str [ 2 * i]) + 144
         v3 = (v1 + v2 + ord ( str [ 2 * i + 1 ]))& 0xff
         out = out + chr (Byte(addr + i % 8 )^v3)
     print  out
def  decrypt(addr1, str ): #这里就是主要的解密
     addr = getaddr(addr1)
     if  addr  = =  None :
         return  ''
     len1 = len ( str )>> 1
     out = ''
     for  in  range ( 0 ,len1):
         v1 = 208
         v2 = 16 * ord ( str [ 2 * i])
         if  ord ( str [ 2 * i + 1 ])> = 0x3a :
             v1 = 201
         if  ord ( str [ 2 * i])> = 0x3a :
             v2 = 16 * ord ( str [ 2 * i]) + 144
         v3 = (v1 + v2 + ord ( str [ 2 * i + 1 ]))& 0xff
         out = out + chr (Byte(addr + i % 8 )^v3)
     print  out
     #print hex(addr1)
     MakeComm(addr1,out)
     for  in  XrefsTo(addr1,flags  =  0 ):
         MakeComm(x.frm,out)
def  GetAllString(addr):
     str_1 = GetString(addr, - 1 ,ASCSTR_C)
     len_1 = len (str_1)
     if  str_1 ! =  ''  and  addr< 0x5f6c8 :
         if  re.search( "^[0-9A-Z]+$" ,str_1):
             #print addr,str_1
             decrypt(addr,str_1)
         return  GetAllString(addr + len_1 + 1 )
     else :
         decrypt(addr,str_1)
addr = 0x5d8e8
GetAllString(addr)

运行之后可以看到字符串已解密成功。

之后就可以继续分析so对dex的保护过程,我目前还未分析完成。大家有兴趣的可以一起分析。分析好后我会再发一篇文章。

libbaiduprotect.so我放到附件了


上传的附件:
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ios and Android

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值