sub_25CDC这个函数就是字符串解密函数,byte_218F01是标记有没有解密的

字符串解密函数就是一个异或,参数第一个存放解密后字符串,第二个是加密的字符串,第三个是密钥,第四个是长度

字符串还是挺多的,需要批量处理一下

解密算法已经知道了,直接从解密函数调用前的10个指令开始模拟执行到解密函数,得到解密函数的4个参数
找解密函数调用处的前10个指令时,需要判断一下是不是bxx之类的分支指令,是的话就不往前找了,避免模拟执行跑飞
然后根据参数解密出字符串,patch到第一个参数的地址
最后会有十几个字符串参数识别有问题,手动处理一下就可以

如果是把字符串解密函数直接内联到使用字符串的函数代码中可能混淆效果好一些
直接看JNI_Onload,先是给W0赋了个值

然后来到0x24980调用sub_25d0c

函数从x30加上w0<<2的内存位置取了一个dword,然后加到x30

函数调用时会把返回地址放到link register(x30),所以取数据的位置就是0x24980下面
上面是取了[0x24984 + 2<<2],也就是0x78

加到x30之后ret来到0x0249FC

查看0x24980的地方发现还有另外几个,并且sub_25D0C也不止有0x24980调用
可以猜测是每个函数都有有类似0x24980这种类似分发器的东西,通过上面的运算分发到函数内的基本块上


还是要用脚本批量处理一下
找到BL sub_25d0c的所有调用处,然后每个调用处向前找3条指令开始模拟执行,找到取数据时的下标,取出对应的偏移加回去得到正确的跳转位置,然后用keystone汇编一下指令,再patch回BL sub_25d0c的调用处
当然也不一定非要去模拟执行,这个so中BL sub_25d0c的所有调用处的前一条指令刚好都是赋值下标的指令,也就是LDR W0, xxx ,所以直接匹配也是可以

patch完成之后,应用到so文件,重新用ida打开就可以看到函数都可以正常分析了


看JNI_Onload

sub_24A74里分配了一些空间,然后调用sub_24D20

sub_24D20中对JNINativeInterface里的函数全部重新映射了一遍,我们直接建个新的struct映射一下就好

最后映射完会发现4个reserve没有映射,所以出现了部分方法出现2次,这里直接把其中一个方法加个前缀保留

导入

修改类型重新反编译

import
flare_emu
from
idc
import
*
from
idaapi
import
*
import
idaapi
import
ida_allins
from
ida_ida
import
*
from
idautils
import
*
import
unicorn
from
keystone
import
*
from
unicorn
import
arm64_const
my_EH
=
flare_emu.EmuHelper()
def
emu_last_x_insn(ea, insn_cnt
=
10
, skip_ea
=
True
, stop_in_brach
=
True
, reg_info
=
{}):
count
=
0
curr_addr
=
ea
while
count < insn_cnt:
curr_addr
=
prev_head(curr_addr,
0
)
insn
=
idaapi.insn_t()
length
=
idaapi.decode_insn(insn, curr_addr)
if
stop_in_brach:
if
(insn.get_canon_feature() & CF_JUMP) !
=
0
or
(insn.get_canon_feature() & CF_CALL) !
=
0
or
insn.itype
in
[
ARM_b, ARM_bl, ARM_br, ARM_bx, ARM_blx1, ARM_blx2, ARM_cbz, ARM_cbnz, ARM_blr,
ARM_tbz, ARM_tbnz, ARM_ldrpc, ARM_ret] :
curr_addr
=
next_head(curr_addr, BADADDR)
break
count
+
=
1
start_addr
=
curr_addr
end_addr
=
ea
my_EH.emulateRange(start_addr, end_addr, registers
=
reg_info, count
=
insn_cnt)
return
my_EH.uc
def
is_valid_address(addr, segment_name
=
".data"
):
seg
=
get_segm_by_name(segment_name)
return
addr >
=
seg.start_ea
and
addr <
=
seg.end_ea
def
is_valid_string(string):
for
c
in
string:
if
c
not
in
string.printable:
return
False
return
True
def
dec_str():
func_addr
=
0x25CDC
all_used_addr
=
list
(CodeRefsTo(func_addr,
False
))
error_process_addr
=
[]
for
used_addr
in
all_used_addr:
try
:
uc_stata
=
emu_last_x_insn(used_addr, reg_info
=
{
"X0"
:
0
,
"X1"
:
0
,
"X2"
:
0
,
"X3"
:
0
})
x0
=
uc_stata.reg_read(arm64_const.UC_ARM64_REG_X0)
x1
=
uc_stata.reg_read(arm64_const.UC_ARM64_REG_X1)
x2
=
uc_stata.reg_read(arm64_const.UC_ARM64_REG_X2)
x3
=
uc_stata.reg_read(arm64_const.UC_ARM64_REG_X3)
out_addr, enc_addr, key, lens
=
x0, x1, x2, x3
if
is_valid_address(enc_addr):
curr_str_bytes
=
[]
for
i
in
range
(lens):
curr_char
=
get_wide_byte(enc_addr
+
i)
curr_char ^
=
key
key
+
=
3
key
%
=
256
curr_str_bytes.append(curr_char)
curr_str
=
bytearray(curr_str_bytes).decode(
'utf-8'
)
if
is_valid_address(out_addr):
for
k
in
range
(lens):
patch_byte(out_addr
+
k, curr_str_bytes[k])
create_strlit(out_addr,
0
,STRTYPE_C)
print
(f
"patched {hex(out_addr)} with {curr_str}"
)
else
:
error_process_addr.append(used_addr)
else
:
error_process_addr.append(used_addr)
except
Exception as e:
error_process_addr.append(used_addr)
print
()
for
addr
in
error_process_addr:
print
(
hex
(addr),end
=
' '
)
import
flare_emu
from
idc
import
*
from
idaapi
import
*
import
idaapi
import
ida_allins
from
ida_ida
import
*
from
idautils
import
*
import
unicorn
from
keystone
import
*
from
unicorn
import
arm64_const
my_EH
=
flare_emu.EmuHelper()
def
emu_last_x_insn(ea, insn_cnt
=
10
, skip_ea
=
True
, stop_in_brach
=
True
, reg_info
=
{}):
count
=
0
curr_addr
=
ea
while
count < insn_cnt:
curr_addr
=
prev_head(curr_addr,
0
)
insn
=
idaapi.insn_t()
length
=
idaapi.decode_insn(insn, curr_addr)
if
stop_in_brach:
if
(insn.get_canon_feature() & CF_JUMP) !
=
0
or
(insn.get_canon_feature() & CF_CALL) !
=
0
or
insn.itype
in
[
ARM_b, ARM_bl, ARM_br, ARM_bx, ARM_blx1, ARM_blx2, ARM_cbz, ARM_cbnz, ARM_blr,
ARM_tbz, ARM_tbnz, ARM_ldrpc, ARM_ret] :
curr_addr
=
next_head(curr_addr, BADADDR)
break
count
+
=
1
start_addr
=
curr_addr
end_addr
=
ea
my_EH.emulateRange(start_addr, end_addr, registers
=
reg_info, count
=
insn_cnt)
return
my_EH.uc
def
is_valid_address(addr, segment_name
=
".data"
):
seg
=
get_segm_by_name(segment_name)
return
addr >
=
seg.start_ea
and
addr <
=
seg.end_ea
def
is_valid_string(string):
for
c
in
string:
if
c
not
in
string.printable:
return
False
return
True
def
dec_str():
func_addr
=
0x25CDC
all_used_addr
=
list
(CodeRefsTo(func_addr,
False
))
error_process_addr
=
[]
for
used_addr
in
all_used_addr:
try
:
uc_stata
=
emu_last_x_insn(used_addr, reg_info
=
{
"X0"
:
0
,
"X1"
:
0
,
"X2"
:
0
,
"X3"
:
0
})
x0
=
uc_stata.reg_read(arm64_const.UC_ARM64_REG_X0)
x1
=
uc_stata.reg_read(arm64_const.UC_ARM64_REG_X1)
x2
=
uc_stata.reg_read(arm64_const.UC_ARM64_REG_X2)
x3
=
uc_stata.reg_read(arm64_const.UC_ARM64_REG_X3)
[注意]看雪招聘,专注安全领域的专业人才平台!
最后于 2024-9-29 01:49
被k423编辑
,原因: 更新