#逆向-NCTF-复现|DOSBOX-Frida3DES
🤕5ea1
一个昏昏欲睡的傍晚...为什么没有妹妹来陪我www
Ez_Dos
题目标题也给了,说是DOS下的东西,大致跑一下汇编代码,可以看到是一个RC4的加密,16位编写,拿不到f5,只能初步看到调用了多个函数来魔改,硬读汇编脚本没写出来,只能去dump密钥了
有dump那肯定得调试,所以先要搭一个能跑DOS应用的环境下来,这里用的是DOS_BOX,安装其实没什么好讲的,直接跑一遍安装包即可,这点还是很先进的(和某些玩意比起来
打开DOSBOX以后出现一个界面,输什么调试指令都没用,因为DOSBOX相当于一个系统模拟器,模拟的是CPU的指令,需要在系统盘下运行,所以这里要把要调试的目录挂载为系统盘
挂载指令:mount c [你自己的路径]
挂载完直接进入盘即可
现在需要一个DOS系统下的调试工具:debug.exe,把它直接拖到要调试的目录下,然后打入指令:debug.exe [要调试的应用]
具体调试的指令可以看我其他的博客(
来看要找什么,rc4可以简化为明文^密钥流=密文,所以只要拿到其中后面两个即可,密文已知,那么我们有两种方案去拿到密钥流
- 类似测信道,预先输入一个明文,观测加密后得到的密文(非实际密文,然后两者异或得到密钥
- 直接从异或操作中读取密钥(寄存器读取
密文
看汇编,观察seg002:FA段,这句代码是一个xor操作,通过观察分析,此处是加密的最后一步异或
继续往下看,加密后的明文和预设密文的比较段,去几个花就能找到下面的汇编
此处出现了**[si]和[di]的比较,往上看,si的预设值是141h,di是168h,蜘蛛感应,不太对,发现si后面又加上了2,di加上了1,所以应该是170h和142h**
然后你随便调一下就知道170h存的是明文(加密后的明文,那142就是预设密文...
dump下来
unsigned char enc[] =
{
0x7C, 0x3E, 0x0D, 0x3C, 0x88, 0x54, 0x83, 0x0E, 0x3B, 0xB8,
0x99, 0x1B, 0x9B, 0xE5, 0x23, 0x43, 0xC5, 0x80, 0x45, 0x5B,
0x9A, 0x29, 0x24, 0x38, 0xA9, 0x5C, 0xCB, 0x7A, 0xE5, 0x93,
0x73, 0x0E, 0x70, 0x6D, 0x7C, 0x31, 0x2B, 0x8C
};
1
按照上面的分析,可以知道FA处的xor是明文和密钥流的异或加密,所以得知执行到这一步的时候的al就是加密的密钥,只要在这一步打下断点,读取寄存器ax的后两位即可
只要硬读就行,38次
2
上面也分析到了,170h存放的数据是加密后的明文,所以只要直接步进到程序末尾,然后去读取170h处的数据即可得到加密后明文,再和明文异或就能拿到密钥
手动dump,写出如下脚本:
# 定义两个十六进制字符串
hex_str1 = "7C 3E 0D 3C 88 54 83 0E 3B B8 99 1B 9B E5 23 43 C5 80 45 5B 9A 29 24 38 A9 5C CB 7A E5 93 73 0E 70 6D 7C 31 2B 8C"
hex_str2 = "53 1c 38 1b 92 6c d2 1a 05 ed 8a 49 a6 c6 32 62 c2 8c 46 0b 82 17 08 6d bb 49 99 69 db c7 76 5f 73 38 24 67 2f 90"
# 将十六进制字符串转换为字节列表
bytes1 = bytes.fromhex(hex_str1.replace(" ", ""))
bytes2 = bytes.fromhex(hex_str2.replace(" ", ""))
# 执行异或操作
result_bytes = bytes(97^a ^ b for a, b in zip(bytes1, bytes2))
# 将结果转换为十六进制字符串
result_hex = ' '.join(format(byte, '02X') for byte in result_bytes)
# 输出十六进制结果
print(result_hex)
# 将结果转换为 ASCII 字符串
try:
result_ascii = result_bytes.decode('ascii')
print(result_ascii)
except UnicodeDecodeError:
print("结果不能完全转换为 ASCII 字符串。")
运行得到flag:NCTF{Y0u_4r3\Bp@fmb1y_M4st3r_5d0b497e}
x1_login
困死了,速写思路
一开始进去发现字符串混淆,有调用某个函数解密,直接找到对应函数,懒得逆,把so库拿来直接调用一遍,还原混淆的字符串;
还原字符串以后关注check类,里面从assets调用了一个本地库,去掉了前40字节,所以从对应位置找到库,写脚本还原,还原后可以解出用户名
解出用户名以后考虑解密码,密码可以通过hook每轮的密钥来解,因为是动态注册,所以这里要在注册的时候先hook一次,保证加载进来以后再hook本地
hook完直接拿到两个密钥,拿来解一下des即可
字符串混淆
- 复写脚本调用
D 准备调用 get 方法,输入: zM1GzM4=
D DecStr.get 方法的返回结果: check
I DecStr.get 方法的返回结果: check
D 准备调用 get 方法,输入: Exv3nhr5BNW0axn3aNz/DNv9C3q0wxj/Exe=
D DecStr.get 方法的返回结果: com.nctf.simplelogin.Check
I DecStr.get 方法的返回结果:
D 准备调用 get 方法,输入: ygvUF2vHFgbPiN9J
D DecStr.get 方法的返回结果: libsimple.so
I DecStr.get 方法的返回结果: libsimple.so
D 准备调用 get 方法,输入: agDYB3bJ
D DecStr.get 方法的返回结果: native
I DecStr.get 方法的返回结果: native
D 准备调用 get 方法,输入: uZPOs29goMu6l38=
D DecStr.get 方法的返回结果: X1c@dM1n1$t
I DecStr.get 方法的返回结果: X1c@dM1n1$t
-
直接分析算法
一次换盒base64外加一次异或长度
-
frida hook
Java.perform(function ()
{
var DecStr = Java.use('com.nctf.simplelogin.DecStr');
DecStr.get.implementation = function (str)
{
result = this.get(str)
console.log("传入的参数 str: " + str +"实际值: " + result );
return this.get(result);
};
});
root检测
/Secure
-
smali代码修改
-
frida hook
Java.perform(function ()
{
var Secure = Java.use('com.nctf.simplelogin.Secure');
Secure.checkRoot.implementation = function ()
{
return false;
};
Secure.checkDebug.implementation = function ()
{
return false;
};
});
本地库加载
- 直接调用上面的解密逆
D 准备调用 get 方法,输入: uZPOs29goMu6l38=
D DecStr.get 方法的返回结果: X1c@dM1n1$t
I DecStr.get 方法的返回结果: X1c@dM1n1$t
- frida hook tocheck
this.tocheck(str,name);
密码
- frida hook密钥
Java.perform(function () {
function Start_NativeHook(libname) {
var dlopen = Module.findExportByName(null, "android_dlopen_ext");
if (dlopen) {
Interceptor.attach(dlopen, {
onEnter: function (args) {
var filePath = args[0].readCString();
if (filePath.indexOf(libname)!== -1) {
console.log(`[+] android_dlopen_ext: start hooking ${libname}`);
this.isCanHook = true;
}
},
onLeave: function (retValue) {
if (this.isCanHook) {
this.isCanHook = false;
hook_native();
}
}
});
} else {
console.error('[!] android_dlopen_ext not found');
}
}//加载
function hook_native() {
var baseAddr = Module.findBaseAddress("libnative.so");
if (baseAddr) {
var target_addr = baseAddr.add(0x1F1C);
Interceptor.attach(target_addr, {
onEnter: function (args) {
var ctx = this.context;
var key0 = null;
var key1 = null;
if (ctx) {
key0 = ctx.x22;
key1 = ctx.x23;
}
console.log(`[+] native key = ${key0} ${key1}`);
},
onLeave: function (retval) {
}
});
} else {
console.error('[!] libnative.so not found');
}
}//native hook
Start_NativeHook("libnative");
});
去混淆
解密
key9784cf3d63dde73和7d2d3436ad3ec537d
已知密文
unsigned char ida_chars[] =
{
0x40, 0x9E, 0xEC, 0x86, 0xB8, 0x84, 0xA5, 0x8B, 0x7E, 0x8A,
0x64, 0xE2, 0x1A, 0xD3, 0xB8, 0xBB, 0xDF, 0x4B, 0xFA, 0x12,
0x46, 0x45, 0x3E, 0x52
};
解密后拿出来拼一下
第一块:~DWPefaS
第二块:+MY?x$y5
第三块:=6mG50U5