抓包
经过对数据包的分析比对,可以看到每次请求都会自动生成相应的请求参数,并计算出一个加密后的参数__skcy,服务端根据这些参数信息和请求内容做校验,校验通过服务端才会返回正确的结果。
找到计算__skcy关键函数
经过搜索关键字我们进入到
CandyPreprocessor类的getParametersSignature方法
private String getParametersSignature(Builder builder, Context context) throws Exception {
Object[] objArr = new Object[]{builder, context};
ChangeQuickRedirect changeQuickRedirect = changeQuickRedirect;
String str = "d7fd4e92b3bd07b96007e804b4226165";
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect, false, str, 6917529027641081856L)) {
return (String) PatchProxy.accessDispatch(objArr, this, changeQuickRedirect, false, str);
}
if (builder != null) {
Object baseString = baseString();
if (TextUtils.isEmpty(baseString)) {
throw new Exception("CandyPreprocessor getParametersSignature normalizedURI is null");
}
List arrayList = new ArrayList();
appendList(arrayList, builder, false);
if (this.version == CandyVersion.Ver1_0) {
arrayList.add(new MyEntry("__sksc", this.candyOriginalMaterial.getScheme()));
}
if (formURLEncoded() != null) {
builder = new StringBuilder("/?");
builder.append(new String(this.candyOriginalMaterial.getPostContent()));
appendList(arrayList, Uri.parse(builder.toString()).buildUpon(), true);
}
builder = getPercentList(arrayList);
dictionarySort(builder);
builder = getNormalizedParameters(builder);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(this.candyOriginalMaterial.getHttpMethod());
stringBuilder.append(StringUtil.SPACE);
stringBuilder.append(baseString);
stringBuilder.append(StringUtil.SPACE);
stringBuilder.append(builder);
builder = stringBuilder.toString();
if (formURLEncoded()) {
builder = builder.getBytes();
} else if (this.candyOriginalMaterial.getPostContent() == null) {
builder = builder.getBytes();
} else {
builder = builder.getBytes();
Builder builder2 = new byte[(builder.length + this.candyOriginalMaterial.getPostContent().length)];
System.arraycopy(builder, 0, builder2, 0, builder.length);
System.arraycopy(this.candyOriginalMaterial.getPostContent(), 0, builder2, builder.length, this.candyOriginalMaterial.getPostContent().length);
builder = builder2;
}
return CandyJni.getCandyDataWithKeyForJava(context, builder, "CandyKey");
}
throw new Exception("CandyPreprocessor getParametersSignature builder is null");
}
在接下来的跳转链之后,我们又找到了CandyJni的getCandyDataWithKeyForJava方法:
public static String getCandyDataWithKeyForJava(Context context, byte[] bArr, String str) {
Object[] objArr = new Object[]{context, bArr, str};
ChangeQuickRedirect changeQuickRedirect = changeQuickRedirect;
String str2 = "8806cdcfdd305bd7b7224b07a9fb85e3";
if (PatchProxy.isSupport(objArr, null, changeQuickRedirect, true, str2, 6917529027641081856L)) {
return (String) PatchProxy.accessDispatch(objArr, null, changeQuickRedirect, true, str2);
}
if (MTGuard.selfExceptionCheck() && bArr != null) {
if (bArr.length != 0) {
return getCandyDataWithKey(context, bArr, str);
}
}
return null;
}
然后我们进入
public static native String getCandyDataWithKey(Object obj, byte[] bArr, String str);
这是一个本地方法,因此,我们需要在原生代码中找到getCandyDataWithKey方法。
打开so文件
经过静态分析 我们得知是在getCandyDataWithKey是在 libmtguard.so 中,我们用IDA打开它
我们在导出函数列表中可以看到,只有JNI_Onload,
动态调试还原算法
经过一系列的动态调试,最终还原出具体算法。请求推荐接口
成功取到数据。