人人视频APP登录协议分析

1 背景

已经学习这么长时间了,所以开始实战分析一个经典APP人人9.3.8版本,虽然该软件因缺乏维护已经无法很难进行修改密码等操作,但是其中登录协议的设计还是耐人寻味的,有一定的借鉴意义。

2 使用Fiddler抓取登录的数据包

首先,进入登录界面输入用户名和密码,如图1所示。

图1 输入用户名和密码

点击登录按钮后即可使用Fiddler抓取到具体的数据包,如图2所示。

图2 Fiddler抓取人人登录数据包

通过分析图2中的web表单,我们可以查看到一些字段信息,可以看见提交的字段和服务器返回的字段,我们一般搜索长字段,这样更好定位。Body中的值会发送到服务器,此处发现字段tab_sequence比较少见,我们下一阶段可以在Jadx中分析哪里调用了这个字段。

3 静态分析程序运行流程

3.1 Jdax反编译APK文件

接着,使用Jadx打开人人.apk文件,并全局搜索tab_sequence的调用,最终只是在com.renren.mobile.android.service.ServiceProvider的方法a()中找到了对tab_sequence的调用,如图3所示。

图3 找到tab_sequence的调用

然后,我们通过溯源分析,查看方法a()的调用处,如图4所示,这里有20处调用。

图4 进而查找方法a()的上一级调用

到了这里我们就有两种继续分析的思路,其一是一个个的去查看调用的情况,这样或许可以找到思路;另外也可以使用Android Device Monitor来分析点击登录按钮后调用的方法即方法剖析

3.2 使用DDMS进行方法剖析

首先,我们使用AndroidKiller通过搜索tab_sequence找到方法a()的定义处,然后在开头插入方法剖析的smali代码,如图5所示。

图5 在方法a()的调用处插入方法剖析的smali代码

接着,将编译好的APK文件重新安装到真机中,在图1中的界面中重新输入用户名和密码并点击登录按钮。
另外,在DDMS过滤出只有人人的栏目,最终就能查看到图6中的方法剖析结果。

图6 找到方法调用顺序

通过图6的结果可知,在点击登录按钮之后,方法com.renren.mobile.android.login.LoginRegistView.onClick()调用了方法com.renren.mobile.android.login.LoginUtils.N(),方法com.renren.mobile.android.login.LoginUtils.N()调用了方法com.renren.mobile.android.service.ServiceProvider.a()

3.2 Jadx分析登录协议的具体实现

回到OnClick函数(com.renren.mobile.android.login.LoginRegistView.onClick()),通过分析可知图7中的代码就是点击登录按钮执行的代码。

图7 找到点击登录按钮触发的操作

这里从用户名和密码框取出字符保存在类Variables的属性hJUpassword中,然后调用了N函数(com.renren.mobile.android.login.LoginUtils.N()),进入N函数,如图8所示。

图8 函数N()的逻辑分析

函数N()主要是对用户输入的用户名和密码进行处理,然后将处理后的值作为函数a()的参数进行调用,接下来跟进函数a(),如下面代码所示。

1
2
3
4
5
6
7
8
9
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
81
82
83
84
85
public static void a(final String str, final String str2, int i, String str3, String str4, final Context context, final LoginStatusListener loginStatusListener) {
JsonObject A = A(false, true);
if (str4 != null) {
A.put("rkey", str4);
}
A.put("v", "1.0");
A.put(KSYMediaMeta.IJKM_KEY_FORMAT, "JSON");
A.put("user", str);
A.put("password", str2);
A.put("uniq_id", Variables.IMEI);
A.remove("session_key");
A.put(INetRequest.luP, INetRequest.luQ);
A.put("isverify", (long) i);
A.put("verifycode", str3);
A.put("tab_sequence", 1L);
JsonObject jsonObject = new JsonObject();
jsonObject.put("station_id", Variables.krS);
A.put("ext_info", jsonObject);
String[] keys = A.getKeys();
StringBuilder sb = new StringBuilder();
Vector vector = new Vector();
for (String str5 : keys) {
String jsonValue = A.getJsonValue(str5).toString();
sb.append(str5).append('=').append(URLEncoder.encode(jsonValue)).append('&');
if (jsonValue.length() > 50) {
jsonValue = jsonValue.substring(0, 50);
}
vector.add(str5 + LogHelper.SEPARATE_DOT + jsonValue);
}
String[] strArr = new String[vector.size()];
vector.copyInto(strArr);
A.put("sig", a(strArr, jhS));
AnonymousClass2 r2 = new INetResponse() {
/* class com.renren.mobile.android.service.ServiceProvider.AnonymousClass2 */
private static /* synthetic */ boolean $assertionsDisabled = (!ServiceProvider.class.desiredAssertionStatus());

@Override // com.renren.mobile.net.INetResponse
public final void response(INetRequest iNetRequest, JsonValue jsonValue) {
if (LoginStatusListener.this != null) {
LoginStatusListener.this.b(iNetRequest, jsonValue);
}
if (!$assertionsDisabled && jsonValue == null) {
throw new AssertionError();
} else if (jsonValue instanceof JsonObject) {
JsonObject jsonObject = (JsonObject) jsonValue;
if (!Methods.noError(iNetRequest, jsonObject)) {
long num = jsonObject.getNum("error_code");
String string = jsonObject.getString(BaseObject.ERROR_DESP);
String string2 = jsonObject.getString("click_url");
if (LoginStatusListener.this != null) {
LoginStatusListener.this.b(num, string, string2);
}
if (num == -99 || num == -97) {
Methods.showToast((CharSequence) "无法连接网络,请检查您的手机网络设置...", false);
return;
}
return;
}
Variables.hJU = str;
Variables.password = str2;
Variables.loginType = 0;
try {
((AccountDAO) DAOFactory.getInstance().getDAO(DAOFactory.DAOTYPE.ACCOUNT)).deleteAccount(context, jsonObject.getNum("uid"));
} catch (NotFoundDAOException e) {
e.printStackTrace();
}
ServiceProvider.b(jsonObject, context);
TalkManager.INSTANCE.initUserInfo(RenrenApplication.getContext(), Variables.user_name, Variables.user_id, ServiceProvider.jhT, true);
new StringBuilder("isGuideUser = ").append((int) jsonObject.getNum("is_guide"));
SharedPreferences.Editor edit = PreferenceManager.getDefaultSharedPreferences(context).edit();
edit.putBoolean("is_new_account_login", true);
edit.commit();
ServiceProvider.a(new INetRequest[]{ServiceProvider.iU(true), ServiceProvider.iT(true)}, true);
LoginStatusListener.this.onLoginSuccess();
ServiceProvider.buf();
}
}
};
HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper();
httpRequestWrapper.setUrl(jgH + "/client/login");
httpRequestWrapper.setData(A);
httpRequestWrapper.setResponse(r2);
httpRequestWrapper.setSecretKey(jhS);
HttpProviderWrapper.getInstance().addRequest(httpRequestWrapper);
}

通过分析上面代码可知,方法a()首先将POST表单提交的所有数据都存储在JsonObject类A中,sig是用前面所有字段和secreKey的值进行排序组合后计算MD5值,然后通过HttpRequestWrapper将数据发送到服务器。

4 Android Studio动态调试

首先使用AndroidKiller反编译APK文件,然后使用Android Studio导入反编译出来的工程文件,接着依次设置Sources RootProject SDKRemote JVM Debug,下一步在cmd中输入下列命令

adb shell am start -D -n com.renren.mobile.android/com.renren.mobile.android.ui.WelcomeScreen
adb shell ps | findstr “com.renren.mobile.android”
adb forward tcp:8700 jdwp:上一步的PID值

其次,我们就可以在com.renren.mobile.android.service.ServiceProvide.a()函数的httpRequestWrapper.setData(A);处下断点,然后就能看到JsonObject类A的对象的具体值,如图9所示。

图9 查看JsonObject类A的对象的具体值

我们还可以通过下断点分析出点击登录按钮后具体的程序运行流程,此处略过。

5 总结

这里基本上分析出人人视频的登录协议,首先使用Fiddler分析点击登录按钮后提交的字段,然后使用AndroidKiller和Jadx查看某些特殊的字段,同时使用DDMS进行方法剖析,有时还可能需要使用IDA分析so层的代码,从而找到关键逻辑,最终Hook或者动态调试出Java层或so层的某些字段的值,这也是分析APK文件的常用思路。

6 参考文献

[1]https://bbs.kanxue.com/thread-268451.htm
[2]https://blog.csdn.net/qq_33364733/article/details/100046422
[3]https://blog.csdn.net/YJJYXM/article/details/101678443
[4]https://blog.csdn.net/Yijin_/article/details/102474851


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,可以邮件至 xingshuaikun@163.com。

×

喜欢就点赞,疼爱就打赏