ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2020-01-27 14:01:47  阅读:237  来源: 互联网

标签:R3 恋恋 text 安卓 getEncryptString int env com app


主题:安卓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

 

标签:R3,恋恋,text,安卓,getEncryptString,int,env,com,app
来源: https://www.cnblogs.com/GKLBB/p/12235889.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有