下载附件,使用JEB加载apk,首先看一下Mainfest配置
大致有个初步印象,这个apk有两个Activity,一个是MainActivity 主入口,另一个是 RegActivity,应该是从主入口某个按钮拉起。
我们先分析 MainActivity 入口初始化函数 OnCreate
这里重点在于 会先判断 MyAPP.m 的变量值,是否为0,如果为0 ,就说明没有注册,会进入到注册环节,调用 doRegister, 如果m为1,说明已经注册过了,直接进入work环节。
m的取值就是本题的解题关键了,我们来看一下m在哪里被定义,已经在哪里被调用的。
根据MyApp类的定义,m为静态变量,MyAPP在 onCreate函数里进行初始化动作,可能会影响到变量m的值。
这里调用了native 函数 initSN()。后续我们会在libmyjni.so文件中进行深入分析。
在回到我们刚刚的主入口MainActivity ,我们还需要分析一下 doRegister函数和 work函数的逻辑
先来看 doRegister 函数,其实就是按钮注册点击事件,拉起注册页面。
在注册页面中,有个输入框和一个按钮,给按钮添加点击事件,将用户的输入sn,作为saveSN函数的入参,进行处理。
具体如何处理用户输入的sn,就需要进一步的分析 saveSN函数,这个saveSN函数也属于native层函数,待会也一并分析。
再看MainActivity中的work函数,当m=1时,会进入到work分支,它的逻辑也是在native层。接下来我们就需要分析so文件了
IDA加载 libmyjni.so文件
JNI_Onload 入口可以看到进行了动态函数注册。
我们可以看到 initSN函数实际上就是n1, saveSN函数实际上就是n2, work函数实际上就是n3
为了方便分析,我们将n1,n2,n3 分别换个变量名
n1 -> realinitsn
n2 -> realsavesn
n3 -> realwork
首先分析一下 initsn函数
这个函数非常的简单,就是将/sdcard/reg.dat文件中的内容取出来,和EoPAoY62@ElRD 进行对比。如果相同 v10=1 ,如果不同v10=0 。
注意这里的setValue函数,其实就是对变量m进行赋值操作的
现在再来看native函数 savesn
这个saveSN函数逻辑也很清晰,用户输入的字符sn,会进行字符计算操作,得到了新的字符串,再保存到 /sdcard/reg.dat 文件中。
至此,我们已经非常清晰知道了整个应用的逻辑。
第一次用户打开apk时,会进行注册,用户输入的注册字符,会经过字符计算操作, 然后存入到/sdcard/reg.dat文件中。以后用户再次打开app时,会对m值进行判断,就是通过对比/sdcard/reg.dat文件内容和
EoPAoY62@ElRD 是否相同。
那么我们的任务就根据字符串常量 EoPAoY62@ElRD 来反推出用户的输入字符。
我们依旧使用frida来进行解题。先测试一下我们的输入,输出
从这个输入输出的结果来看,我们总结了一个规律:
1、输入的字符个数和输出字符个数是 一一对应的。
2、将三个输入字符分为一组,对应一个输出字符。
我们可以利用frida快速的爆破出所有的字符,不需要去研究saveSN函数里的字符加密操作。不过此道题saveSN里的字符加密操作比较简单,很容易逆推出来。如果对于很复杂的加密算法,我们的爆破方法会非常简单。
20秒内就可以爆破出第一个三字符元组, 输入201 -> 输出 EoP
第二个元组 输入 608 -> AoY
第三个元组 输入Am! ->62@
第四个元组 输入233 -> ElR
第五个元组 输入3 -> D
最终的flag为 201608Am!2333
注册时输入 201608Am!2333 ,弹出提示框,给出flag的格式提示,也就是work函数的内容,我们不需要深究了。
frida hook 代码
[JavaScript] 纯文本查看 复制代码 var flag = false;
imports = Module.enumerateImportsSync("libmyjni.so");
for(var i=0;i<imports.length;i++){
console.log(imports[i].name);
if(imports[i].name =="fputs")
{
console.log(imports[i].name);
console.log(imports[i].address);
targetfunc = imports[i].address;
break;
}
}
if(targetfunc != undefined)
{
Interceptor.attach(targetfunc,{
onEnter: function(args){
//console.log("strlen enter!");
var data = Memory.readCString(args[0]);
//console.log(data);
if(data == "D")
{
flag = true;
console.log("[+]Found: "+Memory.readUtf8String(args[0]));
}
}
});
}
console.log("[*] hook android.os.Process");
var Process = Java.use("android.os.Process");
Process.killProcess.implementation = function () {
console.log("[*]get in android.os.Process killProcess");
};
var dict = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()";
var result = "EoPAoY62@ElRD";
var plaint = "";
console.log("[*] hook com.gdufs.xman.MyApp")
var MyApp = Java.use("com.gdufs.xman.MyApp");
var myapp = MyApp.$new()
/*
console.log("input: 11");
myapp.saveSN("11");
console.log("input: 111");
myapp.saveSN("111");
console.log("input: 1111");
myapp.saveSN("1111");
console.log("input: 11111");
myapp.saveSN("11111");
console.log("input: 111111");
myapp.saveSN("111111");
console.log("input: 111111111");
myapp.saveSN("111111111");
*/
/*
for(var k =0; k <dict.length; k++)
{
myapp.saveSN(dict[k]);
if(flag)
{
console.log("[+]Found dict : "+dict[k]);
return;
}
}
*/
for(var j =0; j <dict.length; j++)
{
for(var i =0; i <dict.length; i++)
{
for(var k =0; k <dict.length; k++)
{
myapp.saveSN(dict[j]+dict[i]+dict[k]);
if(flag)
{
console.log("[+]Found dict : "+dict[j]+dict[i]+dict[k]);
return;
}
}
}
}
|