绕过更新
![
frida-ps -Ua 查看包名com.aikucun.akapp
objection -g com.aikucun.akapp explore -P ~/.objection/plugins
android hooking list activities 查看所有Activity
android intent launch_activity com.aikucun.akapp.activity.LoginActivity 跳到登录Activity不过被更新弹窗盖住
jadx反编译搜索"立即更新"
,直接hook掉show方法
function hook_sig(){
Java.perform(function(){
console.log("Entering hook")
// 干掉弹窗
Java.use("com.aikucun.akapp.widget.dialog.ConfirmDialog").show.implementation = function(){
console.log("hook show ")
}
})
}
setImmediate(hook_sig)
frida -U -f com.aikucun.akapp -l hook_sig.js --no-pause
抓包
https://zuul.aikucun.com/aggregation-center-facade/api/app/search/product/image/switch
appid | 38741001 |
---|---|
did | 24c29bc14b5e3ddec1bf571c844a7e78 |
noncestr | ae5a5c |
timestamp | 1665846444 |
zuul | 1 |
sig | c5a0da1284344f608043722f45c85dfd934c0ce7 |
反编译搜索"sig"
上frida打印参数和返回值
var MXSecurity = Java.use('com.mengxiang.arch.security.MXSecurity')
MXSecurity.signV1.implementation = function (a, b, c) {
console.log('a: ', a); // url
console.log('b: ', b); // noncestr
console.log('c: ', c); // timestamp
var res = this.signV1(a, b, c); // 加密返回
console.log('res: ', res);
return res
}
IDA打开libmx.so,搜索java发现signV1是静态注册函数
拼接了appid,noncestr,timestamp,secret,url等参数后调用digest函数进行加密,参数分别是JNIEnv,SHA1加密函数,加密内容的bytearray
如果需要还原算法,那么前提得拿到secret盐,可以通过jnitrace或者frida hook native都可以,或者直接使用unidbg黑盒调用。
Jnitrace
pip install jnitrace==3.0.8
jnitrace -l libmx.so -m spawn com.aikucun.akapp --ignore-vm > akc.log
Unidbg
初始化
public class AkuMx1 extends AbstractJni {
// 初始化一些 apk 常量
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
private final DvmClass mxSecurity;
// APK 路径
public String apkPath = "C:\\Users\\Administrator\\Downloads\\爱库存5.7.8.apk";
// so 文件路径
public String soPath = "unidbg-android/src/test/resources/demo/akc/libmx.so";
// 加载指定版本的系统库
private static LibraryResolver createLibraryResolver() {
return new AndroidResolver(23);
}
// 创建 android 模拟器,这里是 32 位的
private static AndroidEmulator createARMEmulator() {
return AndroidEmulatorBuilder.for32Bit().build();
}
AkuMx1() {
emulator = createARMEmulator();
final Memory memory = emulator.getMemory();
// 设置 sdk版本 23
memory.setLibraryResolver(createLibraryResolver());
//创建DalvikVM,可以载入apk,也可以为null
vm = emulator.createDalvikVM(new File(apkPath));
// 设置可以调用 jni 函数
vm.setJni(this);
// 打印 jni 函数调用具体的 log
vm.setVerbose(true);
// 加载 so 文件
DalvikModule dm = vm.loadLibrary(new File(soPath), true);
module = dm.getModule();
// 加载 java 加密函数所在 类
mxSecurity = vm.resolveClass("com/mengxiang/arch/security/MXSecurity");
}
// 关闭模拟器
private void destroy() throws IOException {
emulator.close();
System.out.println("destroy");
}
public static void main(String[] args) throws IOException {
AkuMx1 aku = new AkuMx1();
aku.run();
aku.destroy();
}
public void run() {
}
}
主动调用
public void run() {
String a = "https://zuul.aikucun.com/aggregation-center-facade/api/app/search/product/image/switch?appid=38741001&did=24c29bc14b5e3ddec1bf571c844a7e78&noncestr=af7aff×tamp=1665849838&zuul=1";
String b = "af7aff";
String c = "1665849838";
DvmObject<?> strRc = mxSecurity.callStaticJniMethodObject(
emulator,
"signV1(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
vm.addLocalObject(new StringObject(vm, a)),
vm.addLocalObject(new StringObject(vm, b)),
vm.addLocalObject(new StringObject(vm, c))
);
System.out.println("strRc: " + strRc.getValue());
}
返回值确实空的,使用frida主动调用正常返回
function invoke_sig() {
Java.perform(function () {
var MXSecurity = Java.use('com.mengxiang.arch.security.MXSecurity');
var a = "https://zuul.aikucun.com/aggregation-center-facade/api/app/search/product/image/switch?appid=38741001&did=24c29bc14b5e3ddec1bf571c844a7e78&noncestr=af7aff×tamp=1665849838&zuul=1";
var b = "af7aff";
var c = "1665849838";
console.log(MXSecurity.signV1(a, b, c));
})
}
通过jnitrace查看libmx.so的执行流可知需要先执行Java_com_mengxiang_arch_security_MXSecurity_init
,即public static final native int init(Context context, boolean z);
public void run() {
// 加载 context 上下文对象
DvmClass Context = vm.resolveClass("android/content/Context");
DvmObject<?> strRc1 = mxSecurity.callStaticJniMethodObject(
emulator, "init(Landroid/content/Context;Z;)I;",
// Context.newObject(null) 初始化对象,参数直接 null
vm.addLocalObject(Context.newObject(null)),
// frida hook打印入参为 false
vm.addLocalObject(DvmBoolean.valueOf(vm,false))
);
System.out.println("strRc1: " + strRc1);
String a = "https://zuul.aikucun.com/aggregation-center-facade/api/app/search/product/image/switch?appid=38741001&did=24c29bc14b5e3ddec1bf571c844a7e78&noncestr=af7aff×tamp=1665849838&zuul=1";
String b = "af7aff";
String c = "1665849838";
DvmObject<?> strRc2 = mxSecurity.callStaticJniMethodObject(
emulator,
"signV1(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
vm.addLocalObject(new StringObject(vm, a)),
vm.addLocalObject(new StringObject(vm, b)),
vm.addLocalObject(new StringObject(vm, c))
);
System.out.println("strRc2: " + strRc2.getValue());
}
SHA256 是 android里的,java 里是 SHA-256,我们需要重写该函数
@Override
public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "java/security/MessageDigest->getInstance(Ljava/lang/String;)Ljava/security/MessageDigest;":
StringObject type = vaList.getObjectArg(0);
String name = "";
if ("\"SHA256\"".equals(type.toString())) {
name = "SHA-256";
} else {
name = type.toString();
System.out.println("else name: " + name);
}
try {
return vm.resolveClass("java/security/MessageDigest").newObject(MessageDigest.getInstance(name));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
}
return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
}
if ("\"SHA256\"".equals(type.toString())) {
name = "SHA-256";
} else if ("\"SHA1\"".equals(type.toString())) {
name = "SHA-1";
} else {
name = type.toString();
System.out.println("else name: " + name);
}