题目分析
首先把题拖到IDA中,跳到main函数,但是显示JUMPOUT,反编译失败了
查看汇编代码,手动修正代码
看到一处红色,在Generral打开显示汇编指令,这里很明显的花指令
这是属于假分支跳转类型的花指令
cmp eax, ecx
jnz LABEL1
xor eax, eax
jz LABEL1
这两种花指令都是很常见的,xor eax,eax这让eax为0,逻辑运算指令会影响ZF标志位的,所以会置ZF为1,那么永远会跳转到LABEL1处,这样LABEL1处之前的代码全部是垃圾指令,不管看起来多么正常,统统都是垃圾指令。对于第一种cmp eax,ecx,则会导致ZF标志位为1,从而必定跳转。在这个题当中就是会强制跳转到loc_413BB4+3位置,因此需要删除中间的花指令。
先按U,然后patch
填充为nop,这时候就已经恢复了,往下看到了同样的花指令,因为加花指令一般都是类似的,我们可以alt+B 搜索二进制特征
总共找到五处花指令,修改完后移动到main函数头,按P,然后F5,就可以看到伪代码了。
总共三个关键加密函数,最后的判断条件是加密后的数据和V13的值比较,V13又是从V14拷贝过来的,这里循环了32次,因此V14应该是int[32],修改下类型,然后动态调试获取V14,V7数组的值。编写代码还原第三层加密
第三层加密
关键是这个函数sub_411177,手搓逆向代码
def re(inputstr,a2):
inputstr-=1
result=0
for i in range(0,8):
result |= ((inputstr >> i) & 1) << a2-i-1
return result
第二层加密
这里是C++的虚函数,只有在运行的时候才能看到函数,V22 v21就是函数的地址,因此类型应该是char *,修改下,动态调试时跳到这个地址,然后查看代码,这里返回值是void,修改下代码好看些
然后编写第二个算法的逆算法,这里的话,异或的不需要改,然后-改成+ 就行,V7数组的值动态调试获得。
def trans(arr):
v7 = [0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0]
for i in range(le(arr)):
if i<=16:
if i>=16:
arr[i]^=4
else:
match v7[i]:
case 1:
arr[i] ^= 9
case 0:
arr[i] += 2
else:
match v7[i]:
case 1:
arr[i] ^= 6
case 0:
arr[i] += 5
同理第一个加密函数,动态调试跟进去是rot13加密
def rot13(text):
decrypted_text = ''
for char in text:
if char.isalpha():
if char.isupper():
decrypted_char = chr((ord(char) - 65 - 13) % 26 + 65)
else:
decrypted_char = chr((ord(char) - 97 - 13) % 26 + 97)
else:
decrypted_char = char
decrypted_text += decrypted_char
return decrypted_text
全部代码
def trans(arr):
v7 = [0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0]
for i in range(len(arr)):
if i<=16:
if i>=16:
arr[i]^=4
else:
match v7[i]:
case 1:
arr[i] ^= 9
case 0:
arr[i] += 2
else:
match v7[i]:
case 1:
arr[i] ^= 6
case 0:
arr[i] += 5
def rot13(text):
decrypted_text = ''
for char in text:
if char.isalpha():
if char.isupper():
decrypted_char = chr((ord(char) - 65 - 13) % 26 + 65)
else:
decrypted_char = chr((ord(char) - 97 - 13) % 26 + 97)
else:
decrypted_char = char
decrypted_text += decrypted_char
return decrypted_text
def re(inputstr,a2):
inputstr-=1
result=0
for i in range(0,8):
result |= ((inputstr >> i) & 1) << a2-i-1
return result
inputstr=[0x22,0xFFFFFFA2,0x72,0xFFFFFFE6,0x52,0xFFFFFF8C,0xFFFFFFF2,0xFFFFFFD4,0xFFFFFFA6,0xA,0x3C,0x24,0xFFFFFFA6,0xFFFFFF9C,0xFFFFFF86,0x24,0x42,0xFFFFFFD4,0x22,0xFFFFFFB6,0x14,0x42,0xFFFFFFCE,0xFFFFFFAC,0x14,0x6A,0x2C,0x7C,0xFFFFFFE4,0xFFFFFFE4,0xFFFFFFE4,0x1E]
for i in range(0,32):
inputstr[i]^=1
inputstr[i]=re(inputstr[i],8)
trans(inputstr)
out=''
for i in inputstr:
out+=chr(i)
print(rot13(out))
把a改成{
方法二
看其他人的wp看到了一种方法也很有趣,因为这里的加密有一个鲜明的特征就是逐字节加密的,因此可以单字节爆破,看了下他的方法,将这里exit返回值改成j
import string
import os
import time
table = string.ascii_letters+string.digits+'!-{}'
# table = string.printable
# 'SYC{Y3S-yE5-y0u-S0Ve-Th3-C9P!!!}'
theflag = ''
while len(theflag) < 32:
for ch in table:
flag = (theflag+ch).ljust(32, '#')
exitcode = os.system(f"echo {flag} | ez_cpp.exe 1>&0")
if exitcode >= len(theflag) + 1:
theflag += ch
print(theflag, exitcode)
break
else:
print('not found')
# time.sleep(0.1)
速度也很快