-
-
[原创]使用unicorn对ollvm字符串进行解密
-
发表于: 2021-6-16 21:57 15985
-
样本是一个比较初级的ollvm混淆,其字符串解密过程在init_array中,所以so文件正常加载到内存中后,其加密的字符串就已经解密后写回原始地址了。
所以我们可以通过unicorn加载so文件,手动将解密后的字符串写回so文件,经过简单的patch就可以静态分析了
本文参考了leadroyal与smartdone大佬的文章。
问题一:
偷懒使用unicorn直接将整个so文件load进memory,读取data的数据全是00,修复后也是错误的,我按照leadroyal大佬的脚本修改后是正确的
问题二:
有时候使用unicorn map后,代码看着没有问题,但是跑起来就报错,可能你需要看看你分配的内存是不是不是1024的倍数
脚本如下:
复制uEmu.py到IDA_Pro_7.5\plugins\下,重启ida
找到函数,设置起始与结束断点:
在需要执行的汇编指令起点,右键-uEmu-start
配置寄存器
ctrl+shift+alt+s单步运行
如果你需要多行执行,单击要执行到某一行,右键-uEmu-run
会自动向下执行到你单击的那一行
查看内存,比如我们要查看0x1F0AD处的内存,看一下解密后的字符串内容:右键-uEmu-show memory range
填写地址与长度,注意长度为10进制,选择add
import
binascii
import
math
import
ida_bytes
import
idaapi
from
capstone
import
*
from
elftools.elf.elffile
import
ELFFile
from
elftools.elf.sections
import
SymbolTableSection
from
keystone
import
*
from
unicorn
import
*
from
unicorn.arm_const
import
*
class
FILFINIT(
object
):
def
__init__(
self
,
file
, mode):
self
.
file
=
file
self
.fd
=
self
.file_init(mode)
self
.elf
=
ELFFile(
self
.fd)
self
.file_size
=
self
.file_size()
self
.data_divs
=
self
.get_data_div()
def
file_init(
self
, mode
=
'rb+'
):
fd
=
open
(
self
.
file
, mode
=
mode)
return
fd
def
file_close(
self
):
self
.fd.close()
def
file_seek_to(
self
, seekto):
self
.fd.seek(seekto,
0
)
def
file_read(
self
, size
=
-
1
):
binary
=
self
.fd.read(size)
return
binary
def
file_write(
self
, data):
self
.fd.write(data)
def
file_size(
self
):
import
os
return
os.path.getsize(
self
.
file
)
def
get_data_div(
self
):
data_divs
=
[]
if
self
.elf:
symtab
=
self
.elf.get_section_by_name(
'.dynsym'
)
assert
isinstance
(symtab, SymbolTableSection)
for
sym
in
symtab.iter_symbols():
# sym.name:str
if
sym.name.startswith(
'.datadiv'
):
data_divs.append(sym.entry.st_value)
return
data_divs
class
UCINIT(
object
):
def
__init__(
self
,
file
, mode, offset
=
0x10000
, stack_size
=
0x10000
, data_size
=
0x10000
):
self
.cs
=
Cs(CS_ARCH_ARM, CS_MODE_THUMB)
self
.fileinfo
=
FILFINIT(
file
, mode)
self
.data_divs
=
self
.uc_filter()
self
.func_start_and_end
=
self
.get_func_start_and_end()
self
.uc
=
unicorn.Uc(UC_ARCH_ARM, UC_MODE_THUMB)
self
.image_base
=
0x10000
self
.offset
=
offset
self
.stack_size
=
stack_size
self
.data_size
=
data_size
self
.image_size
=
(math.ceil(
self
.fileinfo.file_size
/
self
.offset)
+
1
)
*
self
.offset
self
.uc.mem_map(
self
.image_base,
self
.image_size)
self
.init_seg()
# self.uc.mem_write(self.image_base, self.fileinfo.file_read()) # 全文件导入是错误的,不知道为什么
self
.stack_base
=
self
.image_base
+
self
.image_size
+
self
.offset
self
.stack_top
=
self
.stack_base
+
self
.stack_size
-
0x1000
self
.uc.mem_map(
self
.stack_base,
self
.stack_size)
self
.uc.reg_write(UC_ARM_REG_SP,
self
.stack_top)
self
.data_base
=
self
.stack_base
+
self
.stack_size
+
self
.offset
self
.uc.mem_map(
self
.data_base,
self
.data_size)
def
init_seg(
self
):
segs
=
[seg
for
seg
in
self
.fileinfo.elf.iter_segments()
if
seg.header.p_type
=
=
'PT_LOAD'
]
for
seg
in
segs:
self
.uc.mem_write(
self
.image_base
+
seg.header.p_vaddr, seg.data())
def
init_reg(
self
, regs):
index
=
0
for
reg
in
regs:
reg_num
=
66
+
index
self
.uc.reg_write(reg_num, reg)
def
uc_run(
self
, target, end):
targ
=
target
+
self
.image_base
util
=
self
.image_base
+
end
self
.uc.emu_start(targ, util)
def
uc_stop(
self
):
self
.uc.emu_stop()
self
.fileinfo.file_close()
def
register_hook_code(
self
, htype, callback):
self
.uc.hook_add(htype, callback)
def
uc_filter(
self
, size
=
0x2
):
data_divs
=
[]
for
data_div
in
self
.fileinfo.data_divs:
self
.fileinfo.file_seek_to(data_div
-
1
)
data
=
self
.fileinfo.file_read(size)
for
inis
in
self
.cs.disasm(data,
0
):
if
inis.mnemonic
=
=
'bx'
and
inis.op_str
=
=
'lr'
:
continue
else
:
data_divs.append(data_div)
self
.fileinfo.file_seek_to(
0
)
return
data_divs
def
get_func_start_and_end(
self
):
data_divs
=
[]
for
data_div
in
self
.data_divs:
func
=
idaapi.get_func(data_div)
func_start_and_end
=
(data_div, func.end_ea
-
0x2
)
data_divs.append(func_start_and_end)
return
data_divs
def
get_elf_data(
self
):
data_section_header
=
self
.fileinfo.elf.get_section_by_name(
'.data'
).header
print
(
'[+] .data addr {}-{}'
.
format
(
hex
(data_section_header.sh_addr),
hex
(data_section_header.sh_size)))
new_data
=
self
.uc.mem_read(
self
.image_base
+
data_section_header.sh_addr, data_section_header.sh_size)
print
(
'[+] .data binary {}'
.
format
(binascii.hexlify(new_data)))
return
new_data
def
patch_so_data(
self
):
data_section_header
=
self
.fileinfo.elf.get_section_by_name(
'.data'
).header
data_addr
=
data_section_header.sh_addr
# self.fileinfo.file_seek_to(data_addr)
# self.fileinfo.file_write(self.get_elf_data())
new_data
=
self
.get_elf_data()
# new_data: bytearray
print
(
'[+] patch so data addr {}\n {}'
.
format
(
hex
(data_addr), bytes(new_data)))
ida_bytes.patch_bytes(data_addr, bytes(new_data))
def
patch_data_div(
self
):
ks
=
Ks(KS_ARCH_ARM, KS_MODE_THUMB)
for
data_div
in
self
.data_divs:
binary
=
ks.asm(
'bx lr'
)
print
(bytes(binary[
0
]))
# print(struct.pack('B', binary[0]))
print
(
'[+] patch so text .datadiv {}\n {}'
.
format
(
hex
(data_div
-
1
), bytes(binary[
0
])))
ida_bytes.patch_bytes(data_div
-
1
, bytes(binary[
0
]))
def
hook_code(uc: unicorn.Uc, address, size, user_data):
cs
=
Cs(CS_ARCH_ARM, CS_MODE_THUMB)
me
=
uc.mem_read(address, size)
for
code
in
cs.disasm(me, size):
print
(
"[+] Tracing instructions at 0x%x, instructions size = ix%x, inst: %s %s"
%
(
address, size, code.mnemonic, code.op_str))
def
hook_mem_access(uc, access, address, size, value, user_data):
if
access
=
=
UC_MEM_WRITE:
print
(
'[+] Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x'
%
(address, size, value))
else
:
# READ
me
=
uc.mem_read(address, size)
print
(
'[+] Memory is being READ at 0x%x, data size = %u, data value = 0x%s'
%
(
address, size, binascii.hexlify(me)))
if
__name__
=
=
'__main__'
:
print
(
'[+] So file parse starting!'
)
so
=
UCINIT(
'libcrack.so'
,
'rb'
)
# so.get_elf_data()
for
start, end
in
so.func_start_and_end:
print
(
'[+] Data div addr {}-{}'
.
format
(
hex
(start),
hex
(end)))
so.init_reg((
0
,
0
,
0
))
# so.register_hook_code(UC_HOOK_CODE, hook_code)
# so.register_hook_code(UC_HOOK_MEM_READ, hook_mem_access)
# so.register_hook_code(UC_HOOK_MEM_WRITE, hook_mem_access)
so.uc_run(start, end)
so.patch_data_div()
# so.patch_so_data()
# so.get_elf_data()
so.uc_stop()
print
(
'[+] So file run finished!'
)
import
binascii
import
math
import
ida_bytes
import
idaapi
from
capstone
import
*
from
elftools.elf.elffile
import
ELFFile
from
elftools.elf.sections
import
SymbolTableSection
from
keystone
import
*
from
unicorn
import
*
from
unicorn.arm_const
import
*
class
FILFINIT(
object
):
def
__init__(
self
,
file
, mode):
self
.
file
=
file
self
.fd
=
self
.file_init(mode)
self
.elf
=
ELFFile(
self
.fd)
self
.file_size
=
self
.file_size()
self
.data_divs
=
self
.get_data_div()
def
file_init(
self
, mode
=
'rb+'
):
fd
=
open
(
self
.
file
, mode
=
mode)
return
fd
def
file_close(
self
):
self
.fd.close()
def
file_seek_to(
self
, seekto):
self
.fd.seek(seekto,
0
)
def
file_read(
self
, size
=
-
1
):
binary
=
self
.fd.read(size)
return
binary
def
file_write(
self
, data):
self
.fd.write(data)
def
file_size(
self
):
import
os
return
os.path.getsize(
self
.
file
)
def
get_data_div(
self
):
data_divs
=
[]
if
self
.elf:
symtab
=
self
.elf.get_section_by_name(
'.dynsym'
)
assert
isinstance
(symtab, SymbolTableSection)
for
sym
in
symtab.iter_symbols():
# sym.name:str
if
sym.name.startswith(
'.datadiv'
):
data_divs.append(sym.entry.st_value)
return
data_divs
class
UCINIT(
object
):
def
__init__(
self
,
file
, mode, offset
=
0x10000
, stack_size
=
0x10000
, data_size
=
0x10000
):
self
.cs
=
Cs(CS_ARCH_ARM, CS_MODE_THUMB)
self
.fileinfo
=
FILFINIT(
file
, mode)
self
.data_divs
=
self
.uc_filter()
self
.func_start_and_end
=
self
.get_func_start_and_end()
self
.uc
=
unicorn.Uc(UC_ARCH_ARM, UC_MODE_THUMB)
self
.image_base
=
0x10000
self
.offset
=
offset
self
.stack_size
=
stack_size
self
.data_size
=
data_size
self
.image_size
=
(math.ceil(
self
.fileinfo.file_size
/
self
.offset)
+
1
)
*
self
.offset
self
.uc.mem_map(
self
.image_base,
self
.image_size)
self
.init_seg()
# self.uc.mem_write(self.image_base, self.fileinfo.file_read()) # 全文件导入是错误的,不知道为什么
self
.stack_base
=
self
.image_base
+
self
.image_size
+
self
.offset
self
.stack_top
=
self
.stack_base
+
self
.stack_size
-
0x1000
self
.uc.mem_map(
self
.stack_base,
self
.stack_size)
self
.uc.reg_write(UC_ARM_REG_SP,
self
.stack_top)
self
.data_base
=
self
.stack_base
+
self
.stack_size
+
self
.offset
self
.uc.mem_map(
self
.data_base,
self
.data_size)
def
init_seg(
self
):
segs
=
[seg
for
seg
in
self
.fileinfo.elf.iter_segments()
if
seg.header.p_type
=
=
'PT_LOAD'
]
for
seg
in
segs:
self
.uc.mem_write(
self
.image_base
+
seg.header.p_vaddr, seg.data())
def
init_reg(
self
, regs):
index
=
0
for
reg
in
regs:
reg_num
=
66
+
index
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-6-16 21:58
被新萌编辑
,原因: 修改样式
赞赏记录
参与人
雪币
留言
时间
B1ink
为你点赞~
2023-10-23 13:47
一笑人间万事
为你点赞~
2022-7-28 00:27
Youlor
为你点赞~
2022-7-17 11:23
2DCoXrq
为你点赞~
2021-6-18 22:14
wusha
为你点赞~
2021-6-17 12:30
赞赏
谁下载
看原图
赞赏
雪币:
留言: