总体难度不是很大,第三道题我赛后搞了六七个小时才做出,不是因为难,只是因为我对c语言的指针很不熟悉,影响了对验证逻辑的理解。
RE1 签到
v6[i-112]
即v5[i]
s = list('akhb~chdaZrdaZudqduvdZvvv|')
for i in range(len(s)):
s[i] = chr((ord(s[i])-1) ^ 6)
print(''.join(s))
encrypt3
首先输入一个即将输入字符串的长度n
然后输入字符串,记为input
然后程序使用enc_flag(网上查了查,意思是encrypt flag)对input加密(说解密也行叭~)
将结果输出,记为output.
分析关键代码:
v10=input[0] ^ input[1] ^ input[2] ^ ... ^ input[n-1]
output[i] = v10 ^ enc_flag[i]
enc_flag[i]
已知。
可以看出,v10与input有关,但不管input的内容是什么,v10只是个char。所以可以爆破出所有的output。
程序中没有提示flag是input还是output,但是我们求出output后就已经发现了flag了。
我觉得input没法求,毕竟只需要逐位异或满足v10是某一特定值(爆破求出是64)即可,input的可能的字符串取值空间极大。
enc_flag = [38,44,33,39,59,35,34,115,117,114,113,33,36,117,118,119,35,120,38,114,117,113,38,34,113,114,117,114,36,112,115,118,121,112,35,37,121,61]
for i in range(0, 0x7f):
output = ''
for j in range(len(enc_flag)):
if not(32 <= (enc_flag[j] ^ i) <= 127):
break
else:
output += chr(enc_flag[j] ^ i)
if 'flag' in output:
print('v10=%d' % i)
print(output)
RE3 (忘了本题名称了)
输入四个int,经过sub_401863()进行变换,得到4个int,并和v9~v12比较数值,相等则输入的4个int的十六进制形式就是flag.
sub_401863() 函数:
第一部分:求出dword_408A40[]
虽然参数里有input,但是函数内部没用到input。用到了main中的v15~v18.
值是固定的,所以动态调试拿到,一共32个int.
之后进入一个32次的循环,循环内部有第2,3,4,5部分。
第二部分:求出v12
函数内部是一个4次循环,每次循环得到一个byte,合起来即一个INT。
对于每个byte:
v12[j] = input[i+2][j]^input[i+1][j]^input[i+3][j]^dword_408A40[i][j]
,
所以v12在形式上为:v12[0]v12[1]v12[2]v12[3]
按字节异或和按int异或其实是等同的,所以简化为:
v12 = input[i+2]^input[i+1]^input[i+3]^dword_408A40[i]
第三部分:求出v11
得到v11,4个_BYTE,即INT
v11[j] = byte_404100[16*(__int8(v12[j]>>4)) + (v12[j]&0xF)]
可以归纳为v11[j] = byte_404100[v12[j]]
byte_404100为已知的长度为0xff的数组,可以使用idc脚本导出二进制流文件。
导出后(记文件名称为dump),可以使用下面的代码提取成数组:
import binascii
with open('dump','rb')as f:
data = f.read()
num = []
for i in range(len(data)):
d = data[i : i+1][::-1] #b'\x00\x00C\xdf'
d = str(binascii.hexlify(d))[2:-1] #000043df
d = int(d,16) #17375
num.append(d)
print(num)
代码没优化,其实不需要这么多行,先这样放在这里吧。
第四部分:求出v10
__ROL4__
是uint32循环左移,__ROL2__
是uint16循环左移,__ROL1__
是uint8循环左移
__ROR4__
是uint32循环右移,__ROR2__
是uint16循环右移,__ROR1__
是uint8循环右移
这些名称都是ida的定义,可以在ida根目录\plugins\defs.h
中详细查看:
(我tm一开始没注意v10的计算同时包含左移和右移,我眼瞎,以为都是__ROL4__
)
第五部分:求a[i+4]
a[i+4][j] = a[i][j] ^ v10[j]
求四个byte,合起来一个int.
按字节异或和按int异或其实是等同的,所以简化为:
a[i+4] = a[i] ^ v10
归纳
程序输入4个int,然后通过32次循环,每次循环求出一个input[i+4]
这里的input不完全代表输入,input[0]到input[3]是输入,但是此后的input[4]到input[35]都是求出来的。
验证input[32],input[33],input[34],input[35]是否和main中的v9~v12相等。
为了不影响理解,我把input[]这个数组起个别名:数组a[]
我们把代数式整合一下:
v12 = a[i+1] ^ a[i+2] ^ a[i+3] ^ dword_408A40[i]
v11[j] = byte_404100[v12[j]]
v10 = __ROR4__(v11, 14) ^ __ROL4__(v11, 10) ^ v11 ^ __ROL4__(v11, 2) ^ __ROR4__(v11, 8)
a[i+4] = a[i] ^ v10
对于上面的算式,我们已知的有dword_408A40[]
,byte_404100[]
,a[32]~a[35]
我们需要求出的是a[0]~a[3]
所以先求v12,再求v11,再求v10,最后求a[i],直到倒着求出a[0]~a[3]
脚本
脚本没整理,有些臃肿,大家将就看吧。
'''
循环左移和循环右移
from https://blog.csdn.net/C_chuxin/article/details/83691674
我嫌弃这两函数不够简洁,自己实现了这个需求,所以此段代码作废。
def circular_shift_left(int_value,k,bit = 8):
bit_string = '{:0%db}' % bit
bin_value = bit_string.format(int_value) # 8 bit binary
bin_value = bin_value[k:] + bin_value[:k]
int_value = int(bin_value,2)
return int_value
def circular_shift_right (int_value,k,bit = 8):
bit_string = '{:0%db}' % bit
bin_value = bit_string.format(int_value) # 8 bit binary
bin_value = bin_value[-k:] + bin_value[:-k]
int_value = int(bin_value,2)
return int_value
'''
def __ROL4__(value, k):
k %= 32
return (((value << k)&0xffffffff) | ((value >> (32-k))&0xffffffff))&0xffffffff
def __ROR4__(value, k):
k %= 32
return (((value >> k)&0xffffffff) | ((value << (32-k))&0xffffffff))&0xffffffff
byte_404100 = [214, 144, 233, 254, 204, 225, 61, 183, 22, 182, 20, 194, 40, 251, 44, 5, 43, 103, 154, 118, 42, 190, 4, 195, 170, 68, 19, 38, 73, 134, 6, 153, 156, 66, 80, 244, 145, 239, 152, 122, 51, 84, 11, 67, 237, 207, 172, 98, 228, 179, 28, 169, 201, 8, 232, 149, 128, 223, 148, 250, 117, 143, 63, 166, 71, 7, 167, 252, 243, 115, 23, 186, 131, 89, 60, 25, 230, 133, 79, 168, 104, 107, 129, 178, 113, 100, 218, 139, 248, 235, 15, 75, 112, 86, 157, 53, 30, 36, 14, 94, 99, 88, 209, 162, 37, 34, 124, 59, 1, 33, 120, 135, 212, 0, 70, 87, 159, 211, 39, 82, 76, 54, 2, 231, 160, 196, 200, 158, 234, 191, 138, 210, 64, 199, 56, 181, 163, 247, 242, 206, 249, 97, 21, 161, 224, 174, 93, 164, 155, 52, 26, 85, 173, 147, 50, 48, 245, 140, 177, 227, 29, 246, 226, 46, 130, 102, 202, 96, 192, 41, 35, 171, 13, 83, 78, 111, 213, 219, 55, 69, 222, 253, 142, 47, 3, 255, 106, 114, 109, 108, 91, 81, 141, 27, 175, 146, 187, 221, 188, 127, 17, 217, 92, 65, 31, 16, 90, 216, 10, 193, 49, 136, 165, 205, 123, 189, 45, 116, 208, 18, 184, 229, 180, 176, 137, 105, 151, 74, 12, 150, 119, 126, 101, 185, 241, 9, 197, 110, 198, 132, 24, 240, 125, 236, 58, 220, 77, 32, 121, 238, 95, 62, 215, 203, 57, 72]
dword_408A40 = [0xF9,0x86,0x21,0xF1,0x61,0x2B,0x66,0x41,0x9A,0xB1,0x6A,0x5A,0x77,0x20,0xA9,0x7B,0xF4,0x60,0x73,0x36,0x61,0x0C,0x6A,0x77,0xB3,0x89,0xBB,0xB6,0x51,0x31,0x76,0x24,0x7C,0x30,0x20,0xA5,0xBD,0x4D,0x58,0xB7,0xED,0x53,0x07,0xC3,0x57,0x5B,0xE5,0x7E,0x8C,0x60,0x88,0x69,0xB7,0x95,0xD8,0x30,0xAF,0x14,0xBA,0x44,0xA1,0x95,0x44,0x10,0x28,0xB4,0x20,0xD1,0xA3,0x5F,0xB5,0x73,0x66,0x49,0x87,0xCC,0x39,0x44,0x24,0x92,0x1F,0x64,0x9E,0xE8,0x5A,0x01,0xCA,0x98,0x60,0x90,0x15,0xC7,0x2E,0xFD,0xE1,0x99,0x0C,0xD8,0x9B,0xB7,0xB0,0x15,0x21,0x1D,0xEB,0x8A,0x22,0x0E,0x81,0x0C,0x78,0xF1,0x54,0x36,0x8D,0x42,0x96,0x34,0x29,0x62,0xE5,0x72,0xCF,0x01,0x12,0xA0,0x24,0x91]
dword_408A40_2 = [0] * 32
for i in range(32):
#四个byte合并成一个int
#dword_408A40_2[i] = hex(dword_408A40[i*4+3] * 2**24 + dword_408A40[i*4+2] * 2**16 + dword_408A40[i*4+1] * 2**8 + dword_408A40[i*4])
dword_408A40_2[i] = dword_408A40[i*4+3] * 2**24 + dword_408A40[i*4+2] * 2**16 + dword_408A40[i*4+1] * 2**8 + dword_408A40[i*4]
dword_408A40 = dword_408A40_2
#print(dword_408A40)
global a
a = [0] * 36
a[35] = 3229185894
a[34] = 2011540633
a[33] = 835020779
a[32] = 1191593383
def get_v12(i):
return a[i+1] ^ a[i+2] ^ a[i+3] ^ dword_408A40[i]
def get_v11(v12):
#print(hex(v12))
v12 = hex(v12)[2:]
if len(v12)<8:
v12 = (8-len(v12))*'0' + v12
v11 = ''
for j in range(4):
v12_j = int('0x' + v12[j*2:j*2+2], 16)
v11_j = hex(byte_404100[v12_j])[2:]
if len(v11_j)<2:
v11_j = (2-len(v11_j))*'0' + v11_j
#print(byte_404100[v12_j], ' ', v12_j, ' ', v11_j)
v11 += v11_j
#v11 = '0x' + v11[6:8] + v11[4:6] + v11[2:4] + v11[0:2]
#print(v11)
#print()
return int(v11, 16)
def get_v10(v11):
#return circular_shift_right(v11, 14) ^ circular_shift_left(v11, 10) ^ v11 ^ circular_shift_left(v11, 2) ^ circular_shift_right(v11, 8)
return __ROR4__(v11, 14) ^ __ROL4__(v11, 10) ^ v11 ^ __ROL4__(v11, 2) ^ __ROR4__(v11, 8)
def get_a_i(i):
return a[i+4] ^ get_v10(get_v11(get_v12(i)))
def solve():
import ctypes
for i in range(31, -1, -1):
a[i] = get_a_i(i)
for i in range(4):
#a[i] = ctypes.c_int(a[i]).value
print(a[i])
print('或')
for i in range(4):
a[i] = ctypes.c_int(a[i]).value
print(a[i])
print('一个是int,一个是unsigned int,反正内存中存储的十六进制是相同的')
solve()
结果有两组,分别是int和uint(内存中存储的十六进制是相同的)
哎~RE三是有两组可以跳flag的值嘛,昨天第一眼我还以为是一个动态的呢。(你这也不是藤原老千花嘛,Σ(っ °Д °;)っ你这个居然还会动)
(我刚刚从你博客偷来的书记.png)
两组是没错,但是这两组在内存中的十六进制表示是相同的。