APP登陆协议之Native层分析
来源:互联网 发布:马赛克视频还原软件 编辑:程序博客网 时间:2024/05/22 16:49
1、整体分析
首先登陆,抓包分析,如下:
其中密码为:123456
可以看到,对密码进行了加密。
2、加密算法java层分析
定位到java代码如下:
protected void buildSignRequestParams(String paramString, HashMap<String, String> paramHashMap) { paramHashMap.put("username", this.a); try { this.b = GameCenterNative.DesCbcEncrypt(this.b); paramHashMap.put("password", this.b); paramHashMap.put("deviceIdentifier", gc.a().getUniqueID()); paramHashMap.put("captcha", this.d); paramHashMap.put("captchaId", this.c); paramHashMap.put("info", "1"); long l = gf.a().getDateline(); paramHashMap.put("dateline", "" + l / 1000L); return; } catch (Error paramString) { for (;;) { paramString.printStackTrace(); } } }
可以看到调用了GameCenterNative.DesCbcEncrypt对密码进行了加密,但是没有看到sign签名的计算过程。看下该类,如下:
public class jr extends SignDataProvider
继承自SignDataProvider类,来到该类,如下:
public void buildRequestParams(String paramString, RequestParams paramRequestParams) { HashMap localHashMap = new HashMap(); buildSignRequestParams(paramString, localHashMap); paramString = new ArrayList(localHashMap.keySet()); Collections.sort(paramString); StringBuilder localStringBuilder = new StringBuilder(); int i = 0; while (i < paramString.size()) { String str1 = (String)paramString.get(i); String str2 = (String)localHashMap.get(str1); if (!TextUtils.isEmpty(str2)) { paramRequestParams.put(str1, str2); localStringBuilder.append(str2); } i += 1; } paramRequestParams.put("sign", buildSignValue(localStringBuilder.toString())); } public abstract void buildSignRequestParams(String paramString, HashMap<String, String> paramHashMap);
看到没有,里面有sign的签名算法,在计算sign之前,调用了子类的buildSignRequestParams方法。而sign签名算法为buildSignValue,如下:
protected String buildSignValue(String paramString) { try { paramString = GameCenterNative.getServerApi(paramString); return paramString; } catch (UnsatisfiedLinkError paramString) { paramString.printStackTrace(); } return ""; }
可以看到最终调用了GameCenterNative.getServerApi(paramString);
下面来看下上面提到的对密码加密的算法GameCenterNative.DesCbcEncrypt和对sign签名的算法GameCenterNative.getServerApi(paramString);如下:
public static final native String DesCbcEncrypt(String paramString);public static final native String getServerApi(String paramString);
可以,看到这两个加密函数在native层进行了实现。
下面,我们跟进native层看看。
2、加密算法native层分析
用IDA打开so文件,看下导出函数,发现没有java_等函数,所有猜测,此处应该采用了动态注册。找到JJNI_OnLoad,如下
可以看到动态注册过程,看下注册数组,如下:
注册过程,就分析到此为止,下面继续分析,上面说的那两个加密函数。
1、DesCbcEncrypt()函数分析:
对应的汇编代码,如下
由于DesCbcEncrypt参数应为(JNIEnv *env, jobject jobj, jobject input),通过汇编代码可知,输入待加密的字符串放到类R1寄存器中
其中 COMMON_KEY为 “u!~#7@w0”
F5后的代码为:
int __fastcall des_encrypt(JNIEnv_ *a1, jobject obj, char *a3){ JNIEnv_ *env; // r9@1 char *commonkey; // r10@1 int result; // r0@2 const char *v6; // r0@3 const char *v7; // r8@3 signed int v8; // r0@4 signed int v9; // r6@4 int v10; // r5@5 void *v11; // r0@7 void *inputBuffer; // r7@7 size_t v13; // r0@8 void *v14; // r4@8 void *v15; // r5@9 int v16; // r4@10 env = a1; commonkey = a3; if ( obj && (v6 = GetStringUnicodeChars(a1, obj, "utf-8"), (v7 = v6) != 0) && ((v8 = strlen(v6), v9 = v8, !(v8 << 29)) ? (v10 = v8 + 8) : (v10 = ((v8 + (v8 < 0 ? 7 : 0)) & 0xFFFFFFF8) + 8), (v11 = calloc(v10, 1u), (inputBuffer = v11) != 0) && (memset(v11, 8 - v9 % 8, v10), v13 = strlen(v7), memcpy(inputBuffer, v7, v13), v14 = calloc(v10 + 1, 1u), memset(v14, 0, v10 + 1), v14) && (crypt(inputBuffer, v14, v10, commonkey, 1), v15 = php_base64_encode(v14, v10, 0), free(v14), free(inputBuffer), free(v7), v15)) ) { v16 = (env->functions->NewStringUTF)(env, v15); free(v15); result = v16; } else { result = 0; } return result;}
下面来分析des_encrypt函数,流程为:
A、调用GetStringUnicodeChars以UTF-8得到输入的字符串,其中参数分别为R0 : JNIEnv_ * env , R1 : jobject input, char* utf-8
整理后,代码如下:
void *__fastcall GetStringUnicodeChars(JNIEnv_ *a1, jobject a2, char *a3){ jobject obj; // r6@1 JNIEnv_ *v4; // r4@1 char *str; // r5@1 jclass clazz; // r0@1 jmethodID getBytes; // r7@1 jstring inputStr; // r0@1 int v9; // r6@1 signed int v10; // r5@1 const void *v11; // r8@1 void *v12; // r7@2 obj = a2; v4 = a1; str = a3; clazz = (a1->functions->FindClass)(); getBytes = (v4->functions->GetMethodID)(v4, clazz, "getBytes", "(Ljava/lang/String;)[B"); inputStr = (v4->functions->NewStringUTF)(v4, str); v9 = (v4->functions->CallObjectMethod)(v4, obj, getBytes, inputStr); v10 = (v4->functions->GetArrayLength)(v4, v9); v11 = (v4->functions->GetByteArrayElements)(v4, v9, 0); if ( v10 <= 0 ) { v12 = 0; } else { v12 = malloc(v10 + 1); memcpy(v12, v11, v10); *(v12 + v10) = 0; } (v4->functions->ReleaseByteArrayElements)(v4, v9, v11, 0); return v12;}
B、调用crypt进行加密,整理后的代码如下:
int __fastcall crypt(char *input, int a2, int a3, char *commonkey, int a5){ char *key; // r6@1 char *inputStr; // r9@1 int encodeBuffer; // r8@1 int inputStrLen; // r7@1 size_t keyLength; // r0@1 int result; // r0@1 char keySchedule; // [sp+Ch] [bp-B4h]@1 char keyEncrypt; // [sp+8Ch] [bp-34h]@1 int ivec; // [sp+94h] [bp-2Ch]@1 signed int v14; // [sp+98h] [bp-28h]@1 int v15; // [sp+9Ch] [bp-24h]@1 key = commonkey; inputStr = input; encodeBuffer = a2; inputStrLen = a3; v15 = _stack_chk_guard; keyLength = strlen(commonkey); memcpy(&keyEncrypt, key, keyLength); DES_set_key_unchecked(&keyEncrypt, &keySchedule); ivec = 2018915346; v14 = -271733872; result = DES_ncbc_encrypt(inputStr, encodeBuffer, inputStrLen, &keySchedule, &ivec, a5); if ( v15 != _stack_chk_guard ) _stack_chk_fail(result); return result;}
可以看到采用类openssl库进行的加密。
C、用php_base64_encode对加密后字符串进行base64加密,参数为R0 : DES加密后的字符串,R1 : 字符串长度 R3: 0
_BYTE *__fastcall php_base64_encode(char *DESencodeStr, signed int strLength, _DWORD *a3){ char *v3; // r5@1 signed int v4; // r6@1 _DWORD *v5; // r7@1 _BYTE *result; // r0@2 _BYTE *i; // r4@2 _BYTE *v8; // r3@5 unsigned int v9; // r3@6 char *v10; // r12@6 unsigned int v11; // r3@6 char v12; // r1@6 char *v13; // r12@6 unsigned int v14; // r3@6 char v15; // r1@6 unsigned int v16; // r12@8 unsigned int v17; // r2@8 int v18; // r12@8 unsigned int v19; // r5@9 char v20; // r1@9 v3 = DESencodeStr; v4 = strLength; v5 = a3; if ( (strLength + 2 < 0) ^ __OFADD__(strLength, 2) ) { result = 0; if ( a3 ) *a3 = 0; else result = 0; } else { result = malloc(4 * ((strLength + 2) / 3) + 1); for ( i = result; ; *(i - 1) = v14 ) { v8 = i; if ( v4 <= 2 ) break; v9 = *v3; v4 -= 3; v3 += 3; i += 4; v10 = &aAbcdefghijklmn[16 * (v9 & 3)]; *(i - 4) = aAbcdefghijklmn[v9 >> 2]; v11 = *(v3 - 2); v12 = v10[v11 >> 4]; v13 = &aAbcdefghijklmn[4 * (v11 & 0xF)]; *(i - 3) = v12; v14 = *(v3 - 1); v15 = v13[v14 >> 6]; LOBYTE(v14) = aAbcdefghijklmn[v14 & 0x3F]; *(i - 2) = v15; } if ( v4 ) { v16 = *v3; v17 = v16 >> 2; v18 = v16 & 3; *i = aAbcdefghijklmn[v17]; if ( v4 == 2 ) { v19 = v3[1]; v8 = i + 4; i[3] = 61; v20 = aAbcdefghijklmn[4 * (v19 & 0xF)]; i[1] = *(&aAbcdefghijklmn[16 * v18] + (v19 >> 4)); i[2] = v20; } else { v8 = i + 4; i[1] = aAbcdefghijklmn[16 * v18]; i[2] = 61; i[3] = 61; } } if ( v5 ) *v5 = v8 - result; *v8 = 0; } return result;}
2、getServerApi()函数分析:
汇编代码如下:
待加密的参数传递到了R1寄存器
可以看到采用了MD5加密,MD5_SERVER_KEY为”ef2vx#sf*^FlklSD*9sdf(m$&qw%d7po”
进行分析:
分析之前,需要说明下,IDA有时识别不正确,如下:
才是,可以右键 强制转换
可以得到
在介绍完技巧后,下面开始分析 md5_string函数,整理后代码如下:
char *__fastcall md5_string(JNIEnv_ *a1, char *input, const char *key){ const char *commonkey; // r7@1 JNIEnv_ *env; // r5@1 char *inputStr; // r9@1 const char *v6; // r6@2 size_t v7; // r4@4 size_t v8; // r0@4 char *v9; // r4@4 size_t v10; // r0@5 size_t v11; // r0@7 int v12; // r8@7 int v13; // r3@8 char *result; // r0@11 char v15[16]; // [sp+8h] [bp-B8h]@7 char s; // [sp+18h] [bp-A8h]@7 char v17; // [sp+3Ch] [bp-84h]@7 int v18; // [sp+94h] [bp-2Ch]@1 commonkey = key; env = a1; inputStr = input; v18 = _stack_chk_guard; if ( input ) v6 = a1->functions->GetStringUTFChars(&a1->functions, input, 0); else v6 = &unk_DA24; v7 = strlen(v6); v8 = strlen(commonkey); v9 = calloc(v8 + v7 + 2, 1u); if ( v9 ) { v10 = strlen(v6); strncpy(v9, v6, v10 + 1); if ( commonkey ) strcat(v9, commonkey); memset(&s, 0, 33u); memset(&v17, 0, 0x58u); MD5Init(&v17); v11 = strlen(v9); MD5Update(&v17, v9, v11); memset(v15, 0, 0x10u); v12 = 0; MD5Final(v15, &v17); do { v13 = v15[v12++]; sprintf(&s, "%s%02x", &s, v13); } while ( v12 != 16 ); free(v9); if ( inputStr ) (env->functions->ReleaseStringUTFChars)(env, inputStr, v6); result = (env->functions->NewStringUTF)(env, &s); } else { result = inputStr; } if ( v18 != _stack_chk_guard ) _stack_chk_fail(result); return result;}
代码比较好理解,主要采用类openssl库,并把传入的钥匙作为盐,进行了加密。
好了,分析到此结束吧!
- APP登陆协议之Native层分析
- APP登陆协议的分析
- Binder源码分析之Native层
- Webkit之App层的分析
- Binder源码分析之Native层(原)
- Binder源码分析之Native层(原)
- qq2007登陆协议分析
- CC2541之SimpleBLEPeripheral程序流程分析 -- 02.App层初始化
- CC2541之SimpleBLEPeripheral程序流程分析 -- 03.App层事务处理
- 传输层安全协议抓包分析之SSL/TLS
- 传输层安全协议抓包分析之SSL/TLS
- Camera APP层分析之对camera framework层封装解析
- 应用层协议分析模型
- 应用层协议分析-HTTP
- Web App和Native App之争
- 【协议分析】Web QQ登陆验证分析
- WEB APP、HYBRID APP与NATIVE APP 差异分析
- bluez 协议栈实现3 应用层的协议栈实现分析之dbus
- MySQL function创建
- Java 多线程 学习笔记(二)停止线程的几种方法
- JSON java fastJson
- 选择器
- Java 多线程学习笔记(三)-守护线程
- APP登陆协议之Native层分析
- Android新浪微博开发(三)完结篇之调用新浪微博API实现信息展示
- 最长回文子串
- 3、区间重合判断
- MyBatis的其它方法
- Nginx学习历程_3_配置Nginx打开目录浏览功能
- Java 多线程学习笔记(四)yield 介绍
- sql语句学习
- Java 多线程学习笔记(五)synchronized 锁重入