分析某样本遇到两个解密函数,第一个函数引用位置下图,被调用105次:
函数传参有寄存器传参,push 字符串地址,还有先mov到寄存器再入栈,展示一处: 10072EFE入栈就是待解密的字符串。
该解密算法比较简单,指针值减去下标+1 mod 10: 直接写解密算法:
data是传入的待解密字符串。
然后是解决怎么找到调用此解密函数地址,直接xfreTo函数拿到:
拿到调用函数位置就要往上找入参的待解密字符串,
addr传入的是引用解密函数位置,PreHead是该地址向上寻找地址,GetMnem获得地址的指令,GetOpnd获得指令的参数,GetOperandValue获得指令参数数据,详见idc文档,简单的一批,设置i指定寻找四次就返回。 该段代码意思就是找调用解密算法函数位置上面的入栈解密数据: 测试一下,跟上面图比较看:
拿到字符串地址之后就是读取字符串,一直向后读取到null,也可以直接getString,但是不准:
所以整个过程是,循环找调用解密函数位置,找到之后寻找解密字符串,找到取出来,再调用解密函数,最后添加注释,完整如下:
看一下解密之后效果:
如果是寄存器入栈idc有点鸡肋,简单用一下模拟执行,寄存器入栈: 配置flare_emu官方有不赘述,顺便说一下官网demo的取值是直接物理取值,而栈空间在虚拟执行的时候返回之后都平衡了,直接取idb里面的数据基本拿不到: 不在意,我们主要是用它来帮忙解决寄存器入栈的数据。
注册eh实例,第一个参数idc返回解密函数地址,第二个自定义回调函数:
然后它可以帮忙找到寄存器的值:
address是调用函数位置,后面就是解密函数的三个参数,所以能拿到需要的数据位置,可以配合idc完成后面的操作: 鸡肋代码:
其实大可以直接模拟执行完成了,但是处理虚拟内存一直报错,(这就是强行用6.8环境跑flare_emu的问题吧)
这样就可以全解密了:
第二个解密函数只是算法上的不同,逆向算法的时候比较鸡贼,需要动态调试配合,代码加了膨胀只能参考,不能依赖: 第一个函数初始化一个256位的数组,然后第二个下标开始,先取出数组值,数组值再计算出一个位置,把那个位置的数组值取出来,两个交换,交换完成之后再两数相加赋值给第二个位置的数组,虽然很绕,直接动调dump完事:
第二个函数是真解密函数,数组经过上面同样的操作之后,再把两个位置的数据相加再计算一个偏移,这个偏移取出再和加密数据异或完成解密:
之后都可以按照上面的解密步骤相似运行。
def
decrypt(data):
length
=
len
(data)
i
=
0
out
=
''
while
i<length:
out
+
=
chr
(
ord
(data[i])
-
(i
+
1
)
%
10
)
i
+
=
1
return
out
def
decrypt(data):
length
=
len
(data)
i
=
0
out
=
''
while
i<length:
out
+
=
chr
(
ord
(data[i])
-
(i
+
1
)
%
10
)
i
+
=
1
return
out
def
get_string(addr):
out
=
""
while
True
:
if
Byte(addr) !
=
0
:
out
+
=
chr
(Byte(addr))
else
:
break
addr
+
=
1
return
out
def
get_string(addr):
out
=
""
while
True
:
if
Byte(addr) !
=
0
:
out
+
=
chr
(Byte(addr))
else
:
break
addr
+
=
1
return
out
from
binascii
import
*
def
decrypt(data):
length
=
len
(data)
i
=
0
out
=
''
while
i<length:
out
+
=
chr
(
ord
(data[i])
-
(i
+
1
)
%
10
)
i
+
=
1
return
out
def
find_function_arg(addr):
i
=
0
while
True
:
i
+
=
1
addr
=
PrevHead(addr)
print
(
hex
(addr)
+
GetMnem(addr))
if
(GetMnem(addr)
=
=
"mov"
and
"edi"
in
GetOpnd(addr,
0
)):
print
(
"angr addr:0x%x"
%
GetOperandValue(addr,
1
))
return
GetOperandValue(addr,
1
)
break
if
i>
3
:
return
0
return
0
def
get_string(addr):
out
=
""
while
True
:
if
Byte(addr) !
=
0
:
out
+
=
chr
(Byte(addr))
else
:
break
addr
+
=
1
return
out
for
addr
in
XrefsTo(
0x10031B76
,
0
):
xef
=
find_function_arg(addr.frm)
if
xef!
=
0
:
data
=
get_string(xef)
dec
=
decrypt(data)
print
"function addr: 0x%x | Decrypted: %s"
%
(addr.frm, dec)
MakeComm(addr.frm, dec)
MakeComm(xef, dec)
from
binascii
import
*
def
decrypt(data):
length
=
len
(data)
i
=
0
out
=
''
while
i<length:
out
+
=
chr
(
ord
(data[i])
-
(i
+
1
)
%
10
)
i
+
=
1
return
out
def
find_function_arg(addr):
i
=
0
while
True
:
i
+
=
1
addr
=
PrevHead(addr)
print
(
hex
(addr)
+
GetMnem(addr))
if
(GetMnem(addr)
=
=
"mov"
and
"edi"
in
GetOpnd(addr,
0
)):
print
(
"angr addr:0x%x"
%
GetOperandValue(addr,
1
))
return
GetOperandValue(addr,
1
)
break
if
i>
3
:
return
0
return
0
def
get_string(addr):
out
=
""
while
True
:
if
Byte(addr) !
=
0
:
out
+
=
chr
(Byte(addr))
else
:
break
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-1-30 17:00
被kanxue编辑
,原因: