0
点赞
收藏
分享

微信扫一扫

安卓逆向的初步研究--从某恋app入手

霍华德 2023-07-04 阅读 74

主题:安卓app中的关键登录逻辑分析
目标:des算法分析,.so文件分析
样本:某恋v5.0.1 app
代码:main函数自实现,其它函数提取自app中的安卓无关代码
作者:by GKLBB
参考:Bu弃 https://www.chinapyg.com/forum.php?mod=viewthread&tid=119242&highlight=DES
无名Android逆向 系列视频

资源:

链接:https://pan.baidu.com/s/14FzEJt0uegp9XQhYr5iQoA
提取码:rrc0

test文件里是加密器,依据分析app逻辑用java写出

 

1.依据线索定位关键函数
抓包找到线索
在dex中搜索线索

com.a.a.a.b. s
switch
紧跟着加密代码

JSONObject v1_1 = new JSONObject();   //创建一个构建JSON字符串的对象
 v1_1.put("xxxx", xxxx);   //往里面加入key/value形式的键值对
 v1_1.put("xxxx", xxxx);
 v0 = com.a.a.a.f.a.a(v1_1.toString()).getBytes(); //com.a.a.a.f.a.a(v1_1.toString()) 就是具体的加密逻辑了

 com.a.a.a.f.a.a
 Jni.getInstance().encryptString(arg1);

 com.Jni.encryptString
 v4.append(this.getEncryptString(v2, true)); //调用this.getEncryptString(v2, true)加盐
 DESencryption.getEncString(v4.toString(), this.getEncryptString("a", true).substring(0, 8));//getEncryptString

就是libjni.so中的一个方法,这里有个细节容易被忽略,v2是输入v2字符串格式化json后hex转换后的结果,如果直接传入“a”其结果是a+盐

最终我们找到了两个关键函数加密用的getEncString,加盐用的getEncryptString

2.分析关键函数
分析getEncryptString
用ida生成的c伪码

int __fastcall Java_com_jni_Jni_getEncryptString(_JNIEnv *a1, JNINativeInterface *a2, int a3, int a4)
{
  _JNIEnv *v4; // r5@1
  int v5; // r4@1
  const char *v6; // r7@1
  size_t v7; // r4@1
  _JNIEnv *v8; // r0@2
  char *v9; // r1@2
  jstring (__cdecl *v10)(JNIEnv *, const char *); // r3@2
  int result; // r0@6
  char *s; // [sp+0h] [bp-828h]@1
  int v13; // [sp+4h] [bp-824h]@1
  char dest; // [sp+Ch] [bp-81Ch]@3
  int v15; // [sp+80Ch] [bp-1Ch]@1  v4 = a1;
  v5 = a3;
  v13 = a4;
  v15 = _stack_chk_guard;
  g_env = a1;
  s = (char *)initAddStr();//// 初始化一个字符串,即就是盐本身。
  v6 = (const char *)jstringTostring(v4, v5);// 调用JNI的方法,把Java中的String变成C中的char *,即就是输入的v5转char×
  v7 = strlen(s);// 求出初始化字符串的长度
  if ( strlen(v6) + v7 <= 0x7FF )// 转成C的inputStr的长度和s的长度<0x7ff(2047)如果小于则拼上s.否则不拼
  {
    memset(&dest, 0, 0x7FFu);// 往dest这个地址填充0x7FF个0
    strcat(&dest, v6);// 这里是把v6的值,也就是inputStr转成Char的值赋给dest
    if ( v13 )// 第2个传参也就是a4 inputBool为true的时候,就在后面跟上初始值s。否则不跟。也就是说要想加入盐必需加入后的长度不能超过2047且为true时。
      strcat(&dest, s);//加盐!!!!!!!!!!!!!!!!!!!!!!!1111
    v8 = v4;// v8 = JNIEnv
    v9 = &dest;
    v10 = v4->functions->NewStringUTF;// v10 =  NewStringUTF:把C的char* 转换成Java中的String
  }
  else//无盐直接转换string回去
  {
    v8 = v4;
    v9 = (char *)v6;
    v10 = v4->functions->NewStringUTF;//// v10 =  NewStringUTF:把C的char* 转换成Java中的String
  }
  result = ((int (__fastcall *)(_JNIEnv *, char *))v10)(v8, v9);// 反转结果为string,调用NewStringUTF方法,把v10转换成String
  if ( v15 != _stack_chk_guard )
    _stack_chk_fail(result);
  return result;
}//总结一下,就是如果加盐后的长度不能超过2047且为true时,加盐,否则不加直接返回
分析initAddStr
用ida生成的c伪码
int initAddStr()
{
  int v0; // r0@2
  int v1; // r2@2

  if ( !isInit ) //这里是如果初始化一次了,就不需要再执行了。也就是这里只会执行一次。
  {
    v0 = initInflect((int)jniStr);  //反射到java层,参数就是“/key=i im lianai”
    key = jstringTostring((int)g_env, v0, v1); //调用方法,把java的String变成C语言中的char*
    isInit = 1;
  }
  return key;
}
//总结一下,这段是包含/key=i im lianai中间代码,跳转到initInflect分析initInflect
用ida生成的c伪码
int __fastcall initInflect(int a1)
{
  _JNIEnv *env; // r5@1
  int v2; // r0@1
  bool v3; // zf@1
  int v4; // r7@1
  const struct JNINativeInterface *v5; // r0@1
  int (__fastcall *v6)(int *, const char *); // r3@3
  const char *v7; // r1@3
  int v8; // r0@2
  const struct JNINativeInterface *v9; // r3@4
  int v10; // r4@4
  int v12; // [sp+Ch] [bp-1Ch]@1  v12 = a1;//保存入参
  env = (_JNIEnv *)g_env;

  //找出类ID 
  v2 = (*(int (__fastcall **)(_DWORD *, const char *))(*g_env + 24))(g_env, "com/Reflect");//clzID
  v4 = v2=clzID;
  v3 = v2 == 0;
  v5 = env->functions;
  if ( v3 )//clzID存在,自行下面代码段
  {
    v6 = (int (__fastcall *)(int *, const char *))env->functions;->NewStringUTF;
    v7 = "jclass";
    return v6((int *)env, v7);
  }

  //找出方法ID
  v8 = ((int (__fastcall *)(_JNIEnv *, int, const char *, const char *))env->functions;->GetStaticMethodID)(
         env,
         clzID,
         "func",
         "(ILjava/lang/String;)Ljava/lang/String;");
  v9 = env->functions;
  v10 = v8=MethodID;
  if ( !v8 )
  {
    v6 = (int (__fastcall *)(int *, const char *))env->functions;->NewStringUTF;
    v7 = "method";
    return v6((int *)env, v7);
  }

  //调用类.方法
  ((void (__fastcall *)(_JNIEnv *, int))env->functions;->NewStringUTF)(env, v12);//v12就是本函数入参/key=i im lianai,把v12转string,但是这里没有接收者,是因为伪代码不可信
  return _JNIEnv::CallStaticObjectMethod(env, clzID, MethodID, 10);
}
//总结一下,,这段是真正的反射中间代码,调用java层的com.Reflect类中的func方法,并返回结果。分析com.Reflect类!!!!!!!!!!
    public static String func(int arg2, String arg3) {//这个就是IDA中调用的方法
        return Reflect.encode(String.valueOf(arg3) + " alien");  //这里调用了此类中的encode方法,传入的是我们传递过来的参数“/key=i im lianai” 加上此类提供的一个静态字符串" alien"<注意前面有个空格>,综合起来也就是“/key=i im lianai alien”!!!!!!!!!!!!!!!!1
//总结一下,/key=i im lianai后加入alien,生成最终盐key=i im lianai alien返回

 

 3.解密加密了的字符串

 des是对称加密算法,因此密文:已知,算法:DES/CBC/PKCS5Padding, 密码:a2F6B657,IV:010230405060708,就可以解出明文,注意解出来的值还要转换HEX字符串然后每两位一组当作HEX字符串转换为accii,这是因为加密时ascii->hex->byte 解码就要byte->hex->ascii

 

附录:

分析前要清楚r0 r1 r2 r3四个寄存器的保存的是什么,保存的是少于4个的入参,超过4个用堆保存。
汇编无非就是操作寄存器并通过寄存器操作内存的过程。
分析指令关键看调用代码bl blx 跳转指令B
在所arm指令前先说说smali代码,smali是给dalvik vm使用的代码,而dvm类似于jvm,所以smali类似于在dalvik上跑的伪汇编代码

比如
java代码 a a1=b(123);
java代码之所以属于高级语言,容易读取,是因为到处用到了人可以识别的命名,并且把能合并参数的合并 能省略类型、包名的省略,再加上封装 所以得到了易读取的java

转换成smali代码(#为注解)

const-string v0,"123"#v0=123
	invoke-virtual {v0} L包名/包名/包名/类名;->b(Ljava/lang/String;)Ljava/lang/String;
	move-result-object v0


我们并没有看见a1这个名字是给人看的,我们看见的都是v0等等的虚拟寄存器,为什么叫虚拟寄存器因为只有虚拟机才能识别

转换成arm汇编(;为注解)
mov r0 123;r0=123
bl b();跳转到b并将r0传入
mov r1 r0 ;将返回值保存在r1中
在汇编代码里都是隐性的传入参数 隐性的返回,用到更多的寄存器和内存堆栈和地址,可读性更差

最终完整分析Java_com_jni_Jni_getEncryptString函数

.text:000010A4 ; =============== S U B R O U T I N E =======================================
.text:000010A4
.text:000010A4 ; r0 env
.text:000010A4 ; r1 obj
.text:000010A4 ; r2 input
.text:000010A4 ; r3 bool
.text:000010A4 ; r4 - r7 为结局变量声明,就是说后面要用临时用到这些个寄存器
.text:000010A4
.text:000010A4 ; int __fastcall Java_com_jni_Jni_getEncryptString(_JNIEnv *a1, JNINativeInterface *a2, int a3, int a4)
.text:000010A4                 EXPORT Java_com_jni_Jni_getEncryptString
.text:000010A4 Java_com_jni_Jni_getEncryptString
.text:000010A4
.text:000010A4 s               = -0x828
.text:000010A4 var_824         = -0x824
.text:000010A4 dest            = -0x81C
.text:000010A4
.text:000010A4                 PUSH    {R4-R7,LR}
.text:000010A6                 LDR     R6, =(__stack_chk_guard_ptr - 0x10B0) ; 栈保护代码忽略
.text:000010A8                 LDR     R4, =0xFFFFF7EC
.text:000010AA                 MOVS    R5, R0          ; 保存env
.text:000010AC                 ADD     R6, PC ; __stack_chk_guard_ptr
.text:000010AE                 LDR     R6, [R6] ; __stack_chk_guard
.text:000010B0                 ADD     SP, R4
.text:000010B2                 MOVS    R4, R2          ; 保存input
.text:000010B4                 LDR     R2, =0x80C      ; 80c
.text:000010B6                 STR     R3, [SP,#0x828+var_824] ; 存bool
.text:000010B8                 LDR     R3, [R6]
.text:000010BA                 ADD     R2, SP          ; sp+80c
.text:000010BC                 STR     R3, [R2]        ; sp+80c=栈保护
.text:000010BE                 LDR     R3, =(g_env_ptr - 0x10C4)
.text:000010C0                 ADD     R3, PC ; g_env_ptr
.text:000010C2                 LDR     R3, [R3] ; g_env
.text:000010C4                 STR     R0, [R3]        ; g_env = env 把env放进[r3]目的不知道
.text:000010C6                 BL      initAddStr      ; 调用iniAddStr
.text:000010C6                                         ; nop
.text:000010CA                 MOVS    R1, R4          ; 取出input
.text:000010CC                 STR     R0, [SP,#0x828+s] ; 保存上一个函数的返回值salt到栈s,s=salt
.text:000010CE                 MOVS    R0, R5          ; 取出env
.text:000010D0                 BL      jstringTostring ; r0 env
.text:000010D0                                         ; r1 =r4=r2=input
.text:000010D0                                         ; jstringTostring(env,input)
.text:000010D4                 MOVS    R7, R0          ; 保存*input
.text:000010D6                 LDR     R0, [SP,#0x828+s] ; s
.text:000010D8                 BLX     strlen          ; r0 salt
.text:000010D8                                         ; 求盐长
.text:000010DC                 MOVS    R4, R0          ; 存盐长
.text:000010DE                 MOVS    R0, R7          ; s
.text:000010E0                 BLX     strlen          ; r0 *input
.text:000010E0                                         ; 求入长
.text:000010E0                                         ; 将7ff存到堆为什么不存在寄存器里目的是方便后面调用
.text:000010E4                 LDR     R2, =0x7FF      ; n
.text:000010E6                 ADDS    R0, R0, R4      ; 盐长加入长
.text:000010E8                 MOVS    R4, R6
.text:000010EA                 MOVS    R6, #0x29C      ; 偏移量668,即就是NewStringUTF
.text:000010EE                 CMP     R0, R2          ; 总长比0x7ff
.text:000010F0                 BLS     loc_10FC        ; <=则跳转
.text:000010F2                 LDR     R3, [R5]
.text:000010F4                 MOVS    R0, R5          ; env
.text:000010F6                 MOVS    R1, R7          ; *input
.text:000010F8                 LDR     R3, [R3,R6]
.text:000010FA                 B       loc_1122        ; 跳转到NewStringUTF
.text:000010FC ; ---------------------------------------------------------------------------
.text:000010FC
.text:000010FC loc_10FC                                ; CODE XREF: Java_com_jni_Jni_getEncryptString+4Cj
.text:000010FC                 MOVS    R1, #0          ; c
.text:000010FE                 ADD     R0, SP, #0x828+dest ; s
.text:00001100                 BLX     memset          ; r0 &dest
.text:00001100                                         ; r1 0
.text:00001100                                         ; r2 0x7ff
.text:00001100                                         ; 上一个函数的返回值不用所以直接r0=&dest
.text:00001104                 ADD     R0, SP, #0x828+dest ; dest
.text:00001106                 MOVS    R1, R7          ; src
.text:00001108                 BLX     strcat          ; r0 &dest
.text:00001108                                         ; r1 *input
.text:0000110C                 LDR     R3, [SP,#0x828+var_824] ; 取bool
.text:0000110E                 CMP     R3, #0
.text:00001110                 BEQ     loc_111A        ; 真则跳转
.text:00001112                 ADD     R0, SP, #0x828+dest ; dest
.text:00001114                 LDR     R1, [SP,#0x828+s] ; src
.text:00001116                 BLX     strcat          ; 加入盐
.text:0000111A
.text:0000111A loc_111A                                ; CODE XREF: Java_com_jni_Jni_getEncryptString+6Cj
.text:0000111A                 LDR     R3, [R5]
.text:0000111C                 MOVS    R0, R5          ; env
.text:0000111E                 ADD     R1, SP, #0x828+dest
.text:00001120                 LDR     R3, [R3,R6]
.text:00001122
.text:00001122 loc_1122                                ; CODE XREF: Java_com_jni_Jni_getEncryptString+56j
.text:00001122                 BLX     R3              ; 跳转到NewStringUTF
.text:00001124                 LDR     R3, =0x80C
.text:00001126                 ADD     R3, SP
.text:00001128                 LDR     R2, [R3]
.text:0000112A                 LDR     R3, [R4]
.text:0000112C                 CMP     R2, R3          ; v15 = _stack_chk_guard
.text:0000112E                 BEQ     loc_1134
.text:00001130                 BLX     __stack_chk_fail
.text:00001134 ; ---------------------------------------------------------------------------
.text:00001134
.text:00001134 loc_1134                                ; CODE XREF: Java_com_jni_Jni_getEncryptString+8Aj
.text:00001134                 LDR     R3, =0x814
.text:00001136                 ADD     SP, R3
.text:00001138                 POP     {R4-R7,PC}
.text:00001138 ; End of function Java_com_jni_Jni_getEncryptString
.text:00001138
.text:00001138 ; ---------------------------------------------------------------------------
.text:0000113A                 ALIGN 4
.text:0000113C off_113C        DCD __stack_chk_guard_ptr - 0x10B0
.text:0000113C                                         ; DATA XREF: Java_com_jni_Jni_getEncryptString+2r
.text:00001140 dword_1140      DCD 0xFFFFF7EC          ; DATA XREF: Java_com_jni_Jni_getEncryptString+4r
.text:00001144 dword_1144      DCD 0x80C               ; DATA XREF: Java_com_jni_Jni_getEncryptString+10r
.text:00001144                                         ; Java_com_jni_Jni_getEncryptString+80r
.text:00001148 off_1148        DCD g_env_ptr - 0x10C4  ; DATA XREF: Java_com_jni_Jni_getEncryptString+1Ar
.text:0000114C ; size_t n
.text:0000114C n               DCD 0x7FF               ; DATA XREF: Java_com_jni_Jni_getEncryptString+40r
.text:00001150 dword_1150      DCD 0x814               ; DATA XREF: Java_com_jni_Jni_getEncryptString:loc_1134r
.text:00001154                 CODE32

举报

相关推荐

0 条评论