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分钟前
被螺丝兔编辑
,原因: