吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 31470|回复: 106
收起左侧

[Android 原创] 恋恋加密算法解析

  [复制链接]
无名侠 发表于 2016-4-20 17:30
本帖最后由 无名侠 于 2016-4-20 21:35 编辑

今天发烧请假回家,睡了一觉起来精神大好,就把恋恋APP的加密算法分析了一下,请各位大神勿喷。
平时也就靠这些接一点私活。

恋恋登陆的数据包是被加密过,Fiddler抓包如下(篇幅有限,删去HTTP头的大部分数据):
[HTML] 纯文本查看 复制代码
POST http://mob.imlianai.com/call.do?cmd=mobileUser.login HTTP/1.1
 
WbbJnSJh63FYTNGmBk5J+lGOOQ1PzYWveRIJF8F//3Wo576FuH0Kyhb9VG/UpFHkICtAQcQAReUcUYLjsJ4sozorNeprDwcahSJs7D3f2YJFWWNtlwJlqXmwIPHUz0mHPZiJqJJNSYmGr2EsfPHz6FtnagDzHP4eXQlsZMyRKOOrRgyigyTRYQ==


把APP拖入到Ak,然后搜索mobileUser.login
搜索结果有两项,第一个里面有很多url,应该不是。
第二个的类名是com.a.a.a.b.t;
在这个类中找到如下代码:

[Java] 纯文本查看 复制代码
1
2
3
4
5
6
7
JSONObject localJSONObject = new JSONObject();
      localJSONObject.put("uid", paramString1);
      localJSONObject.put("loginKey", paramString2);
      paramString1 = com.a.a.a.f.a.a(localJSONObject.toString()).getBytes();
      this.b.a(paramString1);
      this.b.c();
      return;


推测未加密的数据包应该采用的json格式。
com.a.a.a.f.a.a  应该是对数据的进一步处理,可能是加密部分,跟进去一看,八九不离十了。

[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package com.a.a.a.f;
import com.jni.Jni;
import java.security.Key;
import javax.crypto.spec.IvParameterSpec;
public class a
{
  private static final byte[] a = { 1, 2, 3, 4, 5, 6, 7, 8 };
  private static final IvParameterSpec b = new IvParameterSpec(a);
  private static String c = "hqi/FjjcBxA=";
  private static Key d = null;
  public static String a(String paramString)
  {
    return Jni.getInstance().encryptString(paramString);
  }
}


在这个类中调用了Jni的encryptString负责加密。继续反编译Jni类。
我已经给出大部分代码的注释。


[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
package com.jni;
 
public class Jni
{
  private static final int blockLength = 500;
  private static String hexString = "0123456789ABCDEF";
  private static Jni myJni;
   
  static
  {
    System.loadLibrary("jni");// jni这个lib中导出getEncryptString函数
  }
   
  private native String getEncryptString(String paramString, boolean paramBoolean);
   
  public static Jni getInstance()
  {
    if (myJni == null) {
      myJni = new Jni();
    }
    return myJni;
  }
   
  public String encode(String paramString) // 这个函数的功能是把paramString转换成16进制的数据文本
  {
    paramString = paramString.getBytes();
    StringBuilder localStringBuilder = new StringBuilder(paramString.length * 2);
    int i = 0;
    for (;;)
    {
      if (i >= paramString.length) {
        return localStringBuilder.toString();
      }
      localStringBuilder.append(hexString.charAt((paramString[i] & 0xF0) >> 4));
      localStringBuilder.append(hexString.charAt((paramString[i] & 0xF) >> 0));
      i += 1;
    }
  }
 
[/i][/i][i] public String encryptString(String arg9) {[/i]
[i]        String v0_1;[/i]
[i]        if(arg9 == null || arg9.length() == 0) {[/i]
[i]            v0_1 = "";[/i]
[i]        }[/i]
[i]        else {[/i]
[i]            String v2 = this.encode(arg9);//把arg9转换为16进制文本[/i]
[i]            int v3 = v2.length();[/i]
[i]            StringBuffer v4 = new StringBuffer();[/i]
[i]            if(v3 < 500) {[/i]
[i]                v4.append(this.getEncryptString(v2, true));//通过getEncryptString对字符串编码[/i]
[i]            }[/i]
[i]            else {[/i]
[i]                int v0;[/i]
[i]                for(v0 = 1; v0 < v3 / 500 + 1; ++v0) {[/i]
[i]                    if(v3 % 500 == 0 && v0 == v3 / 500) {//通过getEncryptString对字符串编码[/i]
[i]                        v4.append(this.getEncryptString(v2.substring((v0 - 1) * 500, v0 * 500), true));[/i]
[i]                        break;[/i]
[i]                    }[/i]
[i]                                        //通过getEncryptString对字符串编码[/i]
 
[i]                    v4.append(this.getEncryptString(v2.substring((v0 - 1) * 500, v0 * 500), false));[/i]
[i]                }[/i]
 
[i]                if(v3 % 500 == 0) {[/i]
[i]                    goto label_14;[/i]
[i]                }[/i]
[i]                                //通过getEncryptString对字符串编码[/i]
[i]                v4.append(this.getEncryptString(v2.substring((v0 - 1) * 500, v3), true));[/i]
[i]            }[/i]
 
[i]        label_14:[/i]
[i]                        //getEncryptString("a", true).substring(0,8)做密匙[/i]
[i]            v0_1 = DESencryption.getEncString(v4.toString(), this.getEncryptString("a", true).substring([/i]
[i]                    0, 8));[/i]
[i]        }[/i]
 
[i]        return v0_1;[/i]
[i]    }[/i]
 
[i][i]

getEncryptString是SO库中的东西,就用IDA来分析。
库的文件名是libjni.so
主函数代码比较清晰,直接F5

[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
int __fastcall Java_com_jni_Jni_getEncryptString(int a1, int a2, int a3, int a4)
{
  _JNIEnv *v4; // r5@1
  int v5; // r4@1
  int v6; // r2@1
  const char *input; // r7@1
  size_t sizeSInit; // r4@1
  _JNIEnv *v9; // r0@2
  char *v10; // r1@2
  jstring (__cdecl *v11)(JNIEnv *, const char *); // r3@2
  int result; // r0@6
  char *s; // [sp+0h] [bp-828h]@1
  int v14; // [sp+4h] [bp-824h]@1
  char dest; // [sp+Ch] [bp-81Ch]@3
  int v16; // [sp+80Ch] [bp-1Ch]@1
 
  v4 = a1;
  v5 = a3;
  v14 = a4;
  v16 = _stack_chk_guard;
  g_env = a1;
  s = initAddStr();                             // 获取一段内置的字符串,具体分析看后文
  input = jstringTostring(v4, v5, v6);          // 转换函数,把java字符串类转换为char *
  sizeSInit = strlen(s);
  if ( strlen(input) + sizeSInit <= 0x7FF )     // 分两种情况:
                                                // 1、输入字符串长度 + 内置字符串长度 小于等于0x7ff
  {
    memset(&dest, 0, 0x7FFu);
    strcat(&dest, input);
    if ( v14 )                                  // 根据Java传递过来的第二个参数(paramBoolean)选择处理方法
      strcat(&dest, s);                         // 如果第二个参数为true,则追加内置字符串
    v9 = v4;
    v10 = &dest;
    v11 = v4->functions->NewStringUTF;
  }
  else                                          // 如果第二个参数为false,输入什么就返回什么
  {
    v9 = v4;
    v10 = input;
    v11 = v4->functions->NewStringUTF;
  }
  result = (v11)(v9, v10);
  if ( v16 != _stack_chk_guard )
    _stack_chk_fail(result);
  return result;
}


再来说说initAddStr()函数。这个函数实际是一个固定的字符串,可以通过动态调试得出,但是还是分析一下它是如何获取的吧。因为它比较有意思,还返回到了Java来获取字符串。

[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
int initAddStr()
{
  int v0; // r0@2
  int v1; // r2@2
 
  if ( !isInit )
  {
    v0 = initInflect(jniStr);
    key = jstringTostring(g_env, v0, v1);
    isInit = 1;
  }
  return key;
}

initInflect 函数回调到Java层的com.Reflect.func函数。
jniStr是一串字符串“/key=i im lianai”

[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
int __fastcall initInflect(int a1)
{
  _JNIEnv *v1; // r5@1
  int v2; // r0@1
  bool v3; // zf@1
  int v4; // r7@1
  JNINativeInterface *v5; // r0@1
  jstring (__cdecl *v6)(JNIEnv *, const char *); // r3@3
  const char *v7; // r1@3
  int v8; // r0@2
  JNINativeInterface *v9; // r3@4
  int v10; // r4@4
  int v12; // [sp+Ch] [bp-1Ch]@1
  v12 = a1;
  v1 = g_env;
  v2 = ((*g_env)->FindClass)(g_env, "com/Reflect");
  v4 = v2;
  v3 = v2 == 0;
  v5 = v1->functions;
  if ( v3 )
  {
    v6 = v5->NewStringUTF;
    v7 = "jclass";
    return (v6)(v1, v7);
  }
  v8 = (v5->GetStaticMethodID)(v1, v4, "func", "(ILjava/lang/String;)Ljava/lang/String;");
  v9 = v1->functions;
  v10 = v8;
  if ( !v8 )
  {
    v6 = v9->NewStringUTF;
    v7 = "method";
    return (v6)(v1, v7);
  }
  (v9->NewStringUTF)(v1, v12);
  return _JNIEnv::CallStaticObjectMethod(v1, v4, v10, 10);
}


再来看看Java的Reflect代码:
[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Reflect
{
  private static String hexString = "0123456789ABCDEF";
  public static String tmp = " alien";
   
  private static String encode(String paramString)//把数据编码为HEX文本
  {
    paramString = paramString.getBytes();
    StringBuilder localStringBuilder = new StringBuilder(paramString.length * 2);
    int i = 0;
    for (;;)
    {
      if (i >= paramString.length) {
        return localStringBuilder.toString();
      }
      localStringBuilder.append(hexString.charAt((paramString[i] & 0xF0) >> 4));
      localStringBuilder.append(hexString.charAt((paramString[i] & 0xF) >> 0));
      i += 1;
    }
  }
  public static String func(int paramInt, String paramString)
  {
    return encode(paramString + tmp);
  }
}


func实际作用就是把输入字符串拼接上“alien”,然后转换HEX数据文本。

实际上,initInflect返回的是“/key=i im lianai alien”的16进制数据文本2F6B65793D6920696D206C69616E616920616C69656E
最后面:
[Java] 纯文本查看 复制代码
1
2
3
public static String getEncString(String paramString1, String paramString2){
paramString1 = DESBase64.encode(getEncCode(paramString1.getBytes("UTF8"), paramString2));
      return paramString1;}

所以最后的数据进行了DESBase64加密,得出了最后的结果。








免费评分

参与人数 29吾爱币 +1 热心值 +30 收起 理由
KaQqi + 1 无名大人
于汉超 + 1 + 1 过来看看
a3362613 + 1 好厉害
794182251 + 1 用心讨论,共获提升!
yushan8603 + 1 用心讨论,共获提升!
曜曜1314 + 1 用心讨论,共获提升!
ZF0806 + 1 热心回复!
FallStop + 1 我很赞同!
lakshmi + 1 热心回复!
469164323 + 1 学习了,无名大大无处不在啊
1204376616 + 2 只能膜拜
sunbeat + 1 不错,请清晰的思路
胡一刀 + 1 我很赞同!
beige + 1 我很赞同!
小宅博士 + 1 我很赞同!
小虾米TC + 1 谢谢@Thanks!
a87399 + 1 赞!
hcloveld + 1 用心讨论,共获提升!
a422 + 1 用心讨论,共获提升!
shelher + 1 用心讨论,共获提升!
诗木 + 1 热心回复!
chenxiansen307 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
Dd_nirvana + 1 用心讨论,共获提升!
Pizza + 1 不愧是无名
SatayKun + 1 热心回复!
瞧黑猫警长 + 1 666赞个
arryboom + 1 我很赞同!
平淡最真 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
我爱吾爱a + 1 热心回复!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

头像被屏蔽
叫我屌炸天 发表于 2016-4-20 17:38
此无名侠非无名侠?
额爱破解 发表于 2016-4-20 18:06
苏波Rose 发表于 2016-4-20 18:08
头像被屏蔽
99888 发表于 2016-4-20 18:17

好厉害啊
poca 发表于 2016-4-21 15:12 来自手机
无名大侠。。。
qtfreet00 发表于 2016-4-22 10:39
顺便补一下样本链接吧
SatayKun 发表于 2016-4-22 11:22
赞一个!!!
吾爱破解旭哥哥 发表于 2016-4-22 11:34
厉害,能出成品吗?
醉城公子 发表于 2016-4-22 11:38
感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-17 20:58

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表