来自看雪3w班的一道题
拿到app,先装上手机
看样子是要拿到正确的flag才能通过。
将apk拖入jadx中,发现加固了,那就先脱壳
上frida脱壳,这里用到的是github上大佬写的脱壳工具,https://github.com/lasting-yang/frida_dump
脱壳后发现有三个dex,很显然是最后一个
将dex拖入jadx中分析下,发现问题并没那么简单,居然onCreate方法都是jni函数化
将libnative-lib.so拖入ida分析,查看导出函数,并未发现onCreate及check,十分火大,看来还是动态注册
拷贝大佬写的对RegisterNatives方法的hook,再spawn方式启动启动app
function hook_RegisterNatives() {
var symbols = Module.enumerateSymbolsSync("libart.so");
var addrRegisterNatives = null;
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
//_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
if (symbol.name.indexOf("art") >= 0 &&
symbol.name.indexOf("JNI") >= 0 &&
symbol.name.indexOf("RegisterNatives") >= 0 &&
symbol.name.indexOf("CheckJNI") < 0) {
addrRegisterNatives = symbol.address;
console.log("RegisterNatives is at ", symbol.address, symbol.name);
}
}
if (addrRegisterNatives != null) {
Interceptor.attach(addrRegisterNatives, {
onEnter: function (args) {
console.log("[RegisterNatives] method_count:", args[3]);
var env = args[0];
var java_class = args[1];
var class_name = Java.vm.tryGetEnv().getClassName(java_class);
//console.log(class_name);
var methods_ptr = ptr(args[2]);
var method_count = parseInt(args[3]);
for (var i = 0; i < method_count; i++) {
var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));
var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));
var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));
var name = Memory.readCString(name_ptr);
var sig = Memory.readCString(sig_ptr);
var find_module = Process.findModuleByAddress(fnPtr_ptr);
console.log("[RegisterNatives] java_class:", class_name, "name:",name ,"sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module!=null?find_module.name:"unknown", "module_base:", find_module!=null?find_module.base:"unknown", "offset:", find_module!=null?ptr(fnPtr_ptr).sub(find_module.base):"unknown");
}
}
});
}
}
function replaceLoader(){
Java.perform(function() {
console.log("Entering")
var application = Java.use("android.app.Application");
application.attach.overload('android.content.Context').implementation = function(context) {
this.attach(context); // 先执行原来的attach方法
// 获取classloader
Java.classFactory.loader = context.getClassLoader();
hook_RegisterNatives();
}
});
}
直接打印出动态注册函数的地址偏移
直接找到check的动态注册地址,跳转过去,并修改传入参数的定义。分析发现flag为20位
往后再看,发现似乎用了strcmp比对,那问题不就简单了吗。直接hook位于libc.so的strcmp函数
再写个函数,直接调用check方法,并随便传入20位数字或字母
起脚本,观察日志发现,似乎是拿了我输入的前6位与kanxue对比
问题是我们需要的flag应该为20位才对。尝试输入以kanxue开头的20个字母
那基本上就水落石出了,先是kanxue,再是training,然后是course,正正好好20个字母。
在app端输入kanxuetrainingcourse,果然就通过了
再回看输入kanxue后,又动态注册了两次check函数,分别跳转查看
int __fastcall sub_1234(JNIEnv *a1, int a2, int a3)
{
int v3; // r1
jstring v4; // r0
int v6; // [sp+20h] [bp-48h]
int v7; // [sp+24h] [bp-44h]
char *s; // [sp+28h] [bp-40h]
_DWORD v11[2]; // [sp+3Ch] [bp-2Ch] BYREF
char s1[4]; // [sp+44h] [bp-24h] BYREF
int v13; // [sp+48h] [bp-20h]
char v14; // [sp+4Ch] [bp-1Ch]
int v15[3]; // [sp+50h] [bp-18h] BYREF
s = (char *)GetStringUTFChars(a1, a3, 0);
if ( strlen(s) != 14 )
return 0;
v15[2] = (int)sub_1148;
v15[1] = (int)&unk_50B0;
v15[0] = (int)aGlago;
v7 = sub_135C(a1, &unk_50D0);
sub_1386(a1, v7, v15, 1);
v6 = sub_13CE(a1, v7, aGlago, &unk_50B0);
v14 = 0;
v13 = 0;
*(_DWORD *)s1 = 0;
v3 = *(_DWORD *)s;
v13 = *((_DWORD *)s + 1);
*(_DWORD *)s1 = v3;
if ( strcmp(s1, aFSU2) )
return 0;
*(_DWORD *)((char *)v11 + 3) = 0;
v11[0] = 0;
LOWORD(v11[1]) = *((_WORD *)s + 6);
v11[0] = *((_DWORD *)s + 2);
v4 = sub_1416(a1, (int)v11);
return (unsigned __int8)sub_1444(a1, v7, v6, v4);
}
bool __fastcall sub_1148(JNIEnv *a1, int a2, int a3)
{
char *s; // [sp+18h] [bp-28h]
bool v5; // [sp+2Bh] [bp-15h]
_DWORD s1[2]; // [sp+2Ch] [bp-14h] BYREF
s = (char *)GetStringUTFChars(a1, a3, 0);
v5 = 0;
if ( strlen(s) == 6 )
{
*(_DWORD *)((char *)s1 + 3) = 0;
s1[0] = 0;
LOWORD(s1[1]) = *((_WORD *)s + 2);
s1[0] = *(_DWORD *)s;
if ( !strcmp((const char *)s1, byte_5096) )
v5 = 1;
}
return v5;
}
发现其中比对了两次字符串,地址编译分别为0x50F7,0x5096,再次编写代码,直接读这个地址打印cstring
function print_string(addr){
var nativelib_baseAddr = Process.getModuleByName("libnative-lib.so");
var str_addr = nativelib_baseAddr.base.add(addr);
console.log(addr,"==>",ptr(str_addr).readCString());
}
到此结束,非常值得新手尝试的crackme