# UIUCTF
做了一道题就摆烂了 (第二天考试) Win95 的主题挺有特色
# VMWHERE1
给了两个文件一个 chal 是虚拟机程序 一个 program 是二进制文件 就是需要用虚拟机载入的程序
# chal
IDA7.7 打开虚拟机程序,逻辑很清晰 while 循环和 switch-case 其中每一个 case 对应一个操作
可以用 x86 汇编指令近似理解
- case 0: 返回指令(ret)
- case 1: 加法指令(add)
- case 2: 减法指令(sub)
- case 3: 位与指令(and)
- case 4: 位或指令(or)
- case 5: 位异或指令(xor)
- case 6: 左移指令(shl)
- case 7: 右移指令(shr)
- case 8: 获取输入指令(getchar)
- case 9: 输出指令(putchar)
- case 0xA: 赋值指令(push)
- case 0xB: 条件跳转指令(jz)
- case 0xC: 条件跳转指令(jnz)
- case 0xD: 无条件跳转指令(jmp)
- case 0xE: 减一指令(dec)
- case 0xF: 复制
- case 0x28: 调用子函数指令
这里注意一点是通过 getchar () 函数读取输入,而 C 语言的 getchar () 函数一次只读取一个,多余的都会忽略,可以猜测对 flag 的验证是一位一位的验证
# program
010Editor 打开
可以看到文件开头和结尾则是程序运行和结束时会提示的字符串 中间就是需要分析的 code
# 动态调试
思路是在输入时候下断点 只分析后面的 code 就行了
在 getchar () 这里下断点 (每个指令后面的 goto LABEL_31 不用管,就是验证指令是否合法以及栈是否溢出)
然后一直 F8 找到验证 flag 的逻辑 发现每当运行在这里 也就是程序 code 为 08 0F 0A 04 07 05 05 0F 0A XX 05 0C 00 03 0D 是一个固定的一串指令在 program 中是负责对 flag 的每一位进行检查 (v10 的前一位是否为 0) 通过即跳转 若错误则会进入 0xD 的跳转 然后就跳转到 program 末尾 输出错误字符串
于是我们的目标就是让他通过验证就行了 而每一位的验证逻辑都一样 分析第一位就行了
这里我写了前三位的示例
(password[0]>>4) ^ password[0] ^ 0x72 ^ 0x00==0 password[0]=’u’
(password[1]>>4) ^ password[1] ^ 0x72 ^ 0x1d==0 password[1]= ‘i’
(password[2]>>4) ^ password[2] ^ 0x1d ^ 0x6f==0 password[2]=’u’
…
(password[i]>>4) ^ password[i] ^ 0xXX ^ 0xXX==0
08 0F 0A 04 07 05 05 0F 0A XX 05 0C 00 03 0D 的验证逻辑就是这样,其中除了第一位的第一个 0xXX 的值为 0x00 后面的 0xXX 的值都是 push 进去的值和前一次验证时 push 的值
直接在 program 中找出 0xXX 再自己异或就行辣
for password_0 in range(256): | |
if ((password_0 >> 4) ^ password_0 ^ 0x72^0x1d) == 0: | |
print("password[0] =", password_0) | |
break | |
...... | |
类似这样解出所有位就行了 |
uiuctf{ar3_y0u_4_r3al_vm_wh3r3_(gpt _g3n3r4t3d _th1s _f14g)} |
# VMWHERE2
输入第一位变二进制 再 push ff
前一位 0xff
没做了 蹲 WP