前言
分析算法,这个事情...我发现了。是真上瘾...
(PS:从某米应用商店下载了一整页的APP...)
工具
- fiddler(抓网络数据包用)
- jadx-gui(APP反编译用)
- 易语言(复现用)
- 算法助手(可用也可不用,xp模块,需root使用)
0x1 抓包
直接手机设置代{过}{滤}理,打开APP,竟然能抓到数据包。【也可以用Drony端口转发抓包、小黄鸟等】
登录下发验证码的接口为/v3/getVerCode.msp
,codeParam值为32位长度,猜测是md5加密
还有另外一个/v3/getVericodek.msp
的接口返回的内容为json格式
{"resultMsg":"请求成功","resultCode":"1","vericodek":"f2G8x42F57UT"}
0x2 分析
jadx搜接口(不用助手)
直接搜/v3/getVericodek.msp
找到cn.thepaper.paper.ui.mine.registerNew.b
类中的a函数。
查看调用堆栈(用助手)
直接上算法助手搜fiddler数据中codeParam值6280CE5F15B9BCC2CD0AA9C73D0A3046
,出现一条数据记录
明文为F028407907FB062579FEE0D33D155F94C27D4A1DBB53F4DC33402F13BE590D7175FFB8EBB425208A1CB86F151398D955
但是好像并没有再助手和fiddler中搜到相关的数据,猜测明文也是经过处理的,我们看调用堆栈。
找到cn.thepaper.paper.ui.mine.registerNew.b
类中的a函数。
public void a(String str, String str2, String str3, String str4, String str5) {
String str6;
String str7;
if (TextUtils.isEmpty(str5)) {
str6 = str3 + "0000";
str7 = "0000";
} else {
str6 = str3 + str5;
str7 = str5;
}
this.f2371c.a(str, str2, str7, EncryptUtils.encryptMD5ToString(EncryptUtils.encryptAES2HexString(("verType=" + str + "mail=" + str2 + "gCode=" + str7).getBytes(), str6.getBytes())), str4).a(new c<BaseInfo>() {
/* 此处省略N多处理代码 */
});
}
简单看一下,根据fiddler的参数,可以得出"verType=" + str + "mail=" + str2 + "gCode=" + str7
中
str |
str2 |
str7 |
a函数的参数1 |
手机号 |
变量str7 |
6 |
18396555555 |
0000 |
查看哪里调用a函数
在a函数处右键,查找用例。有很多,但是根据上门的a函数的第一个参数str=6,可找到本次调用的位置。
双击进去查看。
public void t(View view) {
if (!cn.thepaper.paper.lib.c.a.a(view)) {
if (!PaperApp.isNetConnected()) {
ToastUtils.showShort((int) R.string.network_fail);
return;
}
cn.thepaper.paper.lib.b.a.a("294");
String trim = this.e.getText().toString().trim();
this.w = trim;
if (!RegexUtils.isMobileSimple(trim)) {
ToastUtils.showShort((int) R.string.phone_incorrect);
return;
}
String str = this.v;
if (str == null || str.length() != 12) {
this.r.d();
return;
}
this.r.a("6", this.w, this.v, "1", "");/* 此处为调用a函数 */
this.g.requestFocus();
m(this.g);
}
}
由上述f函数的代码可得出a函数所有的参数(String str, String str2, String str3, String str4, String str5)
str |
str2 |
str3 |
str4 |
str5 |
a函数的参数1 |
this.w=手机号 |
this.v |
变量str4 |
变量str5 |
6 |
18396555555 |
未知 |
1 |
'' |
出现一个未知的this.v ,既然有调用,那就肯定有赋值,我们再右键查this.v的查找用例
果然找到一个,
this.v = vericodek.getVericodek();
public String getVericodek() {
return this.vericodek;
}
this.vericodek这不巧了吗这不是,这不就是/v3/getVericodek.msp
中得到的吗?
又根据
if (TextUtils.isEmpty(str5)) {
str6 = str3 + "0000";
str7 = "0000";
} else {
str6 = str3 + str5;
str7 = str5;
}
str5为空,所以得出:
str6 = str3 + "0000";str7 = "0000";
str |
str2 |
str3 |
str4 |
str5 |
str6 |
str7 |
a函数的参数1 |
this.w=手机号 |
this.v=getVericodek.msp中的vericodek |
变量str4 |
变量str5 |
str6=str3+"0000" |
str7="0000" |
6 |
18396555555 |
f2G8x42F57UT |
1 |
'' |
f2G8x42F57UT0000 |
0000 |
得出所有参数和变量之后,查看处理过程,现实encryptAES2HexString,然后再用encryptMD5ToString。(PS:那就是先AES加密,在用MD5加密,得出的6280CE5F15B9BCC2CD0AA9C73D0A3046
)
分析
明文经过处理,得到md5值
EncryptUtils.encryptAES2HexString(("verType=" + str + "mail=" + str2 + "gCode=" + str7).getBytes(), str6.getBytes())
也就等于
EncryptUtils.encryptAES2HexString(("verType=" + "6" + "mail=" + "18396555555" + "gCode=" + "0000").getBytes(), str6.getBytes())
先看encryptAES2HexString
public static String encryptAES2HexString(byte[] bArr, byte[] bArr2) {
return bytes2HexString(encryptAES(bArr, bArr2));/* 看样子应该是AES加密,编码为hex而不是base64 */
}
再看下encryptAES
private static final String AES_Algorithm = "AES";
public static String AES_Transformation = "AES/ECB/PKCS5Padding";
public static byte[] encryptAES(byte[] bArr, byte[] bArr2) {
return desTemplate(bArr, bArr2, AES_Algorithm, AES_Transformation, true);/* AES_Algorithm= */
}
public static byte[] desTemplate(byte[] bArr, byte[] bArr2, String str, String str2, boolean z) {
if (!(bArr == null || bArr.length == 0 || bArr2 == null || bArr2.length == 0)) {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(bArr2, str);
Cipher instance = Cipher.getInstance(str2);
instance.init(z ? 1 : 2, secretKeySpec, new SecureRandom());
return instance.doFinal(bArr);
} catch (Throwable th) {
th.printStackTrace();
}
}
return null;
}
由desTemplate可得出AES/ECB算法参1为明文,参2为秘钥
所以用在线AES加密网站测试
加密结果为:F028407907FB062579FEE0D33D155F94C27D4A1DBB53F4DC33402F13BE590D7175FFB8EBB425208A1CB86F151398D955
再MD5结果为:6280CE5F15B9BCC2CD0AA9C73D0A3046
,正好是fiddler里面的codeParam值
0x3 需要注意的事项
- 秘钥的一部分this.v=f2G8x42F57UT 并不是固定的,而是每一次下发验证码之前就要先获取的,
- AES加密的结果中的字母必须为大写,小写的话直接MD5,结果会不一样哦
- 请求的时候,记得要添加协议头哦,要不然的话会提示
没找到uuid
0x4 易语言实现下发短信
因为vericodek不一样,所以易语言之后的codeParam值也和之前fiddler抓到的不一样了。