前言
搞了个微信协议, 结果这段时间陆续全拉闸了, 没办法了, 自己动手逆向登录吧
接口逆向
在Jadx中搜索/cgi-bin/micromsg-bin/getloginqrcode
可找到:
package com.tencent.mm.libwxclient.b;
import com.tencent.mm.protocal.b.du;
import com.tencent.mm.protocal.b.dv;
import com.tencent.mm.protocal.p;
import com.tencent.mm.q.b;
/* loaded from: classes.dex */
public final class b extends com.tencent.mm.q.a<dv> {
public b() {
b.a aVar = new b.a();
aVar.cJK = new du();
aVar.cJL = new dv();
aVar.cJI = 502;
aVar.uri = "/cgi-bin/micromsg-bin/getloginqrcode";
aVar.cJM = 232;
aVar.cJN = 1000000232;
com.tencent.mm.q.b En = aVar.En();
du duVar = (du) En.cJG.cJP;
duVar.eJY = com.tencent.mm.platformtools.e.G(com.tencent.mm.libwxclient.logic.account.b.a.czW);
duVar.eKa = 0;
duVar.eGY = com.tencent.mm.protocal.b.DEVICE_NAME;
En.a(p.Wb());
En.cJJ = 1;
En.Eq().eEl = com.tencent.mm.libwxclient.logic.account.b.a.czW;
this.cnM = En;
}
}对应微信协议可以发现, duVar是Protobuf结构
hypack := hec.HybridEcdhPackIosEn(502, 0, nil, reqdata)
recvData, err := httpclient.MMtlsPost(D.ShortHost, "/cgi-bin/micromsg-bin/getloginqrcode", hypack, Data.Proxy)Hook
查找Protobuf特征toByteArray可以发现:
package com.tencent.mm.ag;
/* loaded from: classes.dex */
public class a {
protected static final int OPCODE_COMPUTESIZE = 1;
protected static final int OPCODE_PARSEFROM = 2;
protected static final int OPCODE_POPULATEBUILDERWITHFIELD = 3;
protected static final int OPCODE_WRITEFIELDS = 0;
public static net.a.a.a.a.b unknownTagHandler = new net.a.a.a.a.a();
// 将Protobuf序列化
public byte[] toByteArray() {
VQ();
byte[] bArr = new byte[VR()];
net.a.a.c.a aVar = new net.a.a.c.a(bArr);
a(aVar);
if (aVar.cJk != null) {
aVar.cJk.write(aVar.hkb);
aVar.cJk.flush();
}
return bArr;
}
public static int a(net.a.a.a.a aVar) {
int i = 0;
net.a.a.b.a.a aVar2 = aVar.hjZ;
if (aVar2.bufferPos != aVar2.bufferSize || aVar2.df(false)) {
aVar2.lastTag = aVar2.readRawVarint32();
if (aVar2.lastTag == 0) {
throw net.a.a.b.a.b.aot();
}
i = aVar2.lastTag;
} else {
aVar2.lastTag = 0;
}
aVar.hka = i;
return net.a.a.b.a.getTagFieldNumber(aVar.hka);
}
public a VQ() {
return this;
}
public int b(int i, Object... objArr) {
throw new Error("Cannot use this method");
}
public void a(net.a.a.c.a aVar) {
b(0, aVar);
}
public int VR() {
try {
return b(1, new Object[0]);
} catch (Exception e2) {
return 0;
}
}
// 将Protobuf反序列化
public a T(byte[] bArr) {
b(2, bArr);
return this;
}
public boolean a(net.a.a.a.a aVar, a aVar2, int i) {
return b(3, aVar, aVar2, Integer.valueOf(i)) == 0;
}
}// FridaHook代码
Java.perform(function () {
console.log("[*] WeChat protobuf hook started...");
const ProtoBase = Java.use("com.tencent.mm.ag.a");
const orig_toByteArray = ProtoBase.toByteArray.overload();
const orig_parseFrom = ProtoBase.T.overload('[B');
function javaByteArrayToJsArray(javaByteArray) {
const len = javaByteArray.length;
const jsArr = new Array(len);
for (let i = 0; i < len; i++) jsArr[i] = javaByteArray[i] & 0xFF;
return jsArr;
}
function bytesToString(jsArr) {
if (typeof TextDecoder !== 'undefined') {
return new TextDecoder('utf-8').decode(new Uint8Array(jsArr));
}
// fallback(仅适合 ASCII)
return jsArr.map(x => String.fromCharCode(x)).join('');
}
// --- Outgoing ---
ProtoBase.toByteArray.implementation = function () {
const buffer = orig_toByteArray.call(this);
const className = this.getClass().getName();
const len = buffer ? buffer.length : 0;
if (len > 0) {
const jsArr = javaByteArrayToJsArray(buffer);
if (bytesToString(jsArr).includes('com.tencent.mm.')) {
return buffer;
}
console.log("\n[+] Outgoing Message Serialized (" + className + ")");
console.log("==========================================================");
console.log(" [+] Data Length: " + len + " bytes");
const ptrBuf = Memory.alloc(len);
Memory.writeByteArray(ptrBuf, jsArr);
console.log(hexdump(ptrBuf, {
offset: 0,
length: len,
header: true,
ansi: true
}));
console.log(jsArr)
} else {
console.log(" [-] Empty or null buffer");
}
console.log("----------------------------------------------------------");
return buffer;
};
// --- Incoming ---
ProtoBase.T.implementation = function (bArr) {
const className = this.getClass().getName();
const len = bArr ? bArr.length : 0;
if (len > 0) {
const jsArr = javaByteArrayToJsArray(bArr);
if (bytesToString(jsArr).includes('com.tencent.mm.')) {
return orig_parseFrom.call(this, bArr);
}
console.log("\n[+] Incoming Message to be Parsed (" + className + ")");
console.log("==========================================================");
console.log(" [+] Data Length: " + len + " bytes");
const ptrBuf = Memory.alloc(len);
Memory.writeByteArray(ptrBuf, jsArr);
console.log(jsArr)
console.log(hexdump(ptrBuf, {
offset: 0,
length: len,
header: true,
ansi: true
}));
} else {
console.log(" [-] Empty or null buffer");
}
console.log("----------------------------------------------------------");
// 调用原始 parseFrom,不要递归
return orig_parseFrom.call(this, bArr);
};
console.log("[*] Hooks installed on com.tencent.mm.by.a");
});
Java.perform(function () {
let LauncherFloatView = Java.use("com.tencent.mm.ui.LauncherFloatView");
LauncherFloatView["aaw"].implementation = function () {
console.log(`LauncherFloatView.aaw is called`);
let result = this["aaw"]();
console.log(`LauncherFloatView.aaw result=${result}`);
return true;
};
LauncherFloatView["a"].overload('com.tencent.mm.ui.LauncherFloatView', 'boolean', 'boolean', 'int').implementation = function (launcherFloatView, z, z2, i) {
console.log(`LauncherFloatView.a is called: launcherFloatView=${launcherFloatView}, z=${z}, z2=${z2}, i=${i}`);
this["a"](launcherFloatView, z, true, i);
};
// 开日志输出
let n = Java.use("com.tencent.mm.sdk.platformtools.n");
n["i"].overload('java.lang.String', 'java.lang.String', '[Ljava.lang.Object;').implementation = function (str, str2, objArr) {
console.log(`n.i is called: str=${str}, str2=${str2}, objArr=${objArr}`);
this["i"](str, str2, objArr);
};
n["d"].overload('java.lang.String', 'java.lang.String', '[Ljava.lang.Object;').implementation = function (str, str2, objArr) {
console.log(`n.d is called: str=${str}, str2=${str2}, objArr=${objArr}`);
this["d"](str, str2, objArr);
};
n["v"].overload('java.lang.String', 'java.lang.String', '[Ljava.lang.Object;').implementation = function (str, str2, objArr) {
console.log(`n.v is called: str=${str}, str2=${str2}, objArr=${objArr}`);
this["v"](str, str2, objArr);
};
n["c"].overload('java.lang.String', 'java.lang.String', '[Ljava.lang.Object;').implementation = function (str, str2, objArr) {
console.log(`n.c is called: str=${str}, str2=${str2}, objArr=${objArr}`);
this["c"](str, str2, objArr);
};
n["g"].overload('java.lang.String', 'java.lang.String', '[Ljava.lang.Object;').implementation = function (str, str2, objArr) {
console.log(`n.v is called: str=${str}, str2=${str2}, objArr=${objArr}`);
this["g"](str, str2, objArr);
};
LauncherFloatView["a"].overload('com.tencent.mm.ui.LauncherFloatView', 'boolean', 'boolean', 'int').implementation = function (launcherFloatView, z, z2, i) {
console.log(`LauncherFloatView.a is called: launcherFloatView=${launcherFloatView}, z=${z}, z2=${z2}, i=${i}`);
this["a"](launcherFloatView, true, true, 0);
};
let b = Java.use("com.tencent.mm.sdk.platformtools.b");
b.DEBUG.value = true
let a = Java.use("com.tencent.mm.loader.stub.a");
a.DEBUG.value = true;
})
Protobuf还原
1 {
1: "\000"
2: 0
3: "A58c561f2e7d84f\000"
4: 553651456
5: "car-31"
6: 0
}
2 {
1: 16
2: "\335\334 \342\354\021\253U\246\3544i\017\231\300%"
}
3: 0
4: "Xiaomi-M2004J19C"将序列化后的Protobuf保存, 使用protoc --decode_raw < xxx.bin 还原得到上述结构, 可见增加了一个字段, 且DeviceID改为了Axxxx开头形式