HackThisSite - Application Mission 6
Challenge
Find the Password (easy) 附件是一个 Windows 控制台程序,要求输入密码并校验;只有把正确口令输入进去,程序才会把 HTS 要提交的密码回显出来。
附件 app6win.zip 里只有一个
app6win.exe。是用 MSVC 编译的原生
PE,入口处有一段自解密壳:先把 .text 改成可写,XOR 还原 204
字节被加密的代码,再跳进去执行真正的
main。密码校验就在这段被还原出来的代码里。
Solution
Recon:
file app6win.exe→PE32 executable for MS Windows 4.00 (console), Intel i386, 3 sectionsstrings -a能看到明文常量Please enter the password:、Invalid Password、The password is %s,还有一个残留的源文件名main2.exe。- 直接
strings搜不到口令,也搜不到校验逻辑对应的字符串,因为真正干活的代码被 XOR 加壳了:入口只解密 204 字节再执行。 - 静态定位壳:Ghidra 无头反编译
FUN_00401000就是解密器,参数是 VA0x4010d3、长度0x33个 dword、密钥0xbeefcabe。
Step 1: Ghidra 无头反编译整程序
DecompileAll.java 把每个函数输出为 C:
1 | $ cd <hts-workspace>/challenges/hts-app && mkdir -p out |
-scriptPath 必须给绝对路径,否则 Ghidra
报 Failed to find script in any script directory。
Step 2: 读解密壳
入口 entry 最终调用
FUN_00401000,它的反编译结果就是自解密逻辑:
1 | void FUN_00401000(void) |
VirtualProtect 的保护标志把
local_28.Protect 与 0xffffffdd 相与再或上
4,即把所在内存页加上
PAGE_READWRITE(4),然后对
DAT_004010d3 起的 0x33 个 dword 反复 XOR
0xbeefcabe,最后 func_0x004010d3()
直接跳进刚解密的代码。0x33 * 4 = 0xcc 字节,覆盖 VA
0x4010d3 到 0x40119e。
Step 3: 离线重放 XOR,还原真正的 main
壳的变换是可逆的,直接对文件重放同一个 XOR 即可静态看到明文代码。完整脚本:
1 | #!/usr/bin/env python3 |
1 | $ python3 decrypt_app6.py |
用 objdump 反汇编还原后的代码:
1 | $ objdump -d -M intel --start-address=0x4010d3 --stop-address=0x4011ef app6_plain.exe |
1 | 004010d3 <.text+0xd3>: |
Step 4: 逆推比较逻辑
观察 → 推理:
0x4010d9和0x4010e0把两个常量 dword 写进栈:[ebp-0x10] = 0xa6c6163(小端字节63 61 6c 0a→"cal\n")、[ebp-0xc] = 0x6967616d(6d 61 67 69→"magi")。- 输入缓冲在
[ebp-0x28],用memset清 16 字节;读取循环把每个字符写进去,遇到\n、NUL 或长度到0x10就停,[ebp-0x14]记录实际长度len。 - 校验循环
i = 0, 4(cmp [ebp-0x18],0x8; jae done说明只在i<8时比较,正好两个 dword):- 取
input_dword[i/4], - 和栈上
[ebp-0x14 + 4*((len-i)/4)]比较。
- 取
[ebp-0x14]往下正是len、"cal\n"、"magi"三个 dword。若输入长度len = 8:i=0:比较input_dword[0]与[ebp-0x14 + 4*(8/4)] = [ebp-0xc] = "magi";i=4:比较input_dword[1]与[ebp-0x14 + 4*((8-4)/4)] = [ebp-0x10] = "cal\n"。
所以输入的第 0 个 dword 要等于 "magi"、第 1 个 dword
要等于 "cal\n",拼起来就是 7
个字母加一个换行。字符串常量在内存里的顺序是反的(cal\n
在前、magi 在后),靠索引 (len-i)/4
倒着取,拼回来才是 magical。
Step 5: 本地运行验证
程序是控制台程序,直接在 Wine 里跑即可(无需图形界面):
1 | $ printf 'magical\n' | wine app6/win/app6win.exe 2>/dev/null | tr -d '\r' |
程序在成功分支用 printf("The password is %s", input)
把答案回显出来,所以 The password is magical
这一行就是自证的验证结果。