当时深夜写的题解,写完已经困傻了....
老规矩,先查壳

无壳,ida64位打开,main函数先调用ACEDriverSDK
成员函数来loadDriver
,然后cout,cin等待用户输入

判断输入的长度和头部字符串,要求输入长度大于0x10,头部字符串为“ACE_"

然后发现了一个奇奇怪怪的加密算法,发现和输入无关,动调发现是生成固定的string字符串:sxx


拷贝一份输入,对输入进行base58加密


猜测是常规码表,随意输入ACE_0123456789ABCDEF
,动调得到的是@iCZKJxoTR3SicbazBLej7f
,跟预测不符。

IDA找到码表,交叉引用找到赋值码表处


好吧,全是浮点数寄存器的计算,还是继续动调找到真是码表,abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789
,根据该码表进行base58加密,发现该函数还将加密后的逆序了一下



将sxx
作为异或密钥再进行一层加密

OK,三环加密分析结束,发送消息调用驱动接口进行输入验证

此时输入为:ACE_0123456789ABCDEF
传入驱动的密文:33 11 3B 29 33 32 0B 17 2C 21 4B 2B 1A 1B 1A 12 02 3A 3F 1D 12 44 1E 00
查壳,发现有tvm,丢垃圾桶(
但是只有几十kb,相较于去年的十几mb还算仁慈
IDA打开定位到DriverEntry,果然v掉了

左边函数比较少,符号也有一些,找到PfltMessageNotify
函数,查阅文档发现是用于通信的

刚好三环是通过filterSendMessage
发送消息的,这个应该就是0环通信函数了。windbg对FltCreateCommunicationPort
下断点,从参数定位到MessageNotifyCallback
地址

在该处断点,运行程序,输入ACE_0123456789ABCDEF
,成功断下

观察参数发现确实是预想的传参

接着对MessageNotifyCallback
函数进行分析,尝试找到验证逻辑
发现存在混淆

还好之前分析鹅厂的反作弊,发现这个混淆的方式和某BASE很像,直接将push和pop之间的nop掉即可,之前刚好写过相似的去花脚本,在此基础上修改了点,直接拿来用了
去掉混淆后函数大概长这样

大致能猜出来是sub_140001448
函数处理主要逻辑,但是也被混淆了,继续还原



很明显第三个是一个tea加密,而且存在验证逻辑,提取dword_140004064
和keyACE6
,尝试直接解密,发现结果不对,看来没有那么简单,xref一下tea加密函数,发现还有一处调用

该函数对tea加密存在内存修改行为,怀疑是动态调整加密逻辑,windbg动调看看

发现异常,确实如意料之中,程序加载时对该处内存进行了修改,那dword_140004064
呢?查看内存,发现这个没有修改
OK,逆一下这个修改后的tea加密
好了,发现就是对v9的代码行进行了修改,还原后可以进行测试了,先获取ACE_0123456789ABCDEF
加密后的密文,用作加密算法的验证

得到33 11
加密后的密文965f5996 866e20c2
编写tea解密脚本
运行结果

跟密文相符,验证成功
提取dword_140004064
,注意从dword_140004060
开始提取,然后tea解密,循环异或sxx
,逆序,base58解码,得到flag

def
deObfuscation(start , end, jmp_push_gapMax
=
25
, jmp_pop_gapMax
=
5
):
asmInfo
=
getAsmInfo(start,end)
obfuscated_blocks
=
[]
for
addr
in
sorted
(asmInfo.keys()):
processed_jmp
=
set
()
processed_pop
=
set
()
opcode, op1, _ , _
=
asmInfo[addr]
if
opcode
=
=
'push'
:
reg
=
op1
for
jmp_addr
in
sorted
(
filter
(
lambda
x: x > addr, asmInfo.keys())):
if
jmp_addr
-
addr > jmp_push_gapMax:
break
jmp_op, jmp_op1, _ , _
=
asmInfo[jmp_addr]
if
jmp_op
=
=
'jmp'
and
jmp_op1
=
=
reg
and
jmp_addr
not
in
processed_jmp:
processed_jmp.add(jmp_addr)
for
pop_addr
in
sorted
(
filter
(
lambda
x: x > jmp_addr, asmInfo.keys())):
if
pop_addr
-
jmp_addr > jmp_pop_gapMax:
break
pop_op, pop_op1, _ , size
=
asmInfo[pop_addr]
if
pop_op
=
=
'pop'
and
pop_op1
=
=
reg:
processed_pop.add(pop_addr)
obfuscated_blocks.append({
'start'
: addr,
'end'
: pop_addr
+
size,
})
break
return
obfuscated_blocks
def
deObfuscation(start , end, jmp_push_gapMax
=
25
, jmp_pop_gapMax
=
5
):
asmInfo
=
getAsmInfo(start,end)
obfuscated_blocks
=
[]
for
addr
in
sorted
(asmInfo.keys()):
processed_jmp
=
set
()
processed_pop
=
set
()
opcode, op1, _ , _
=
asmInfo[addr]
if
opcode
=
=
'push'
:
reg
=
op1
for
jmp_addr
in
sorted
(
filter
(
lambda
x: x > addr, asmInfo.keys())):
if
jmp_addr
-
addr > jmp_push_gapMax:
break
jmp_op, jmp_op1, _ , _
=
asmInfo[jmp_addr]
if
jmp_op
=
=
'jmp'
and
jmp_op1
=
=
reg
and
jmp_addr
not
in
processed_jmp:
processed_jmp.add(jmp_addr)
for
pop_addr
in
sorted
(
filter
(
lambda
x: x > jmp_addr, asmInfo.keys())):
[注意]看雪招聘,专注安全领域的专业人才平台!