Reverse
shell
这题是re的第二题,结果是签到题。。。
ida走一波。
似乎就N1
函数有点用?跟进去康康。
似乎是jiake
函数对we
那块进行处理然后把we
那块当函数调用?有简单混淆那味了。那就再跟进去康康。
调用是jiake(we, 738, 5)
,那实际上就是从we那块开始每字节异或5
,走个738
长。
至于we
到底在哪,因为上面__main
函数似乎定义了全局变量一样的东西,所以we
就是ssd
,而ssd
函数的起始基址用010
搜一下hex特征就找到了,就是0x900
。(应该可以在ida里直接找,但是我菜,不会)
那就直接写脚本修一下we
那块然后再重新用ida打开康康正确的代码是啥。
with open('shell_e55a9fa7062b9e0b4ba412ca3584b543.exe', 'rb') as f:
data = bytearray(f.read())
for i in range(738):
data[0x900+i] ^= 5
data = bytes(data)
with open('shell_true.exe', 'wb') as f:
f.write(data)
再去看一下ssd
函数,就是定义一个字符串v24
和一个数组v4
然后各种简单运算处理嘛,用python倒着走一波就完事。
target1 = [0x51,0xb8,0x5a,0x12,0x10,0xfa,0xd,0x23,1,0x17,8,9,0x1b,0,0x12,0x2c,0xf2,0x12,0x11,0xe]
target2 = bytearray("REVERSE".encode())
for i in range(7):
target2[i] %= 64
for i in range(20):
if target2[i % 7] & 1 != 0:
target1[i] -= 2
else:
target1[i] -= 1
for i in range(10):
tmp = target1[i]
target1[i] = target1[20 - i - 1]
target1[20 - i - 1] = tmp
for i in range(20):
target1[i] -= target2[i % 7]
print(target1)
for i in range(18, -1, -1):
target1[i] += target1[i+1]
target1[i] %= 256
for i in range(20):
target1[i] += 64
target1[i] %= 256
print(bytes(target1))
flag{celebrate_you!}
getmiao
刚开始比赛一看这题是re1,直接就上手了,也没看多少分。(我是憨批)
ida看了下函数挺乱的,就先动态调一波跟一波调用康康哪里是核心函数。
然后就跟到了4257b0
这块。
这题用ida 7.0应该是没法看伪代码的,幸好我赛前升级成了ida 7.6,现在打算之后多版本共存以防万一了。
看代码的话,输入16
位的key,然后用4112c6
处理一下输出到v15
,再把v15
用411217
处理一下输出到v13
,然后v13
应该就要和上面那个预置的v16
数组一致才行。
那就跟进去康康这俩函数。
4112c6
的话,最终数据落地应该是a4
指针,但是这边有个this
就很奇怪,感觉是ida没正确解析?
不过问题不大,应该就是生成a5
然后按字节和key异或就是a4
。
至于a5
的话,我选择动态调试直接拿数据,毕竟已经发现这时候的ida有点不靠谱了。
ida给出的异或操作地址是41d9b9
,那么就在这下个断点康康数据。
输入0123456789abcdef
用来测试。回车后成功在断点处暂停。
可以发现eax
是key,那么ecx
就是上面那个生成的a5
数组。所以往上面找找ecx
的赋值处,然后下个断点重新执行康康其内存数据是个啥。
重新执行到达这个断点后,转到内存数据发现只有一个孤零零的0x63
,那么数据确实是每轮实时生成的,那就直接跳个16
轮康康最终结果。
所以最终得到的a5
就是0x63, 0x87, 0x07, 0x03, 0x70, 0x51, 0x12, 0xC4, 0xDB, 0xBB, 0x8C, 0xAA, 0xC3, 0xB5, 0x8B, 0xBB
。
那么这个4112c6
函数基本上就看完了,再去看下411217
的过程。
这个看起来似乎可以不用管中间的一大段,只要把v11
和输入的a2
也就是上面传入的v15
异或就能得到最终结果。
那么就可以写脚本去生成key了。
a = [0x31, 0xD0, 0x0C, 0x05, 0x24, 0x11, 0x42, 0xCA, 0xCC, 0xBA, 0xD8, 0xE5, 0xC9, 0xEA, 0x8E, 0xBE]
x = "49ba59abbe56e057".encode()
xx = [0x63, 0x87, 0x07, 0x03, 0x70, 0x51, 0x12, 0xC4, 0xDB, 0xBB, 0x8C, 0xAA, 0xC3, 0xB5, 0x8B, 0xBB]
key = []
for i in range(16):
key.append(((a[i]) ^ x[i]) ^ xx[i])
print(bytes(key).decode())
得到keyfnigay1ludayoo02
。
然后去输入,没有任何反应直接等个几秒就崩了,题目说这个是正常情况,多试几次就能成,于是我信了他的邪,试了几十次没有一次成功的。。。找赛务反馈说可能是我环境的问题,然后用队友的机子确实能多试几次出下一步,但是队友那也没做re的环境啊。。。期间另一个队还把这题拿一血了,好气。
于是没办法,想接着动态调试做题是没辙了,只能硬着头皮静态分析去了。
看下面的步骤应该是把一个0x11400
的块分成4416*16
的矩阵,然后一行一行和key异或过去。但是矩阵的原始本体ida又给认错了,肯定不是Str
啊,于是猜测这时候应该是还没崩的,打算去接着跟调试看看具体异或了个啥玩意。
这次xor
在ida给出的基址是4259c9
,于是去下个断点然后输入正确的key看看参与计算的是啥。
发现ecx
是输入的key
,那么就肯定是edx
才是那个要找的数据了,所以往上找到edx
的赋值,然后重新执行康康内存是哪块的,如果是直接从静态数据读取的那就很舒服。
把这个数据在010里搜一下,还真是静态直接读入的,地址是0x21c90
,那就用python
直接读取然后处理一下就完事。
with open('getmiao.exe', 'rb') as f:
data = f.read()[0x21c90:0x21c90+0x11400]
data = bytearray(data)
for i in range(0x11400 // 16):
for j in range(16):
data[16*i+j]^=key[j]
with open('data', 'wb') as f:
f.write(bytes(data))
然后根据ida那边后面的处理过程,应该是把这个当成dll载入然后调用哪个函数的(也可能猜错了,反正就是套娃执行),那感觉应该和getmiao
这个程序没关系了,直接看提取出来的dll吧。
ida打开一看,大大的main_0
函数摆着,挺好。主要就是输入的flag用411578
处理下,然后跟内置的v6
数组比较(4111D6
跟进去看了下就是个简单比较,不过比较的有19
字节,所以实际上v7
也会跟在v6
后面当作v6
的数据去越界读取)。
所以就去看看411578
的过程呗。
主要就是和内置的v6
数组按字节异或,其他应该没啥,所以还是写个脚本去生成flag呗。
target = [0x85, 0x4d, 0x4c, 0xdb, 0x9, 0x2f, 0xe9, 0xb3, 0xdc, 0xf5, 0xe3, 0x3f,0x6c, 0xe, b'D'[0], b'X'[0], b'Z'[0], b'm'[0], b')'[0]]
x = [0xed, 0x28,0x35, 0x84, 0x30, 0x4a, 0x9d,0xec, 0xba,0xc4,0x91,0xa,0x18,0x51,0x22, 0x69,0x3b,0xa,0x8]
for i in range(19):
print(chr(x[i]^target[i]), end='')
成功拿下二血。
flag{hey_9et_f1r5t_f1ag!}