WMCTF:welcome-to-ctf

这道题先不说算法,技术层面上就很有意思,反调试函数,硬件断点设置,以及VEH,UEF设置和注册都是借助GetProcAddress函数完成的,导致从import表无法查找到相关函数信息,有关VEH和UEF的相关知识可以看下大佬的这篇文章https://bbs.pediy.com/thread-226235.htm。

程序的反调试函数是NTInformationProcess,如果装了sharpOD后可以不用对其进行patch,可以直接到main函数,但main函数其实是一个假的验证函数,就是一个base64解密,但如何进入到正确的验证流程,还是得分析VEH和UEF在程序中的应用。先是对GetProcAddress函数下断。

1

这个RtlAddVectoredExceptionHandler函数其实就是添加VEH异常处理函数

1597483055033

这个SetUnhandledExceptionFilter是异常捕获主要用于对反调试检测触发,导致程序崩溃

1597483731423

1597483759671

这个getthreadcontext和setthreadcontext函数是配套使用的主要和硬件断点设置有关,在官方wp中也说了程序设置了4个硬件断点,刚好这一对函数也调用了四次。具体的硬件断点查看主要0x40121c处,这个可以从对SetUnhandledExceptionFilter的调用中得到信息。这个反调试函数也是真的隐藏的够深,真的感觉出题人用尽心思。

1597484393338

1597484117807

1597492796065

可以看到里面正好有四个if语句刚好是四个硬件断点处分别对四个硬件断点做了VEH注册,当程序执行到硬件断点处会触发异常处理函数。对if处下断。

1597493597566

可以看到0x4027C8是main函数中那个假的验证函数,当判断通过后修改了eip为0x402883,因此在0x402883才是真正的验证函数:

1597494257162

整个一套流程就是魔改base64.decode()->RSA-768->x^3+z^3+80435758145817515^3=42。但是0x402316里面的表还不是真正魔改后的表。在第VEH注册函数中第三个判断又会对表进行修改,我是是真的吐了。后续调试的话就不能对main函数下断点了,因为有sharpOD的存在导致异常处理不能在调试过程中执行,只能对正确的判断函数下断。RSA-768的特征是从内存中看到它的n值判断来的,上网查它的p和q都有,三元三次方程则是根据看到了80435758145817515推测出来,但具体判断等于42在哪还是希望哪位师傅看到了能够指点一下。最后就是解密脚本了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#coding=utf-8
import gmpy2
import base64
import string
from struct import *
stable="FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 09 FF FF FF 36 3F 08 2E 0C 04 1B 0A 00 27 2F FF FF FF FF FF FF FF 1D 35 29 0B 11 3B 05 31 15 07 10 23 28 02 26 18 37 1E 3C 1A 32 22 06 1F 2D 34 FF FF FF FF FF FF 19 17 30 3A 13 2C 0F 3E 33 38 0D 1C 01 12 14 16 3D 0E 20 2B 39 25 03 2A 21 24 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"
stable=stable.replace(' ',',')
b64stable=[]
for i in range(0,len(stable),3):
tmp=""
tmp+=stable[i]
tmp+=stable[i+1]
b64stable.append(int(tmp,16))
#print tmp
table=['']*64
for i in range(len(b64stable)):
if b64stable[i]!=255:
table[b64stable[i]]=i
#print len(table)
string1="".join(map(chr,table))
print string1
string2="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
n=1230186684530117755130494958384962720772853569595334792197322452151726400507263657518745202199786469389956474942774063845925192557326303453731548268507917026122142913461670429214311602221240479274737794080665351419597459856902143413
p = 33478071698956898786044169848212690817704794983713768568912431388982883793878002287614711652531743087737814467999489
q = 36746043666799590428244633799627952632279158164343087642676032283815739666511279233373417143396810270092798736308917
d=0x10001
m=5803427745647280975433812866345295
#x^3+y^3+z^3=42 z确定z=80435758145817515,此处的m为xy拼接后的值
phiN = (p - 1) * (q - 1)
e = gmpy2.invert(d, phiN)
print 'e =', hex(e)
enc = pow(m, e, n)
print enc
b64 = hex(enc)[2:]
print len(b64)
tmp=b""
for i in range(0,len(b64),2):
tmp+=chr(int(b64[i:i+2],16))
flag = 'WMCTF{' + tmp.encode("base64").translate(string.maketrans(string2,string1)).replace('\n', '') + '}'
print flag
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2020-2022 llx-moon
  • Visitors: | Views:

请我喝杯咖啡吧~