(虚拟机逆向, 简单的加密, 调试)
运行
查壳
无壳, Win64程序
分析
使用IDA分析
可以看到是不断的switch, 然后选择相应的指令来进行操作
这道题用汇编语言会更加清晰一些, 因为变量都是用同一个数组TheIp不同来进行表示, 记下标来看代码看一会就混了, 还不如看汇编还可以改名
所以取出数据表中的数据
开始分析:
0x12
0x8
0x12
0x9
0x10: 这里是输入, 然后只存了开头的一个字符.....再位置0x49F02C处
0x4: 再0x49F024处加一, 然后再放入output[+1]处, 注意的是这个数组是int类型
0x1: 再0x40F028处加一
0xf: 比较end(A)跟0x49F02C(也就是第一个字符)是否相等, 如果相等 类似output[0] = 0, 如果不相等则output[0] = 1, 注意output[1] = 第一个字符
0xd: 比较上一步的output[0]是否等于0, 如果不是零, 就在index -= 5, 下一次就回到了getchar() 这里的意思就是读取到了换行符的时候结束这个循环
0x2: *(0x49F028) - 1, 一开始的值为0x11, 感觉跟输入长度有关
0x12: *(0x49F030) += 1, len += 1 , 再output 后面接上一个data的赋值, data的地址为0x49F36C
0x8: 腾出栈空间, 调用了一个函数, 里面len -= 1, 同时将最后的数据(0x20)传到end中
0x12: *(0x49F030) += 1, len += 1, output的原先的data的位置, 替换为下一个data的数据,
0x9: 调用了DevLen_DataToOne函数: len - 1, output末尾的data数据放入了ForCycle中
0x0: 将(0x49F028)中的值给chr
0x4: 调用了AddLen_ChrToLast()函数: len += 1, 把chr的值给了output的末尾
0xf: 调用了Cmp_Chr_End()函数, 估计是比较长度, 那么输入长度应该就是32了
0xd: 调用了CmpFirst_20addCycle()函数: 比较上一个的first的结果, 如果不相等就加上Cycle的值, 也就是data的第二个值(0x2F), 然后这里就退出了, 说明了上面应该就是检查长度
0x12: (0x49F030) += 1, len += 1, 再output的结尾接上下一个data(-10)
0x9: 调用了DevLen_DataToCycle函数: len - 1, output末尾的data数据放入了ForCycle中 len的作用就是用来跳读output的结尾是替换, 还是连接
0x12: 结尾被替换为了下一个data(0)
0xa: 调用了DevLen_LastToOne()函数: len - 1, 将output最后的值给了(0x49F028)
0x13: 调用了get____char()函数: chr = 输入的字符
0x12: (0x49F030) += 1, len += 1, 再output的结尾接上下一个data(-10)
0xb: 调用了DevLen_LastToOne()函数: len - 1, One是(0x49F03C), 变成了data中的一个数据(0x5E)
0x15: 调用了chr_Mult_Two()函数: chr *= 2
0x3: 调用了xor_data()函数: chr ^= 当前的data数据
0x14: 调用了exchange_output()函数: 把输入换成了异或后的数据
0x1: 调用了addr28_add()函数: (0x49F028) += 1
0x0: 调用了addr28_to_chr()函数: 把(0x49F028)的值给chr
0xf: 调用了Cmp_Chr_End()函数: 比较了chr(也就是index)跟end, 看看是否是加密的末尾
0xd: 通过检查first, 决定是否回到上方
上面的这些操作可以总结为: input[i] = (2 * input[i]) ^ data[i]
0x12: 调用了add30_addlen_DataToLast()函数: (0x49F030) += 1, len += 1, 所以再结尾替换了data2的数据(data2连接再data1的下方)
0xa: 调用了DevLen_LastToOne()函数: len -= 1, One是(0x49F028), 现在28(功能类似于index)被置为0
0x12: 调用了add30_addlen_DataToLast()函数: 根据data2的数据来看又是为了循环做准备,
0x12: 这里连续调用了同样0x12号元素, 说明了data并不是替换, 而是连接在了一起
0x12: 加上这个就有三个data值连在了output后面分别是: -0x16, 0x15, 0x8e
0x8: 调用了DevLen_LastToOne()函数: len -= 1, 把连接的最后的值(0x8e)给end
0x13: 调用了get____char()函数: chr = 变换后的output[i]
0xf: 调用了Cmp_Chr_End()函数: 比较了chr(outpu[i])跟end(data2中的数据), 比较是否相同
0x7: 调用了DevLen_LastToOne()函数: len -= 1, 并将len - 1的末尾(0x15)送入chr中
0x4: 调用了AddLen_ChrToLast()函数: len += 1, 并将chr送入末尾, 但是为啥是没变......也就是说len += 1后还是第二个data2数据的位置吗.....不应该啊
if判断
0x9: 调用DevLen_LastToCycle()函数, 把(0x15)放入了For_Cycle中
0xd: 调用了CmpFirst_20addCycle()函数: 如果不相等, 就ip直接加上0x15, 也就是21, 那就直接gg
0x9: 调用了DevLen_LastToCycle()函数: len -= 1, 将尾巴数据给了Cycle, 此时cycle = -16
0x8: 调用了DevLen_LastToOne()函数: 其中One是end, 被赋值0x20
0x5: len += 1, 并将len的值(0x20)给到output后连接的第一个数(其实感觉应该是len了)
0x6: len += 1, 并将cycle的值给了output后连接的第二个数, 还是没变......
0x4: len += 1, 并将chr的值给了output后连接的第三个数, 还是没变......
0x1: (0x49F028) += 1, 类似于index
0x0: chr = index
0xf: 调用了Cmp_Chr_End()函数: 比较了chr(index)跟end(0x20)
0xd: 回到了-16的位置, 也就是case0x12
0x12
0x9
0x12
0x8
0x12
0xa
0x12
0x7
0xf
0xc
0x11
0xe
0xff
进程已结束,退出代码为 0
这些都是一边调试一边分析出来.......越做越像是CSAPP里面的BombLab
大致的思路
- 首先检验输入长度是否是32
- 然后是每个数据乘以二后异或上对应的xordata(位于上面的data1中)
- 最后是跟cmpdata进行比较(位与上面的data2中)
解题脚本
xordata = [0x5e, 0x46, 0x61, 0x43, 0x0e, 0x53, 0x49, 0x1f, 0x51, 0x5e,
0x36, 0x37, 0x29, 0x41, 0x63, 0x3b, 0x64, 0x3b, 0x15, 0x18,
0x5b, 0x3e, 0x22, 0x50, 0x46, 0x5e, 0x35, 0x4e, 0x43, 0x23,
0x60, 0x3b]
output = [0x8E, 0x88, 0xA3, 0x99, 0xC4, 0xA5, 0xC3, 0xDD, 0x19, 0xEC,
0x6C, 0x9B, 0xF3, 0x1B, 0x8B, 0x5B, 0x3E, 0x9B, 0xF1, 0x86,
0xF3, 0xF4, 0xA4, 0xF8, 0xF8, 0x98, 0xAB, 0x86, 0x89, 0x61,
0x22, 0xC1, 0x02]
print()
for i in range(len(xordata)):
print(chr((output[i] ^ xordata[i]) // 2), end = '')
flag
hgame{Ea$Y-Vm-t0-PrOTeCT_cOde!!}
小结
- 就像是hint所讲的多调试, 就能摸索出大致的逻辑了, 加密的方式非常简单, 就是要看switch的顺序, 还是推荐用汇编来看会好很多