(分析的apk来源于腾讯应用宝:https://android.myapp.com/myapp/detail.htm?apkName=cn.weli.story&ADTAG=mobile)
//抓包环境postern+charless
//逆向环境frida12.8.0全套 jadx+gda
//手机环境android8.1.0_r1+magisk+wifiadb
1.对app登录进行简单的抓包分析
(1)打开微鲤看看,用已有账号密码进行登录,并通过postern+charles进行抓包。抓到关键链接为
:https://cloud-server.weilitoutiao.net/api/coin/common/login

(2)由于apk未进行加壳,所以直接拖入gda/jadx直接可以查看源码,可用上面抓包抓取到的链接在apk中进行字符串搜索,定位到唯一方法:

(3)通过编写frida脚本来确定登录是否调用此方法,以及打印调用此方法传入的参数和调用栈:
function hook_weili(){
Java.perform(function(){
console.log("inside java perform");
Java.use("cn.etouch.ecalendar.sync.a.a.f").$init.implementation = function(str1,str2,context1){
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("str1:",str1);
console.log("str2:",str2);
console.log("context:",context1);
this.$init(str1,str2,context1);
return;
}
});
}
setImmediate(hook_weili);
attach上app后,对app进行登录操作,脚本回显内容为:

此处str1为登录的账号,str2为登录的密码。
2.对前面抓包抓到的登录表单中的app_sign算法进行简单分析
(1)通过gda对app_sign进行搜索:

(其实还有很多没搜到)
(2)通过查看搜索到的类方法,发现都是用hashtable的put方法对"app_sign"这个键进行赋值
即:hashtable.put("app_sign", ah.a(hashtable)); 于是对"app_sign"的值进行分析。
app_sign的值,是通过将 创建并插入一些键值对的hashtable为参数,
调用cn.etouch.ecalendar.manager.ah的a方法
{
此类的a方法有多个重载,
此处调用的a方法的源码:
public static String ah.a(Hashtable p0) //method@00ac20
{
String sElement;
TreeMap treeMap = new TreeMap();
if (p0) {
Enumeration ekeys = p0.keys();
while (ekeys.hasMoreElements()) {
sElement = ekeys.nextElement();
treeMap.put(sElement, p0.get(sElement));
}
}
StringBuffer stringBuffer = new StringBuffer();
Iterator iiterator = treeMap.keySet().iterator();
while (iiterator.hasNext()) {
sElement = iiterator.next();
stringBuffer = stringBuffer.append(sElement).append(treeMap.get(sElement));
}
return ah.a(stringBuffer.append("616c4ac6d6fd4eea986041a360f4e7b2").toString().getBytes());
}
}
/*
此方法的主要逻辑:新建一个TreeMap,将传过来的hashtable复制到自身。
然后创建字符串变量,将TreeMap中的所有键值对,以此追加到字符串后面,
最后再加上616c4ac6d6fd4eea986041a360f4e7b2,
并将这段字符串转为byte类型数组作为参数调用cn.etouch.ecalendar.manager.ah类的
另一个a方法{ 此a方法原型:public static String ah.a(byte[] p0) }
*/
---------------------------分-割-线---------------------------
public static String ah.a(byte[] p0)原型的a方法的源码:
public static String ah.a(byte[] p0) //method@00ac22
{
return ah.c(ah.d(p0));
}
/*
此方法主要逻辑就是用传入的参数p0调用cn.etouch.ecalendar.manager.ah类的d方法,
并将d方法的返回结果作为参数调用cn.etouch.ecalendar.manager.ah类的c方法。
*/
---------------------------分-割-线---------------------------
cn.etouch.ecalendar.manager.ah类的d方法源码:
private static byte[] ah.d(byte[] p0) //method@00ac8a
{
byte[] obyteArray;
int vint;
try{
obyteArray = null;
MessageDigest mInstance = MessageDigest.getInstance("MD5");
}catch(java.lang.Exception e1){
a.b(e1);
vint = obyteArray;
}
if (!vint) {
return obyteArray;
}
vint.update(p0);
return vint.digest();
}
/*
此方法的主要逻辑就是将使用java.security.MessageDigest类的md5摘要算法,
对传入的参数整体进行md5,将最后的结果返回。
*/
---------------------------分-割-线---------------------------
cn.etouch.ecalendar.manager.ah类的c方法源码:
private static String ah.c(byte[] p0) //method@00ac72
{
int vint;
if (!p0) {
return null;
}
String str = "0123456789abcdef";
StringBuilder stringBuilde = new StringBuilder((p0.length*2));
for (vint = 0;vint < p0.length;vint = vint+1) {
stringBuilde = stringBuilde+str.charAt(((p0[vint]>>4)&0x0f))+str.charAt((p0[vint]&0x0f));
}
return stringBuilde;
}
//此方法主要逻辑是将传入的字节数组进行一系列操作,最后再将结果返回。
(3)从上面简单分析可知,cn.etouch.ecalendar.manager.ah类的d方法将传入的byte数组进行md5操作,返回的byte数组是一串md5。cn.etouch.ecalendar.manager.ah类的c方法返回的string字符串 将成为键"app_sign"的值。
/*
编写frida脚本,
(1)将cn.etouch.ecalendar.manager.ah类的d方法的byte数组参数
转为字符串。并将此类的返回结果的byte数组输出出来(即将byte数组参数进行md5后的结果)
(2)将cn.etouch.ecalendar.manager.ah类的c方法的byte数组参数输出出来,
并将此方法的返回结果的byte数组输出出来。
*/
function hook_weili(){
Java.perform(function(){
console.log("inside java perform");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
var String_class = Java.use("java.lang.String");
Java.use("cn.etouch.ecalendar.manager.ah").d.overload('[B').implementation = function(byte1){
//console.log("bytearray2:",ByteString.of(byte1).hex());
//console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
var result = this.d(byte1);
console.log("d function byte1 to String:",String_class.$new(byte1));
console.log("d function result :",ByteString.of(result).hex());
return result;
}
Java.use("cn.etouch.ecalendar.manager.ah").c.overload('[B').implementation = function(byte1){
//console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("c function byte1 ",ByteString.of(byte1).hex());
var result = this.c(byte1);
console.log("c function result:",result);
return result;
}
});
}
setImmediate(hook_weili);
(4)将抓包软件启动,将app启动并退出当前账号,用上面的frida脚本attach上app。然后使用账号密码的方式在app中进行登录。抓包软件抓取到的登录表单如下图:

app_sign值为9a7a5fe9875c49f21f89a81f43a1b06b。
另一边,attach上app的frida脚本,回显如下图:

cn.etouch.ecalendar.manager.ah类的c方法的返回结果(即键"app_sign"的值)为:9a7a5fe9875c49f21f89a81f43a1b06b。从回显可以知道,cn.etouch.ecalendar.manager.ah类的c方法对传入的byte数组的内容没有任何变化。以及构造此md5的原始数据。
(ps:最后还可以hook cn.etouch.ecalendar.manager.ah的a方法,方法原型为:public static String ah.a(Hashtable p0)。并且打印调用栈。以追踪其他有app_sign的地方。)