0x01前言
马上开学了,整理一下假期自己研究的成果,希望在安卓逆向分析这条路上能走得更远。
声明:文章仅供技术研究,切勿非法利用!
0x02功能协议分析
app登录后进入appCenter的界面,内有九个图标
1.“我的成绩”协议分析
用fd抓包工具,抓取手机的http和https协议。
这里我登录完帐号后,点击“我的成绩”按钮,fd抓到了这个url请求。
发现这是一个GET包,我把链接地址贴出来,uid部分隐藏
http://ydjwapp.hznu.edu.cn/login_sso.aspx?procode=002&type=1&choice=XS0205&uid=20162104♦♦♦♦♦&role=XS&key=C3840551F78846DFAABA84C9A81B4F06&time=1520081445
2.分析url的参数
没错,key这个参数格外引人注目,前面的无非是uid不同,choice之类的是成绩查询的代号,time这个参数和现行时间戳很像,但是位数不够,等会分析。
0x03深入APK
首先用APK改之理载入分析我们分析的这个app
1.参数key的寻找
搜索结果太多了,于是我换个思路,上面既然是GET请求,那么会不会有&key这个常量的保存呢?于是很巧,找到了一处我们要找的关键地方
2.进入这处的java源码
贴图部分为openURL()的部分源码,但是可以清除看到里面str1基本上就是刚才提交url的地址了。因为里面包含了procode,choice,uid,key,time这些参数,而这些参数正是上面url的get请求数据部分。
这里贴上openURL()的全部代码:
private void openURL()
{
this.time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String str3 = this.url + "&" + "time=" + this.time;
Object localObject = getMapParam(this.url);
if (str3.indexOf("?") != -1)
{
str1 = (String)((Map)localObject).get("procode");
String str2 = (String)((Map)localObject).get("choice");
localObject = (String)((Map)localObject).get("uid");
if ((str1 != null) && (str1.equals("002")))
{
str3 = String.valueOf(new Date().getTime()).substring(0, 10);
Log.e("@@@@@@@@@@@@@@@@@@@@", String.valueOf(str3));
str1 = o.a(str1 + str2 + (String)localObject + "DAFF8EA19E6BAC86E040007F01004EA" + str3);
Log.e("@@@@@@@@@@@@@@@@@@@@", str1);
str1 = this.url + "&" + "key=" + str1 + "&" + "time=" + str3;
openUrlWithWebview(this.detailView, str1);
Log.e("WebModuleOaActivity", str1);
return;
}
if ((str1 != null) && (str1.equals("006")))
{
str1 = p.a(str1 + str2 + (String)localObject + this.time + "DAFF8EA19E6BAC86E040007F01004EA");
Log.e("snstimepublickey", str1);
str1 = new StringBuilder(String.valueOf(this.url)).append("&").append("key=").append(str1).append("&").append("time=").append(this.time).toString() + "&tgc=" + t.a(getApplicationContext(), "catgc", "tgc");
openUrlWithWebview(this.detailView, str1);
Log.e("WebModuleOaActivity", str1);
return;
}
str1 = str3 + "&tgc=" + t.a(getApplicationContext(), "catgc", "tgc");
openUrlWithWebview(this.detailView, str1);
Log.e("WebModuleOaActivity", str1);
return;
}
String str1 = this.url + "&tgc=" + t.a(getApplicationContext(), "catgc", "tgc");
openUrlWithWebview(this.detailView, str1);
Log.e("WebModuleOaActivity", str1);
}
0x04HOOK关键函数
1.找到关键函数
PartⅠ. a()方法
Part Ⅱ . openUrlWithWebview()方法
2.写hook模块准备拦截上述两个函数的传参和返回值
static int i = 1;
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
if(!loadPackageParam.packageName.equals("com.hzsfdx.jw")){
return;
}
XposedBridge.log("Load app:----->"+loadPackageParam.packageName+" 加载完毕");
findAndHookMethod("com.zfsoft.core.d.o", loadPackageParam.classLoader, "a", String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("----第"+(i++)+"次拦截----");
XposedBridge.log("a方法 传入参数:"+param.args[0]);
XposedBridge.log("a方法 返回参数:"+param.getResult());
}
});
findAndHookMethod("com.zfsoft.webmodule.controller.WebModuleOaFun", loadPackageParam.classLoader, "openUrlWithWebview", WebView.class, String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("openUrlWithWebview方法 传入参数:"+param.args[1]);
}
});
}
3.在模拟器中测试查看拦截数据
str1 | 002 |
---|---|
str2 | XS0205 |
(String)localObject | 20162104♦♦♦♦♦ |
const str | DAFF8EA19E6BAC86E040007F01004EA |
time | 1520088144 |
----->com.hzsfdx.jw 加载完毕
----第1次拦截----
a方法 传入参数:002XS020520162104♦♦♦♦♦DAFF8EA19E6BAC86E040007F01004EA1520088144
a方法 返回参数:ECC9D8F62A45091ED3EA35B691105318
openUrlWithWebview方法 传入参数:
http://ydjwapp.hznu.edu.cn/login_sso.aspx?procode=002&type=1&choice=XS0205&uid=20162104♦♦♦♦♦&role=XS&key=ECC9D8F62A45091ED3EA35B691105318&time=1520088144
----第2次拦截----
a方法 传入参数:002XS020820162104♦♦♦♦♦DAFF8EA19E6BAC86E040007F01004EA1520088165
a方法 返回参数:818348171E471F392C23CBFF99EA3E36
openUrlWithWebview方法 传入参数:
http://ydjwapp.hznu.edu.cn/login_sso.aspx?procode=002&type=1&choice=XS0208&uid=20162104♦♦♦♦♦&role=XS&key=818348171E471F392C23CBFF99EA3E36&time=1520088165
----第3次拦截----
a方法 传入参数:002XS020420162104♦♦♦♦♦DAFF8EA19E6BAC86E040007F01004EA1520088169
a方法 返回参数:1CE6D9F428DF5294E66D88C0653574BC
openUrlWithWebview方法 传入参数:
http://ydjwapp.hznu.edu.cn/login_sso.aspx?procode=002&type=1&choice=XS0204&uid=20162104♦♦♦♦♦&role=XS&key=1CE6D9F428DF5294E66D88C0653574BC&time=1520088169
0x05核心加密算法
1.明确加密方法a()
借用上面第一次a方法拦截到的参数
002XS020520162104♦♦♦♦♦DAFF8EA19E6BAC86E040007F01004EA1520088144
可知这个参数包含5个参数,进入到o这个类中的a()加密运算后返回给str1,后面str1再拼接成完整的GET请求的url。
2.用Elipse模拟a()方法
来到com.zfsoft.core.d.o这个类,在Elipse中新建类,命名为o,拷贝通过jd-gui反编译过来的java源码,会发现很多处不合理的代码,如:
if (paramInt >= j)
{
a(e, paramArrayOfByte, i, 0, j);
a(e);
i = j;
if (i + 63 >= paramInt)
{
j = i;
i = k;
}
}
for (;;)
{
a(e, paramArrayOfByte, i, j, paramInt - j);
return; //这里return显然不合理,后面的操作会影响结果的。
a(arrayOfByte, paramArrayOfByte, 0, i, 64);
a(arrayOfByte);
i += 64;
break;
j = 0;
}
这里我是用到了JEB 1.5强大的反编译工具,
上面的代码变成了如下:
if(paramInt >= j) {
o.a(o.e, paramArrayOfByte, i, 0, j);
o.a(o.e);
for(i = j; i + 63 < paramInt; i += 64) {
o.a(arrayOfByte, paramArrayOfByte, 0, i, 64);
o.a(arrayOfByte);
}
}
else {
int v10 = i;
i = 0;
k = v10;
}
o.a(o.e, paramArrayOfByte, k, i, paramInt - i);
可读性变强了很多,这里要修改的地方还有别的三处,就不一一陈列了,当时调试了一个下午,才改完的。
3.执行Main函数测试a方法
public static void main(String[] args) {
System.out.println(o.a("002XS020520162104♦♦♦♦♦DAFF8EA19E6BAC86E040007F01004EA1520088144"));
}
前面记录的数据:
a方法 传入参数:002XS020520162104♦♦♦♦♦DAFF8EA19E6BAC86E040007F01004EA1520088144
a方法 返回参数:ECC9D8F62A45091ED3EA35B691105318
测试成功截图:
0x06实战协议
通过学号和当前时间戳取前十位以及前面的固定参数做一个key的加密计算,
拼接url提交网页即可访问到正确的结果。
0x07总结
明确关键位置,Hook拦截传参,以及加密结果返回值,能为我们更好分析部分参数的生成以及提交GET请求参数的拼接。
后面要学习安卓动态调试了,Hook要先放一段落。
加油,每天进步一点点!
今天开学第一天,2018年3月5日 14:38:08。
安卓逆向,我在路上!