CGCTF WxyVM1 WriteUp——IDA Python的使用
CGCTF WxyVM1 WriteUp——IDA Python的使用
准备工作
首先查看文件信息,可以看到,这是个64位elf文件
在Linux上运行起来简单看一下,如果提示没有权限,可以使用
sudo chmod +x ./WxyVM1
来给予执行的权限
然后拖到IDA-64bit中逆向
分析伪代码
- 在main函数中,F5生成伪代码如下
从上面可以看出,输入的flag应该是24长度的。byte_604B80
用来表示输入的字符串,v4
用来标记是否满足条件。第13-17行,就是用来判断byte_604B80
和dword_601060
是否相同。
再进入
sub_4005B6()
函数看一下
byte_604B80
还是我们刚才的输入,主体是一个switch-case语句,通过判断v0
的值,来确定具体执行的内容,也可以算得上一道虚拟机的题目吧——通过找到虚表,来确定指令的具体操作。这里,循环的含义是
byte_6010C0
这段数据中每三字节一组:每组的第一个字节用来作为上述的指令,第二个字节和第三个字节用来对我们的输入(byte_604B80
)进行变换,一共要循环5000次。看下byte_6010C0
这段数据:
再回到主函数中,我们就可以理清整个流程:
我们输入的字符串,会被程序内部进行操作(
sub_4005B6()
函数),转化为新的字符串,只有新的字符串与dword_601060
相同,才会输出correct
那只需要逆向操作,将
dword_601060
放入sub_4005B6()
中逆向操作,就可以得到原始应该输入的字符串,也就是flag
这里面,有几个需要注意的地方:
dword_601060
用到的,应该是Dword中的一个字节,而不是四字节。dword_601060
数据如下:.data:0000000000601060 dword_601060 dd 0FFFFFFC4h ; DATA XREF: main+6E↑r .data:0000000000601064 dd 34h .data:0000000000601068 dd 22h .data:000000000060106C dd 0FFFFFFB1h .data:0000000000601070 dd 0FFFFFFD3h .data:0000000000601074 dd 11h .data:0000000000601078 dd 0FFFFFF97h .data:000000000060107C dd 7 .data:0000000000601080 dd 0FFFFFFDBh .data:0000000000601084 dd 37h .data:0000000000601088 dd 0FFFFFFC4h .data:000000000060108C dd 6 .data:0000000000601090 dd 1Dh .data:0000000000601094 dd 0FFFFFFFCh .data:0000000000601098 dd 5Bh .data:000000000060109C dd 0FFFFFFEDh .data:00000000006010A0 dd 0FFFFFF98h .data:00000000006010A4 dd 0FFFFFFDFh .data:00000000006010A8 dd 0FFFFFF94h .data:00000000006010AC dd 0FFFFFFD8h .data:00000000006010B0 dd 0FFFFFFB3h .data:00000000006010B4 dd 0FFFFFF84h .data:00000000006010B8 dd 0FFFFFFCCh .data:00000000006010BC dd 8 .data:00000000006010C0 ; char byte_6010C0[15000]
所以,以第一个dword为例,比较的应该是0xC4,而不是0x0FFFFFFC4
在
sub_4005B6()
函数中,循环是正向的,一次一次对我们输入的数据进行更新迭代,而在我们逆向求解的过程中,循环也应该是逆向的,即byte_604B80
应该从最后一组进行倒序遍历
思路清楚了,就可以写脚本来输出了
脚本实现
Array_6010C0_0 = [] #用来存储byte_604B80中每一组的第一个元素
Array_6010C0_1 = [] #用来存储byte_604B80中每一组的第二个元素
Array_6010C0_2 = [] #用来存储byte_604B80中每一组的第三个元素
Array_601060 = [] ##用来存储dword_601060中每个dword里需要比较的元素
flag = ""
#将Hex中的字符转换为Dec中对应的数字
def Trans(NumHex):
NumDec = 0
if NumHex == 'a':
NumDec = 10
elif NumHex == 'b':
NumDec = 11
elif NumHex == 'c':
NumDec = 12
elif NumHex == 'd':
NumDec = 13
elif NumHex == 'e':
NumDec = 14
elif NumHex == 'f':
NumDec =15
else:
NumDec = NumHex
return NumDec
# 将byte_604B80中的数据分别存入下面三个数组中
for index in range(0x6010C0, 0x604B58, 3):
Array_6010C0_0.append(Byte(index))
Array_6010C0_1.append(Byte(index+1))
Array_6010C0_2.append(Byte(index+2))
# 对dword_601060数据进行抽取,转化为需要比较的十进制
for index in range(0x601060, 0x6010BD, 4):
Array_601060.append(int(Trans(('0' + (hex(Dword(index))).replace('0x', '').replace('L', '')[-2:])[-1])) + 16 * int(
Trans(('0' + (hex(Dword(index))).replace('0x', '').replace('L', '')[-2:])[-2])))
# 逆向循环,复现sub_4005B6()函数中的VM
for index in range(4999, -1, -1):
if Array_6010C0_0[index]==1:
Array_601060[Array_6010C0_1[index]] = Array_601060[Array_6010C0_1[index]] - Array_6010C0_2[index]
elif Array_6010C0_0[index]==2:
Array_601060[Array_6010C0_1[index]] = Array_601060[Array_6010C0_1[index]] + Array_6010C0_2[index]
elif Array_6010C0_0[index]==3:
Array_601060[Array_6010C0_1[index]] = Array_601060[Array_6010C0_1[index]] ^ Array_6010C0_2[index]
elif Array_6010C0_0[index]==4:
Array_601060[Array_6010C0_1[index]] = Array_601060[Array_6010C0_1[index]] / Array_6010C0_2[index]
elif Array_6010C0_0[index]==5:
Array_601060[Array_6010C0_1[index]] = Array_601060[Array_6010C0_1[index]] ^ flag[Array_6010C0_2[index]]
else:
continue
for i in range(24):
flag += chr(Array_601060[i]%256)
print flag
在IDA的输出中,有nctf{Embr4ce_Vm_j0in_R3}
验证Flag
在命令行中输入,有如下结果,证明结果正确
有空再整理一下IDA python的内容,先挖个坑
原文地址:https://www.cnblogs.com/xrblog/p/11896841.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Go 常见并发模式实现(一):调度后台处理任务的作业程序
- 路由使用进阶(二)
- linux No space left on device由索引节点(inode)爆满引发500问题
- linux中批量添加文件前缀的操作方法
- Go 常见并发模式实现(二):通过缓冲通道实现共享资源池
- 在 Linux 终端中查找域名 IP 地址的命令(五种方法)
- Laravel 表单方法伪造与 CSRF 攻击防护
- Java图形验证码支持gif、中文、算术等
- Go 常见并发模式实现(三):通过无缓冲通道创建协程池
- Linux下安装SVN服务端的方法步骤
- SSM项目频繁打成war包部署,使用tomcat和maven实现热部署配置
- Laravel 视图使用入门
- 基于 Go 协程实现图片马赛克应用(上):同步版本
- Blade 模板引擎入门篇
- 基于 Go 协程实现图片马赛克应用(下):并发重构