-
-
[原创]去除反混淆后程序中的冗余汇编代码
-
发表于: 2024-7-7 15:02 2795
-
在之前去混淆的过程中,发现去混淆的程序中,伪代码与原程序伪代码相差无几,然而如果仔细观察汇编代码的话,可以发现存在较多的冗余代码,即混淆过程中产生的垃圾代码。那么如何去除这些冗余汇编指令呢?
先给出我们需要解决一个例子(这是一个虚假控制流混淆中的部分代码,其中标*
的为冗余汇编指令):
最开始的想法是从第20行的汇编指令开始,跟踪与该指令以及后续指令相关的指令。如何跟踪呢?简单想就是跟踪这些指令使用到的寄存器。
Liveness Analysis
是一种数据流分析技术,用于确定程序中的每个变量在程序的不同点上是否“活跃”。“活跃”的意思是变量在某个程序点后将被使用,且在这之前没有被重新定义。这项技术在编译器优化、寄存器分配和死代码消除中起着关键作用。
本文章中主要用到了里面的def
和use
集合的想法。
然而从图中看,似乎Liveness Analysis
技术没有考虑到标志寄存器的改变和使用。对于Liveness Analysis
技术能否解决我所预设的问题,我也不太清楚。因此在此基础上,进行如下改进:
对刚才那个例子进行def
和use
集合的分析,结果如下:
具体跟踪的初步想法是:
感觉说的有些混乱,就那上面这个例子解释一下吧。首先我们假设跟踪的指令为jnz loc_401235
,后续步骤如下:
对于上述例子,我们的想法成功的追踪到了所有冗余指令。然而当存在cmovcc
指令时,我们的想法并不是很理想。
以下是控制流平坦化混淆的垃圾汇编指令(其中标*
的为冗余汇编指令)。
由于cmovnz
存在执行和不执行的情况,因此需要分两种情况讨论:
单独分析以上任何一种情况,其结果都不能覆盖所有的冗余指令,但其两者情况的并集能够覆盖所有冗余指令。因此在向上溯源时,需要注意是否时cmovcc
指令,如果是,则需要保存当前状态,探索其中一条路径,当该路径探索完毕后,再探索另一种路径。此情况可通过递归实现。
接下来主要展示代码中重要的部分,完整代码请参考6ecK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6Y4j5h3H3J5P5s2W2Q4x3V1k6m8M7%4y4W2L8h3u0D9k6f1y4G2k6r3g2f1M7X3q4U0k6i4u0Q4c8e0y4Q4z5o6m8Q4z5o6t1`.
对于刚才所展示的例子中,我们可以直观的看出指令的def
和use
集合,但是计算机不行。这是我所面临的第一个麻烦,解决这个问题,后续分析才能进行下去。为此,我建立了一个简单的汇编指令字典,部分示例如下:
由于汇编指令中的操作数有多种类型,且对于不同类型的操作数,我们需要进行不同的操作。
由于指令中的内存可能是一个表达式的情形,例如[2*rax+rbx-1]
,我们可以直观的看出,它肯定使用了rax
、rbx
寄存器,因此我们需要实现一个函数来提取内存表达式中的寄存器。
这一步就需要借助到 3.1 所定义的汇编指令字典。整个函数的功能为:
最终算法如下:
当真实汇编指令与垃圾汇编指令构成了标志寄存器的前者定义后者使用的关系时,此算法会造成误判。具体例子如下所示。(其中标*
的为冗余汇编指令,其余为真实代码)
由于cmovl
指令依赖标志寄存器,因此会将第5行的cmp
指令纳入到相关指令集合中(然而第5行的cmp
指令是真实代码),以至于后续将其余真实代码也囊括进来,最终造成了误判!这个问题似乎解决不了,所以我认为我这个想法到头来还是失败的。
'mov'
: {
'insns'
: [
'mov'
,
'movsx'
,
'movzx'
,
'movsxd'
],
# 类似指令的集合, 由于一些指令的def和use集合可能是相同的,因此这么做是为了避免冗余
'has_types'
:
False
,
# 该指令格式是否有多个,即操作数个数是否不固定
'def_exp_index'
: [
0
],
# 显式def,即在指令中出现的,存储对应索引值
'use_exp_index'
: [
1
],
# 显式use,即在指令中出现的,存储对应索引值
'def_imp_reg'
: [],
# 隐式def,即在指令中未出现的但是又被该指令更改了值的
'use_imp_reg'
: [],
# 隐式use,即在指令中未出现的但是又被该指令使用了的
}
'imul'
: {
'insns'
: [
'imul'
],
'has_types'
:
True
,
'types'
: {
# 指令中的操作数个数为1个
1
: {
'def_exp_index'
: [],
'use_exp_index'
: [
0
],
'def_imp_reg'
: [
'rax'
,
'rflags'
],
'use_imp_reg'
: [
'rax'
],
},
# 指令中的操作数个数为2个
2
: {
'def_exp_index'
: [
0
],
'use_exp_index'
: [
0
,
1
],
'def_imp_reg'
: [
'rflags'
],
'use_imp_reg'
: [],
},
# 指令中的操作数个数为3个
3
: {
'def_exp_index'
: [
0
],
'use_exp_index'
: [
0
,
1
,
2
],
'def_imp_reg'
: [
'rflags'
],
'use_imp_reg'
: [],
}
},
}
'mov'
: {
'insns'
: [
'mov'
,
'movsx'
,
'movzx'
,
'movsxd'
],
# 类似指令的集合, 由于一些指令的def和use集合可能是相同的,因此这么做是为了避免冗余
'has_types'
:
False
,
# 该指令格式是否有多个,即操作数个数是否不固定
'def_exp_index'
: [
0
],
# 显式def,即在指令中出现的,存储对应索引值
'use_exp_index'
: [
1
],
# 显式use,即在指令中出现的,存储对应索引值
'def_imp_reg'
: [],
# 隐式def,即在指令中未出现的但是又被该指令更改了值的
'use_imp_reg'
: [],
# 隐式use,即在指令中未出现的但是又被该指令使用了的
}
'imul'
: {
'insns'
: [
'imul'
],
'has_types'
:
True
,
'types'
: {
# 指令中的操作数个数为1个
1
: {
'def_exp_index'
: [],
'use_exp_index'
: [
0
],
'def_imp_reg'
: [
'rax'
,
'rflags'
],
'use_imp_reg'
: [
'rax'
],
},
# 指令中的操作数个数为2个
2
: {
'def_exp_index'
: [
0
],
'use_exp_index'
: [
0
,
1
],
'def_imp_reg'
: [
'rflags'
],
'use_imp_reg'
: [],
},
# 指令中的操作数个数为3个
3
: {
'def_exp_index'
: [
0
],
'use_exp_index'
: [
0
,
1
,
2
],
'def_imp_reg'
: [
'rflags'
],
'use_imp_reg'
: [],
}
},
}
def
parseOperand(operand:
str
):
operand
=
operand.strip()
# 判断是否为寄存器.后一个捕获r8~r15相关寄存器
if
re.match(r
'^[er]?[abcds][xlhpi]$'
, operand)
or
re.match(r
'(r[89]|r1[0-5])[dwb]?'
, operand):
return
'register'
# 判断是否为内存操作数
elif
re.match(r
'^\[.*\]$'
, operand):
return
'memory'
# 判断是否为立即数
elif
re.match(r
'(?:0[xX])?[0-9A-Fa-f]+h?'
, operand):
return
'immediate'
return
'unknown'
def
parseOperand(operand:
str
):
operand
=
operand.strip()
# 判断是否为寄存器.后一个捕获r8~r15相关寄存器
if
re.match(r
'^[er]?[abcds][xlhpi]$'
, operand)
or
re.match(r
'(r[89]|r1[0-5])[dwb]?'
, operand):
return
'register'
# 判断是否为内存操作数
elif
re.match(r
'^\[.*\]$'
, operand):
return
'memory'
# 判断是否为立即数
elif
re.match(r
'(?:0[xX])?[0-9A-Fa-f]+h?'
, operand):
return
'immediate'
return
'unknown'
def
findRegInMemOp(operand:
str
):
pattern
=
r
'\b([er]?([abcds][xlhpi]|bp|sp)|r[89]|r1[0-5])\b'
registers
=
set
([])
# 查找内存操作数中的所有寄存器
registersInOperand
=
re.findall(pattern, operand)
print
(f
'find registers in memory operand: {registersInOperand}'
)
# [('rbp', 'bp')]
for
registersTuple
in
registersInOperand:
for
register
in
registersTuple:
fullName
=
findFullRegisterName(register)
if
fullName
is
not
None
:
registers.add(fullName)
else
:
print
(f
'Warning!!! Unknow register: {register}, may be you need define it in registers_db'
)
print
(f
'memory operand: {operand}, extract registers: {registers}'
)
return
registers
def
findRegInMemOp(operand:
str
):
pattern
=
r
'\b([er]?([abcds][xlhpi]|bp|sp)|r[89]|r1[0-5])\b'
registers
=
set
([])
# 查找内存操作数中的所有寄存器
registersInOperand
=
re.findall(pattern, operand)
print
(f
'find registers in memory operand: {registersInOperand}'
)
# [('rbp', 'bp')]
for
registersTuple
in
registersInOperand:
for
register
in
registersTuple:
fullName
=
findFullRegisterName(register)
if
fullName
is
not
None
:
registers.add(fullName)
else
:
print
(f
'Warning!!! Unknow register: {register}, may be you need define it in registers_db'
)
print
(f
'memory operand: {operand}, extract registers: {registers}'
)
return
registers
def
getDefAndUseSet(insnsDic:
dict
, operands:
List
):
defSet
=
set
([])
useSet
=
set
([])
# 先找显式定义的
# 遍历需要加入到def集合中的操作数索引
for
pos
in
insnsDic[
'def_exp_index'
]:
# 根据操作数的类型决定是否添加到def集合中
operandType
=
parseOperand(operands[pos])
if
operandType
=
=
'register'
:
# 寄存器直接加入
defSet.add(operands[pos])
elif
operandType
=
=
'memory'
:
# 内存操作数
defSet.add(operands[pos])
# 内存操作数借助的寄存器放入useSet中
registers
=
findRegInMemOp(operands[pos])
useSet.update(registers)
else
:
# 未知类型,例如label标签
continue
# 再找隐式定义的
for
register
in
insnsDic[
'def_imp_reg'
]:
defSet.add(register)
# useSet同理
for
pos
in
insnsDic[
'use_exp_index'
]:
# 根据操作数的类型决定是否添加到use集合中
operandType
=
parseOperand(operands[pos])
if
operandType
=
=
'register'
:
# 寄存器直接加入
useSet.add(operands[pos])
elif
operandType
=
=
'memory'
:
# 内存操作数,则读取表达式中的寄存器并加入
useSet.add(operands[pos])
registers
=
findRegInMemOp(operands[pos])
useSet.update(registers)
else
:
# 未知类型,例如label标签
continue
for
register
in
insnsDic[
'use_imp_reg'
]:
useSet.add(register)
# 由于寄存器存在高低位之分,所以转成最大的寄存器的名称
newDefSet
=
set
([])
for
register
in
defSet:
fullName
=
findFullRegisterName(register)
if
fullName
is
not
None
:
newDefSet.add(fullName)
else
:
# 其他类型的操作数,例如[rbp+var_4], rflags
newDefSet.add(register)
# 将寄存器名全部转成最大位数的寄存器名
newUseSet
=
set
([])
for
register
in
useSet:
fullName
=
findFullRegisterName(register)
if
fullName
is
not
None
:
newUseSet.add(fullName)
else
:
newUseSet.add(register)
return
newDefSet, newUseSet
def
getDefAndUseSet(insnsDic:
dict
, operands:
List
):
defSet
=
set
([])
useSet
=
set
([])
# 先找显式定义的
# 遍历需要加入到def集合中的操作数索引
for
pos
in
insnsDic[
'def_exp_index'
]:
# 根据操作数的类型决定是否添加到def集合中
operandType
=
parseOperand(operands[pos])
if
operandType
=
=
'register'
:
# 寄存器直接加入
defSet.add(operands[pos])
elif
operandType
=
=
'memory'
:
# 内存操作数
defSet.add(operands[pos])
# 内存操作数借助的寄存器放入useSet中
registers
=
findRegInMemOp(operands[pos])
useSet.update(registers)
else
:
# 未知类型,例如label标签
continue
# 再找隐式定义的
for
register
in
insnsDic[
'def_imp_reg'
]:
defSet.add(register)
赞赏
- [原创]去除反混淆后程序中的冗余汇编代码 2796
- [原创]从Clang到Pass加载与执行的流程 24407
- OLLVM混淆源码解读 24613