超星学习通逆向接口参数加密分析 - 课程页面切换tab接口
超星的接口分析了很久,分析到这个接口的时候才想到写篇博客,之前的一些接口和分析就没写下来
先开俩坑在这里,等以后再填
1.如何抓安卓APP上HTTPS的包(需ROOT)
2.如何逆向安卓APP
本次分析的接口是这个
http://data.xxt.aichaoxing.com/analysis/course/tab
参数 u=224217004&sign=task&description=%E4%BB%BB%E5%8A%A1&enc=E783557848978DAFBC36732F63E554E9&personid=243104286&classid=49650050&courseid=220816469
接口大致分析
这个接口在课程页面点击tab切换时会被调用一次
先看参数
参数名 | 值 |
---|---|
u | 224217004 |
sign | task |
description | 任务 |
enc | E783557848978DAFBC36732F63E554E9 |
personid | 243104286 |
classid | 49650050 |
courseid | 220816469 |
根据之前抓包的内容可以得到除了enc以外的内容,再看enc长度是32位,可以猜一手MD5
反编译
反编译看下代码
反编译用了MT管理器
脱壳用了Arm Pro
都是要付费的,怎么操作就省略,相信你们都会
用MT管理器搜索接口关键字analysis/course/tab
,只搜索到一个结果,好的开头是成功的一半
点进去看下,反编译成,java代码很明显代码被混淆了,
包名都变成了单个的英文字母,先不管,试试继续分析下去
看到方法上的注解,返回参数还用了泛型,很明显是Retrofit2框架了,enc参数在这里
手机上截图有点小,把代码粘出来
@f(value="analysis/course/tab")
public b<String> a(
@t(value="u") String var1,
@t(value="sign") String var2,
@t(value="description") String var3,
@t(value="enc") String var4,
@t(value="personid") String var5,
@t(value="classid") String var6,
@t(value="courseid") String var7
);
已经找到源头,那看看哪里会调用这个接口就行了
搜索调用这个类的代码,看这个类的包名和类名方法名分别是e.g.u.h2.b、d和a
有好几个同名的重载方法,先不管,搜索smali调用的代码e/g/u/h2/b;->a
找到了20多个结果,不好办
看一下这个方法有7个String类型的参数,超过4个参数smali里面就会用invoke-interface/range
调用
用正则来搜索一下代码invoke-interface/range.*?e/g/u/h2/b/d;->a
结果没减少多少,还是有21个
不过我们知道参数有7个,看哪里有调用7个参数的方法好了,range后面大括号里的就是参数,v1-v6就是6个参数,快速找一下
还是没法找出来,不过还有别的方法,搜一下字符串任务
完全匹配,这个是抓包抓到的参数
运气不错只搜索到2个结果,看了下第一个没看出啥,看第二个,很明显对应的是课程界面的tab,里面还有接口的域名
public void run() throws Throwable {
String string;
String string2;
if (Objects.equals("任务", this.a)) {
string2 = "task";
string = "任务";
} else if (Objects.equals("章节", this.a)) {
string2 = "chapters";
string = "章节";
} else if (Objects.equals("更多", this.a)) {
string2 = "more";
string = "更多";
} else {
string2 = "";
string = "";
}
if (g.b((CharSequence)string)) {
return;
}
String string3 = URLEncoder.encode(string, "utf-8");
string3 = b.a((b)this.f, (String)this.b, (String)string2, (String)string3, (String)this.c, (String)this.d, (String)this.e);
((d)s.a((String)"http://data.xxt.aichaoxing.com/").a(d.class)).a(this.b, string2, string, string3, this.c, this.d, this.e).a((o.d)new a(this));
}
前面的代码就是接口参数里的sign和description,string2就对应参数里的sign了,string对应参数里的description
先看这两行
String string3 = URLEncoder.encode(string, "utf-8");
string3 = b.a(
(b)this.f,
(String)this.b,
(String)string2,
(String)string3,
(String)this.c,
(String)this.d,
(String)this.e
);
看前面代码可以知道这里string是tab的名字,就是任务、章节、更多
这三个其中一个
随后对string进行了一次url编码
最后调用了b.a
方法,传入了7个参数,上下文并不存在名称为b的对象,因此可以断定b是一个类名
查看import,定位到了e.g.k.e.b
这个类
点进去查看一下
public static String a(b b2, String string, String string2, String string3, String string4, String string5, String string6) {
return b2.a(string, string2, string3, string4, string5, string6);
}
private String a(String string, String string2, String string3, String string4, String string5, String string6) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(string5);
stringBuilder.append(string6);
stringBuilder.append(string3);
stringBuilder.append(string4);
stringBuilder.append(string2);
stringBuilder.append(string);
stringBuilder.append("qK`b3XjC");
return m.a((String)stringBuilder.toString()).toUpperCase();
}
调用了b2.a
方法,b2的类型是b,其实就是自身了,传递了6个参数,也就是调用了下面的a方法
可以看到就是按 5 6 3 4 2 1的顺序将参数进行拼接,最后拼上一个盐,最后调用一个m.a
方法
m.a
其实就是一个md5的方法
根据参数顺序,最后可以分析出 string3 = md5(this.e + this.d + string3 + this.c + string2 + this.b + salt)
,其中salt是一个字符串qK`b3XjC
查看代码发现下面调用了接口
((d)s.a((String)"http://data.xxt.aichaoxing.com/").a(d.class)).a(this.b, string2, string, string3, this.c, this.d, this.e).a((o.d)new a(this));
根据接口定义的参数顺序就可以得到这样一个参数对应表
变量名 | 接口参数名 |
---|---|
this.b | u |
string2 | sign |
string | description |
string3 | enc |
this.c | personid |
this.d | classid |
this.e | courseid |
那么可以得到伪代码enc = md5(classid + courseid + URLEncoder.encode(description) + personid + sign + u + salt).toUpperCase()
最后根据以上的分析,编写出对应的代码,验证分析结果
和抓包时得到的参数一致。