前言
- 本文仅供学习参考,如有侵权,请私信删除。
- 由于版权问题,本文所展示的代码并非完整代码,也不提供相关工具。
初探
拿到APP后,首先就是抓个包看看,到底有哪些参数。结果啥参数也没有,所有请求全部加密~如图所示!
根据抓包,可以发现请求URL格式为:https://slugger.qunar.com/slugger-proxy+服务接口名称,并且POST数据均加密,将POST数据强转为字符串发现前面均为a1907开头。
代码分析定位
- 以截图的请求为例:https://slugger.qunar.com/slugger-proxy?qrt=f_flight_rn_domestic_flightlist,请求中的接口服务名称为f_flight_rn_domestic_flightlist。
- 将APP反编译后在某类中发现如下可疑代码:
public enum FlightServiceMap implements IServiceMap {
FLIGHT_LIST_RN_INLAND("f_flight_rn_domestic_flightlist", "com.mqunar.atom.flight.modules.search.searchforward.entity.CloneableBaseResult");
private final String mClassName;
private Class<? extends BaseResult> mClazz;
private final Class<? extends AbsConductor> mTaskType;
private final String mType;
private FlightServiceMap(String str, String str2) {
this(r7, r8, str, str2, PatchHotdogConductor.class);
}
private FlightServiceMap(String str, String str2, Class<? extends AbsConductor> cls) {
this.mType = str;
this.mClassName = str2;
this.mTaskType = cls;
}
public final String getDesc() {
return this.mType;
}
public final Class<? extends BaseResult> getClazz() {
if (this.mClazz == null) {
try {
this.mClazz = getClass().getClassLoader().loadClass(this.mClassName);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return this.mClazz;
}
public final Class<? extends AbsConductor> getTaskType() {
return this.mTaskType;
}
}
可以看见这段代码中有几个方法,根据这几个方法反跟,在某类中发现如下代码:
public static boolean b(PSearchParams pSearchParams) {
return fa.contains(pSearchParams.serviceMap.getDesc());
}
在继续反跟上面的b方法,在某类中发下如下代码:
public final void b(HotdogConductor hotdogConductor, boolean z) {
if (a.b(pSearchParams)) {
try {
this.d.jsonResult = new String(hotdogConductor.getResult(), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
QLog.d("presearch-remote-onMsgResult", "rn接口-结果:" + this.d.jsonResult, new Object[0]);
}
super.onMsgResult(hotdogConductor, z);
}
到这里发现了可疑代码hotdogConductor.getResult()
,紧接着跟进这个hotdogConductor对象的类中看看,发现了一段加密代码,此时终于感觉到了一丝光明~~
private byte[] encodeContent(byte[] bArr) {
if (bArr == null) {
bArr = new byte[0];
}
return Goblin.sand(bArr);
}
好了,Goblin.sand(bArr)
方法就是加密的,跟进去这个对象类看下,发现是调用的so库(正常操作,一般加密算法都在so库中),以下是该类的完整代码:
public class Goblin {
public static native String SHR();
public static native String d(String str, String str2);
public static native String dPoll(String str);
public static native byte[] da(byte[] bArr);
public static native String dn(byte[] bArr, String str);
public static native byte[] dn1(byte[] bArr, String str);
public static native byte[] drift(byte[] bArr);
public static native String duch(String str);
public static native String e(String str, String str2);
public static native String ePoll(String str);
public static native byte[] ea(byte[] bArr);
public static native byte[] eg(byte[] bArr);
public static native String es(String str);
public static native String espirt(String str);
public static native int getCrc32(String str);
public static String getLibName() {
return "goblin_6_1_1";
}
public static native String getPayKey();
public static native String roller(String str);
public static native byte[] sand(byte[] bArr);
public static native String ve(String str);
public static native String version();
static {
try {
System.loadLibrary(getLibName());
} catch (UnsatisfiedLinkError e) {
e.printStackTrace();
}
}
}
方法是不是有点多?不知道到底哪个方法对我们有用?这里经过一系列测试验证,最后得出了结论,“da”方法用于解密,“ea”方法用于加密,“sand”方法用于加密POST数据,另外APP还调用了“drift”方法,用于不明,感觉也是加密,以及“version”方法只返回了“alpha”字符串。
测试结果图,可以发现“sand”方法返回的和POST数据是一样的。
参考资料:
https://www.52pojie.cn/thread-805388-1-1.html
https://www.uedbox.com/post/11146/
https://wooyun.js.org/drops/%E4%B8%80%E6%AC%A1app%E6%8A%93%E5%8C%85%E5%BC%95%E5%8F%91%E7%9A%84Android%E5%88%86%E6%9E%90%EF%BC%88%E7%BB%AD%EF%BC%89.html