赞
踩
a=[0xBF, 0xA9, 0xB9,
0xAE, 0xBC,
0x81, 0x8D, 0xC9,
0x96, 0x99,
0xCA, 0x97, 0xC9,
0xA5, 0x8E,
0xCA, 0xA5, 0x88,
0x9F, 0x8C,
0x9F, 0x88, 0x89,
0x9F, 0x87]
for i in a:
print(chr(i^0xFA),end="")
# ESCTF{w3lc0m3_t0_reverse}
# Visit https://www.lddgo.net/string/pyc-compile-decompile for more information # Version : Python 3.8 res = [ 146, 58, 34, 106, 210, 118, 54, 242, 106, 154, 60, 164, 14, 154, 176, 108, 44, 60, 66, 194, 56, 194, 66, 106, 194, 4] flag = input('Please input flag:') for i in range(len(flag)): if (ord(flag[i]) * 1314 ^ 520) % 250 != res[i]: print('error!!!') print('yeah you get it Q_W_Q!') return None
def DFS(deep): if deep == 0: #一个flag试探完毕 for i in flag: print((i),end="") print() else: for j in range(65,128): if (j * 1314 ^ 520) % 250 == check[deep - 1]: flag[deep - 1] = chr(j) DFS(deep - 1) check =[54, 242, 106, 154, 60, 164, 14, 154, 176, 108, 44, 60, 66, 194, 56, 194, 66, 106, 194] flag = [0]*19 flag_1="ESCTF{" DFS(len(check)) ```![](https://img-blog.csdnimg.cn/direct/199a3935ef60449aa1a986f4fc4550c2.png#pic_center) 6. 可以看到其中又python_reverse等字样,直接用这个来指定:
def DFS(deep):
if deep == 0: #一个flag试探完毕
if flag[18]“e” and flag[17]“s” and flag[16]“r” and flag[15]“e” and flag[14]“v” and flag[13]“e” and flag[12]==“r”:
for i in flag:
print((i),end=“”)
print()
else:
for j in range(65,128):
if (j * 1314 ^ 520) % 250 == check[deep - 1]:
flag[deep - 1] = chr(j)
DFS(deep - 1)
check =[54,
242,
106,
154,
60,
164,
14,
154,
176,
108,
44,
60,
66,
194,
56,
194,
66,
106,
194]
flag = [0]*19
flag_1=“ESCTF{”
DFS(len(check))
``
8. 寻找到可能的flag提交:
#原程序 res=[1,-1,2,-2,3,-3] for i in range(len(res)): if res[i] < 0: res[i]<<=1 res[i]+=10 if res[1]&1==1: res[i]*=2 else : res[i]^=13 res[i]*=4 # if res[1] print(res) res=[1,-1,2,-2,3,-3] #手动添加平坦换后 i=0 while i<len(res): b=(int(res[i]<0)^1)+1 while True: match b: case 0: break case 1: res[i]<<=1 b=3 case 2: res[i]^=13 b=4 case 3: res[i]+=10 b=(res[i]&1)*5 case 4: res[i]*=4 break case 5: res[i]*=2 break i+=1 print(res)
4. 其实现的功能仍然是一样的,只不过将一个循环里的多个语句放在了不同的子模块中,再通过子模块之间的相互控制,来达到原有程序的效果。详细的平坦换请看下面这篇文章:控制流平坦化
5. 知道控制流平坦化后,可以使用符号化执行来简化程序,使程序的可读性增强,便于反汇编,使用deflat.py脚本即可去除平坦化:命令如下
6. -f后是文件名,–addr后是要平坦化的函数首地址,执行后效果如下:
7. 这里可以看到,去平坦化后的程序刻度性增强,不过其中还有一些出题人塞进去的虚假指令(恒真/假),永远不会执行。
8. 例如:第一个if语句后面的 ((((_BYTE)dword_603054 - 1) * (_BYTE)dword_603054) & 1) != 0条件就永远为假**(n*(n-1))&1这个结果恒等于0,所以前面的条件恒假,即if语句里面的程序根本不会执行,类似的虚指令后面还有16个,需要清除:
9. 这里使用idapython脚本来快速去除,这里脚本的逻辑**:将jnz指令的条件跳转修改为直接跳转,因为后面的jmp语句永远不会执行,后面的while循环同理,只会执行一次,因此利用脚本将jnz的条件跳转直接改为jmp进行直接跳转(顺跳),源程序相当于:
st = 0x0000000000400620 #main开始 end = 0x0000000000402144 #main结束 def patch_nop(start,end): for i in range(start,end): ida_bytes.patch_byte(i, 0x90) #修改指定地址处的指令 0x90是最简单的1字节nop def next_instr(addr): return addr+idc.get_item_size(addr) #获取指令或数据长度,这个函数的作用就是去往下一条指令 addr = st while(addr<end): next = next_instr(addr) if "ds:dword_603054" in GetDisasm(addr): #GetDisasm(addr)得到addr的反汇编语句 while(True): addr = next next = next_instr(addr) if "jnz" in GetDisasm(addr): dest = idc.get_operand_value(addr, 0) #得到操作数,就是指令后的数 ida_bytes.patch_byte(addr, 0xe9) #0xe9 jmp后面的四个字节是偏移 ida_bytes.patch_byte(addr+5, 0x90) #nop第五个字节 offset = dest - (addr + 5) #调整为正确的偏移地址 也就是相对偏移地址 - 当前指令后的地址 ida_bytes.patch_dword(addr + 1, offset) #把地址赋值给jmp后 print("patch bcf: 0x%x"%addr) addr = next break else: addr = next
a=[0x7FE7E49BD585CC6C,0x520100780530EE16,0x4DC0B5EA935F08EC,0x342B90AFD853F450, 0x8B250EBCAA2C3681,0x55759F81A2C68AE4] key=0xB0004B7679FA26B3 for res in a: for j in range(64): #循环64次 tmp=res&1 if tmp == 1:#判定是否为奇数(为奇数则上轮加密是为负数),在二进制下最低为为1则是奇数 res ^= key res>>=1 if tmp==1: res+=0x8000000000000000 #如果该次加密前是负数(),把左移漏掉的最高位1补回来 #输出,大小端续转化输出 k=0 while k<8: print(chr(res&0xff),end="") res>>=8 k+=1 #ESCTF{1229390-6c20-4c56-ba70-a95758e3d1f8}
a=[ 0x2E, 0xE5, 0x8A, 0x46, 0x2E, 0xEF, 0x6A, 0x42, 0x27, 0xDC, 0x41, 0x48, 0x61, 0x1A, 0x27, 0xE7, 0x94, 0x1E, 0x30, 0x52, 0x74, 0xED, 0x5A, 0x42, 0x22, 0x5F,0xB1,0x13,0x78,0xED, 0x1A, 0x42,0x62,0x27,0xDC,0x29, 0x5C, 3, 0xE1,0x27,0xE7,0x94,0x47,0x25, 3, 0xE1,0x22,0x5F,0xB1, 0x13, 0x6E, 0x2E,0x57,0xA6,0x2E,0xE5,0xA2, 0x46, 0xA5,0x2E, 0xE5, 0xA2, 0x46, 0x2E, 0x57, 0xA6,0x2E,0xE5,0x8E,0x67,0xA5 ] print(len(a)) for i in a: print('{:0>2}'.format(hex(i^0x66)[2:]),end=" ") print() b=[0x3F, 0xF4, 0x9B, 0x37, 0x3F, 0xFE, 0x7B, 0x53, 0xB0, 0x33,0x53,0x67,0x18,2,0x19,0x13,0xB0,0x33,0x53,0x63,0x28,0x18,2,5,0x3F,0xB0,0xB6,0x77,0x77,0x77,0x77,0x3F,0xB0,0xB5,0x7F,0x77,0x77,0x77,0x3F,0x46,0xB7,0x3F,0x46,0xAC,0xFD,0x73,0x7B,0xFD,0x2B,0x7B,0x67,0x4F,0xAF,2,0x67,0x3F,0x88,0xB6,0x3F,0x4E,0xA6,0xB,0x9A,0x3F,0x46,0xB7,0x3F,0xF4,0xB3,0x37,0xB4,0x3F,0xB0,0xB7,0x77,0x77,0x77,0x77,0x3F,0xF4,0x9F,0x76,0x3F,0xF4,0xB3,0x37,0xB4, ] print(len(b)) for i in b: print('{:0>2}'.format(hex(i^0x77)[2:]),end=" ") print() c=[0x17, 0xDC, 0xB3, 0x1F, 0x17, 0xD6, 0x53, 0x7B, 0x98, 0x1b, 0x7b, 0x57, 0x30, 0x37, 0, 0x26, 0x98, 0x1B, 0x7b, 0x53, 0x30, 0x2a, 0, 0x39, 0xD4, 0x63, 0x7B, 0xD4, 0x2B, 0x7B, 0x57, 0x66, 0xA8, 0x2A, 0x4A, 0xD4, 0x23, 0x7B, 0x5B, 0xD4, 0x2B, 0x7B, 0x53, 0x66, 0xA8, 0x2A, 0x56, 0x17, 0x98, 0x9F, 0x5F, 0x5F, 0x5F, 0x5F, 0xB4, 0x52, 0x17, 0x98, 0x9F, 0x5F, 0x5F, 0x5F, 0x5F, 0x17, 0xDC, 0xB7, 0x5E, 0xB4, 0x5F, 0x17, 0xDC, 0x9B, 0x1F, 0x9C,] print(len(c)) for i in c: print('{:0>2}'.format(hex(i^0x5f)[2:]),end=" ")
6. 将拿到的数据使用十六进制编辑器写入文件,再将文件拖入ida反编译分析汇编代码,其中有一个函数按F5显示源代码错误,就直接分析其汇编代码(也不长):
7. 上面函数应该使用了RCX寄存器和栈传递了参数,然后与解密后的数据进行比较,可以用脚本反向异或得到部分flag:
a=[0x7C072E27^0x12345678,0x87654321^0x87653A4F]
for i in range(len(a)):
while a[i]:
print(chr(a[i]&0xff),end="")
a[i]>>=8
9. 函数2直接给出了部分flag字符串:
10. 函数3,F5显示元代码错误,直接分析汇编代码,直接进行比较:
11. 解密脚本如下:
a=[0x795F686F,0x665F756F]
for i in range(len(a)):
while a[i]:
print(chr(a[i]&0xff),end="")
a[i]>>=8
12. 将得到的3各字符串拼接起来即可获得flag:oh_you_found_our_x3nny,包上ESCTF即可提交。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。