吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 31117|回复: 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] 纯文本查看 复制代码
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] 纯文本查看 复制代码
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] 纯文本查看 复制代码
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++] 纯文本查看 复制代码
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++] 纯文本查看 复制代码
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++] 纯文本查看 复制代码
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] 纯文本查看 复制代码
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] 纯文本查看 复制代码
 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, 2024-11-15 16:38

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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