
base64一长串一看就不正常,大概是个文件,cybercgef解码下然后保存为文件。
010看了下

PK头,zip吧。。。
解压发现需要密码,找了个工具按纯数字试了下,爆出密码,不记得了,好几天前了。
总之解压出一个exe。
exe加壳,动调了一会,看规律大概是upx,于是直接esp大法dump。


字符串找了一会没有发现像ip端口的字符串,于是尝试wireshark抓包。
这里直接在主机运行了现在有点后怕。。。


在java层看了一下,有控制流平坦化混淆(我不太清楚,看的很像那种native层加的ollvm混淆)
好像jeb可以写脚本去除,但是我还没有研究过,有时间再说叭。
字符串采取标准base64加密

不过稍微扫几眼还是能看出点东西的,大概加载了一个dex,里面应该有一些关键逻辑。

HOOK了一下dexloader函数发现加载的是check类,对这个类的cmp方法HOOK也是可以HOOK到的。

但是用frida-dump无法dump出有效的dex。
于是打算直接HOOK到的同时dump一下,这里直接让gpt写了一个脚本。
然后成功dump了下来

但是还是无法正常分析。

看到了native方法的字样,于是想着native层确实还没看,这里因为native层的字符串也有加密,我记得是有来着,所以我采取的是直接用frida_dump-master dump so。

哦,然后看完就知道它在干什么了,说来惭愧,确实这么久了还没看脱壳是一款我的问题。。
所以这里就稍微详细点说吧。
这里直接看JNI_ONLOAD函数。主要调用了三个函数,一个一个分析。

126A8虽然一开始看不出来在干什么,但是看到最后的字符串就明白了,这是shadowhook的初始化。

同时从这里得到的信息去查找shadowhook,明白这是一个关于inlinehook的项目。
https://github.com/bytedance/android-inline-hook/blob/main/README.zh-CN.md
![]()
接着看第二个函数12300
看到这么简短的操作,以及函数的参数,大致能猜到这是一个HOOK的过程。

至于具体的操作,则是HOOK了libcso中的execve,这是一个创建新进程的函数。
而替换它的函数是根据判断是否是有dex2oat字符串来决定是否执行原来的execve函数。

接着往下看,最后一个函数sub_12324。

首先是调用了sub_1216C。

判断版本号返回不同字符串。


所以它返回的实际上应该是loadmethod这个方法。
然后在下一步再进行HOOK。

然后去搜索了一下对loadmethod进行HOOK的这个操作,明白了这是在实现函数抽取壳,也就是二代壳。
这里也明白了前面对execve HOOK的原因。

文章:https://onejane.github.io/2021/03/25/%E5%8A%A0%E5%A3%B3%E4%B8%8E%E8%84%B1%E5%A3%B3%E4%B9%8B%E4%BA%8C%E4%BB%A3%E5%A3%B3%E5%87%BD%E6%95%B0%E6%8A%BD%E5%8F%96/#%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90
再看下替换成的函数。

唔,这里看到memcpy再结合函数抽取壳的思想就突然明白它在干什么了,它在把字节填充回去。
用010看一下之前dump的dex。

发现果然有一连串0,然后长度就是152.
所以填充回去即可。
以及下面的12345678也替换为Crackme!

实际的比较函数。
解出flag。已经是赛后了,不清楚对不对。

分析代码逻辑,多次调试大致明白他的思路,有一个结构部分收录了要用到的函数,然后再从中取来调用,完成rc4的加密和比较。

最终比较的密文
很多函数都加了花指令,nop即可,范围的判断是push rax到pop rax。

两个比较关键的函数,上面那个是密钥的S盒生成,byte数组就是key,14位。

然后对byte交叉引用的时候发现有第二个调用的地方,猜测是反调试,于是只取静态下的值。

然后大概有点异或之类的魔改,看了下主要的加密逻辑,

最后加密的那一句有一个异或0x23.
解密脚本:
得出flag:

都不是很难的题叭,附件超大小了,放云盘了,师傅们感兴趣的可以自己试试水。
其实这个发看雪感觉有点水。。但是我自己也太懒了没搭博客所以就发这里叭呜呜。
通过网盘分享的文件:软件安全攻防赛.zip
链接: https://pan.baidu.com/s/1544Yudrr5S7C4gaPwlyYoA?pwd=rea1 提取码: rea1
--来自百度网盘超级会员v4的分享
Java.perform(function () {
console.log("[*] Frida 脚本已加载,开始 Hook DexClassLoader 并尝试dump dex文件...");
try {
// 获取 DexClassLoader 类
var DexClassLoader = Java.use("dalvik.system.DexClassLoader");
console.log("[+] 找到 dalvik.system.DexClassLoader 类");
// Hook DexClassLoader 的构造函数
DexClassLoader.$init.overload(
'java.lang.String', // dexPath
'java.lang.String', // optimizedDirectory
'java.lang.String', // libraryPath
'java.lang.ClassLoader'// parent
).implementation = function (dexPath, optimizedDirectory, libraryPath, parent) {
console.log("[*] DexClassLoader 构造函数被调用 >>>");
console.log(" dexPath: " + dexPath);
console.log(" optimizedDirectory: " + optimizedDirectory);
console.log(" libraryPath: " + libraryPath);
console.log(" parent: " + parent);
// ========== 关键:尝试dump dex文件 ==========
// 如果 dexPath 实际上在磁盘上存在,我们可以尝试复制它
// 注意:某些应用可能会瞬时删除或仅在内存中解密dex,这种情况下物理文件可能并不存在
dumpDexToExternal(dexPath);
// 调用原始构造函数
return this.$init(dexPath, optimizedDirectory, libraryPath, parent);
};
// Hook DexClassLoader 的 loadClass 方法(可选,用于观察加载的类)
DexClassLoader.loadClass.overload('java.lang.String').implementation = function (className) {
console.log("[*] DexClassLoader.loadClass 被调用,加载的类名: " + className);
// 调用原始 loadClass 方法
var result = this.loadClass(className);
// 可以在此处添加更多逻辑,如记录加载的类或执行其他操作
return result;
};
console.log("[*] DexClassLoader 的构造函数和 loadClass 方法已成功 Hook");
} catch (err) {
console.error("[-] Hook DexClassLoader 失败: " + err.message);
}
// 简单枚举以确认 DexClassLoader 是否已加载
Java.enumerateLoadedClasses({
onMatch: function(className) {
if (className === "dalvik.system.DexClassLoader") {
console.log("[+] dalvik.system.DexClassLoader 已加载");
}
},
onComplete: function() {
console.log("[*] 已完成类的枚举");
}
});
// ========== 文件复制函数 ==========
// ========== 文件复制函数,修正后的示例 ==========
function dumpDexToExternal(dexPath) {
if (!dexPath) {
console.log("[-] dexPath为空,无法dump");
return;
}
var File = Java.use("java.io.File");
var FileInputStream = Java.use("java.io.FileInputStream");
var FileOutputStream = Java.use("java.io.FileOutputStream");
var Arrays = Java.use("java.util.Arrays");
try {
var dexFile = File.$new(dexPath);
if (!dexFile.exists()) {
console.log("[-] 目标 dex 文件不存在: " + dexPath);
return;
}
var fis = FileInputStream.$new(dexFile);
// 目标输出目录,可自行修改
var outDirPath = "/sdcard/Download/dump_dex";
var outDir = File.$new(outDirPath);
if (!outDir.exists()) {
var created = outDir.mkdirs();
if (!created) {
console.log("[-] 创建目录失败: " + outDirPath);
fis.close();
return;
}
}
var timestamp = Date.now();
var outDexPath = outDirPath + "/dump_" + timestamp + "_classes.dex";
console.log("[*] 准备将 dex 复制到: " + outDexPath);
var outFile = File.$new(outDexPath);
var fos = FileOutputStream.$new(outFile);
var chunkSize = 4096;
// 正确创建长度为 chunkSize 的 byte[]
function newByteArray(size) {
var tmp = [];
for (var i = 0; i < size; i++) {
tmp.push(0);
}
return Java.array('byte', tmp);
}
var buffer = newByteArray(chunkSize);
var bytesRead = 0;
while (true) {
bytesRead = fis.read(buffer, 0, parseInt(chunkSize));
if (bytesRead <= 0) break; // EOF
// 若 read 返回的大小小于 chunkSize,需要截断写入
if (bytesRead < chunkSize) {
var actualData = Arrays.copyOf(buffer, bytesRead);
fos.write(actualData, 0, parseInt(bytesRead));
} else {
fos.write(buffer, 0, parseInt(bytesRead)); // chunkSize 也是 int
}
}
fis.close();
fos.close();
console.log("[+] Dex 文件已成功复制到: " + outDexPath);
} catch (e) {
console.log("[-] dumpDexToExternal 异常: " + e.message);
}
}
});
Java.perform(function () {
console.log("[*] Frida 脚本已加载,开始 Hook DexClassLoader 并尝试dump dex文件...");
try {
// 获取 DexClassLoader 类
var DexClassLoader = Java.use("dalvik.system.DexClassLoader");
console.log("[+] 找到 dalvik.system.DexClassLoader 类");
// Hook DexClassLoader 的构造函数
DexClassLoader.$init.overload(
'java.lang.String', // dexPath
'java.lang.String', // optimizedDirectory
'java.lang.String', // libraryPath
'java.lang.ClassLoader'// parent
).implementation = function (dexPath, optimizedDirectory, libraryPath, parent) {
console.log("[*] DexClassLoader 构造函数被调用 >>>");
console.log(" dexPath: " + dexPath);
console.log(" optimizedDirectory: " + optimizedDirectory);
console.log(" libraryPath: " + libraryPath);
console.log(" parent: " + parent);
// ========== 关键:尝试dump dex文件 ==========
// 如果 dexPath 实际上在磁盘上存在,我们可以尝试复制它
// 注意:某些应用可能会瞬时删除或仅在内存中解密dex,这种情况下物理文件可能并不存在
dumpDexToExternal(dexPath);
// 调用原始构造函数
return this.$init(dexPath, optimizedDirectory, libraryPath, parent);
};
// Hook DexClassLoader 的 loadClass 方法(可选,用于观察加载的类)
DexClassLoader.loadClass.overload('java.lang.String').implementation = function (className) {
console.log("[*] DexClassLoader.loadClass 被调用,加载的类名: " + className);
// 调用原始 loadClass 方法
var result = this.loadClass(className);
// 可以在此处添加更多逻辑,如记录加载的类或执行其他操作
return result;
};
console.log("[*] DexClassLoader 的构造函数和 loadClass 方法已成功 Hook");
} catch (err) {
console.error("[-] Hook DexClassLoader 失败: " + err.message);
}
// 简单枚举以确认 DexClassLoader 是否已加载
Java.enumerateLoadedClasses({
onMatch: function(className) {
if (className === "dalvik.system.DexClassLoader") {
console.log("[+] dalvik.system.DexClassLoader 已加载");
}
},
onComplete: function() {
console.log("[*] 已完成类的枚举");
}
});
// ========== 文件复制函数 ==========
// ========== 文件复制函数,修正后的示例 ==========
function dumpDexToExternal(dexPath) {
if (!dexPath) {
console.log("[-] dexPath为空,无法dump");
return;
}
var File = Java.use("java.io.File");
var FileInputStream = Java.use("java.io.FileInputStream");
var FileOutputStream = Java.use("java.io.FileOutputStream");
var Arrays = Java.use("java.util.Arrays");
try {
var dexFile = File.$new(dexPath);
if (!dexFile.exists()) {
console.log("[-] 目标 dex 文件不存在: " + dexPath);
return;
}
最后于 33分钟前
被螺丝兔编辑
,原因: