这道题主要考察的是对多层jni调用的模拟,所谓多层jni调用指native java
函数的实现c/c++函数中又反过来调用java函数,而这个被调用的java函数仍然是一个native函数。而对于AndroidNativeEmu
和UniDbg
目前(做题的时候)都不支持这种多层调用,UniDbg
更新应该算是比较频繁的,有可能后边会支持。
不能直接支持,只能用取巧的方式来实现。我这里用了两个程序来实现,一个程序通过subprocess
来调用另外一个程序,获取这个程序的结果进一步利用。这种实现方式是有缺陷的,如果模拟的程序函数对全局的变量产生了副作用,或者有共同的依赖,那么模拟可能会失败,在这种情况下需要做特殊的处理,单独处理同步共享的全局变量。
通过这个题目的练习可以学得AndroidNativeEmu
和UniDbg
不支持的地方,或者说有待改进的点。工具不支持,必要时必须自己写代码扩展功能。
单独为crypt2
函数写一个源文件,crypt2_impl.py
文件内容为:
main.py
文件内容:
当输入值为'XUe'时打印出来的结果为:
当输入值不为'XUe'时打印出来的结果为:
模拟成功。
import
logging
import
sys
from
androidemu.emulator
import
Emulator
from
androidemu.java.helpers.native_method
import
native_method
from
androidemu.utils
import
memory_helpers
@native_method
def
sprintf(uc,
str
,
format
, args):
str_utf8
=
memory_helpers.read_utf8(uc,
str
)
format_utf8
=
memory_helpers.read_utf8(uc,
format
)
args_utf8
=
memory_helpers.read_utf8(uc, args)
logger.info(
"str_utf8:{}, format_utf8:{}, args_utf8:{}"
.
format
(str_utf8, format_utf8, args_utf8))
result_string
=
format_utf8
%
args_utf8
memory_helpers.write_utf8(uc,
str
, result_string)
return
len
(result_string)
@native_method
def
malloc(uc, size):
return
emulator.memory_manager.allocate(size)
@native_method
def
strcmp(uc, s1, s2):
s1_utf8
=
memory_helpers.read_utf8(uc, s1)
s2_utf8
=
memory_helpers.read_utf8(uc, s2)
if
s1_utf8
=
=
s2_utf8:
ret
=
0
elif
s1_utf8 < s2_utf8:
ret
=
-
1
else
:
ret
=
1
logger.info(
"s1:{},s2:{},ret:{}"
.
format
(s1_utf8, s2_utf8, ret))
return
ret
logging.basicConfig(
stream
=
sys.stderr,
level
=
logging.DEBUG,
format
=
"%(asctime)s %(levelname)7s %(name)34s | %(message)s"
)
logger
=
logging.getLogger(__name__)
so_file
=
"my_binaries/libnative-lib.so"
libc_file
=
"example_binaries/32/libc.so"
emulator
=
Emulator(vfp_inst_set
=
True
)
emulator.modules.add_symbol_hook(
'sprintf'
, emulator.hooker.write_function(sprintf)
+
1
)
emulator.modules.add_symbol_hook(
'malloc'
, emulator.hooker.write_function(malloc)
+
1
)
emulator.modules.add_symbol_hook(
'strcmp'
, emulator.hooker.write_function(strcmp)
+
1
)
libc_module
=
emulator.load_library(libc_file, do_init
=
False
)
lib_module
=
emulator.load_library(so_file, do_init
=
False
)
emulator.call_native(lib_module.base
+
0x320B8
+
1
)
for
funcptr
in
lib_module.init_array:
logger.info(
"init_array -->"
+
str
(
hex
(funcptr)))
emulator.call_native(funcptr)
result
=
emulator.call_symbol(lib_module,
"Java_com_kanxue_crackme_MainActivity_crypt2"
,
emulator.java_vm.jni_env.address_ptr,
0
, sys.argv[
1
], is_return_jobject
=
False
)
print
(result)
import
logging
import
sys
from
androidemu.emulator
import
Emulator
from
androidemu.java.helpers.native_method
import
native_method
from
androidemu.utils
import
memory_helpers
@native_method
def
sprintf(uc,
str
,
format
, args):
str_utf8
=
memory_helpers.read_utf8(uc,
str
)
format_utf8
=
memory_helpers.read_utf8(uc,
format
)
args_utf8
=
memory_helpers.read_utf8(uc, args)
logger.info(
"str_utf8:{}, format_utf8:{}, args_utf8:{}"
.
format
(str_utf8, format_utf8, args_utf8))
result_string
=
format_utf8
%
args_utf8
memory_helpers.write_utf8(uc,
str
, result_string)
return
len
(result_string)
@native_method
def
malloc(uc, size):
return
emulator.memory_manager.allocate(size)
@native_method
def
strcmp(uc, s1, s2):
s1_utf8
=
memory_helpers.read_utf8(uc, s1)
s2_utf8
=
memory_helpers.read_utf8(uc, s2)
if
s1_utf8
=
=
s2_utf8:
ret
=
0
elif
s1_utf8 < s2_utf8:
ret
=
-
1
else
:
ret
=
1
logger.info(
"s1:{},s2:{},ret:{}"
.
format
(s1_utf8, s2_utf8, ret))
return
ret
logging.basicConfig(
stream
=
sys.stderr,
level
=
logging.DEBUG,
format
=
"%(asctime)s %(levelname)7s %(name)34s | %(message)s"
)
logger
=
logging.getLogger(__name__)
so_file
=
"my_binaries/libnative-lib.so"
libc_file
=
"example_binaries/32/libc.so"
emulator
=
Emulator(vfp_inst_set
=
True
)
emulator.modules.add_symbol_hook(
'sprintf'
, emulator.hooker.write_function(sprintf)
+
1
)
emulator.modules.add_symbol_hook(
'malloc'
, emulator.hooker.write_function(malloc)
+
1
)
emulator.modules.add_symbol_hook(
'strcmp'
, emulator.hooker.write_function(strcmp)
+
1
)
libc_module
=
emulator.load_library(libc_file, do_init
=
False
)
lib_module
=
emulator.load_library(so_file, do_init
=
False
)
emulator.call_native(lib_module.base
+
0x320B8
+
1
)
for
funcptr
in
lib_module.init_array:
logger.info(
"init_array -->"
+
str
(
hex
(funcptr)))
emulator.call_native(funcptr)
result
=
emulator.call_symbol(lib_module,
"Java_com_kanxue_crackme_MainActivity_crypt2"
,
emulator.java_vm.jni_env.address_ptr,
0
, sys.argv[
1
], is_return_jobject
=
False
)
print
(result)
import
logging
import
sys
import
unicorn
import
capstone
from
androidemu.emulator
import
Emulator
from
androidemu.utils
import
memory_helpers
from
androidemu.java.java_class_def
import
JavaClassDef
from
androidemu.java.java_method_def
import
java_method_def
from
androidemu.java.helpers.native_method
import
native_method
from
subprocess
import
check_output
cs
=
capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB)
cs.detail
=
True
class
MainActivity(metaclass
=
JavaClassDef, jvm_name
=
'com/kanxue/crackme/MainActivity'
):
def
__init__(
self
):
pass
@java_method_def
(name
=
'crypt2'
, args_list
=
[
'jstring'
], signature
=
'(Ljava/lang/String;)Z'
, native
=
False
)
def
crypt2(
self
,
*
args,
*
*
kwargs):
arg
=
args[
0
].value
print
(
"crypt2 args : {}"
.
format
(arg))
ret
=
check_output([
"python"
,
"crypt2_impl.py"
,arg])
ret
=
int
(ret.decode(
'utf-8'
).strip())
print
(
"crypt2 result : {}"
.
format
(ret))
return
ret
@native_method
def
sprintf(uc,
str
,
format
, args):
str_utf8
=
memory_helpers.read_utf8(uc,
str
)
format_utf8
=
memory_helpers.read_utf8(uc,
format
)
args_utf8
=
memory_helpers.read_utf8(uc, args)
print
(
"str_utf8:{}, format_utf8:{}, args_utf8:{}"
.
format
(str_utf8, format_utf8, args_utf8))
result_string
=
format_utf8
%
args_utf8
memory_helpers.write_utf8(uc,
str
, result_string)
return
len
(result_string)
@native_method
def
malloc(uc, size):
return
emulator.memory_manager.allocate(size)
@native_method
def
strcmp(uc, s1, s2):
s1_utf8
=
memory_helpers.read_utf8(uc, s1)
s2_utf8
=
memory_helpers.read_utf8(uc, s2)
if
s1_utf8
=
=
s2_utf8:
return
0
print
(
"s1:{},s2:{}"
.
format
(s1_utf8, s2_utf8))
return
len
(s1_utf8)
-
len
(s2_utf8)
logging.basicConfig(
stream
=
sys.stdout,
level
=
logging.DEBUG,
format
=
"%(asctime)s %(levelname)7s %(name)34s | %(message)s"
)
logger
=
logging.getLogger(__name__)
def
mem_read_unmapped(uc,
type
, address, size, value, user_data):
print
(
"mem_read_unmapped type:{},address:{},size:{},value:{},pc:{}"
.
format
(
type
, address, size, value,
hex
(uc.reg_read(
unicorn.arm_const.UC_ARM_REG_PC)
-
libc_module.base)))
def
hook_code(uc, address, size, user_data):
if
lib_module.base < address < (lib_module.base
+
lib_module.size):
lib_name
=
"libnative-lib.so"
pc
=
uc.reg_read(unicorn.arm_const.UC_ARM_REG_PC)
-
lib_module.base
elif
libc_module.base < address < (libc_module.base
+
libc_module.size):
lib_name
=
"libc.so"
pc
=
uc.reg_read(unicorn.arm_const.UC_ARM_REG_PC)
-
libc_module.base
else
:
lib_name
=
"lib_Unknown"
pc
=
uc.reg_read(unicorn.arm_const.UC_ARM_REG_PC)
CODE
=
uc.mem_read(uc.reg_read(unicorn.arm_const.UC_ARM_REG_PC), size)
dis_code
=
None
for
i
in
cs.disasm(CODE,
0
,
len
(CODE)):
dis_code
=
i.mnemonic
+
" "
+
i.op_str
print
(lib_name
+
" >>> Tracing instruction at {}, instruction size = {} , pc:{}, bytes:{}"
.
format
(pc, size,
hex
(pc),
dis_code))
def
UC_HOOK_MEM_WRITE(uc,
type
, address, size, value, userdata):
print
(
"address:"
+
str
(
hex
(address))
+
",value:"
+
str
(value))
so_file
=
"my_binaries/libnative-lib.so"
libc_file
=
"example_binaries/32/libc.so"
emulator
=
Emulator(vfp_inst_set
=
True
)
emulator.modules.add_symbol_hook(
'sprintf'
, emulator.hooker.write_function(sprintf)
+
1
)
emulator.modules.add_symbol_hook(
'malloc'
, emulator.hooker.write_function(malloc)
+
1
)
emulator.modules.add_symbol_hook(
'strcmp'
, emulator.hooker.write_function(strcmp)
+
1
)
libc_module
=
emulator.load_library(libc_file, do_init
=
False
)
lib_module
=
emulator.load_library(so_file, do_init
=
False
)
emulator.call_native(lib_module.base
+
0x320B8
+
1
)
for
funcptr
in
lib_module.init_array:
logger.info(
"init_array -->"
+
str
(
hex
(funcptr)))
emulator.call_native(funcptr)
emulator.java_classloader.add_class(MainActivity)
result
=
emulator.call_symbol(lib_module,
"Java_com_kanxue_crackme_MainActivity_jnicheck"
,
emulator.java_vm.jni_env.address_ptr,
0
,
"XUe"
, is_return_jobject
=
False
)
print
(
"jnicheck result : {}"
.
format
(result))
import
logging
import
sys
import
unicorn
import
capstone
from
androidemu.emulator
import
Emulator
from
androidemu.utils
import
memory_helpers
from
androidemu.java.java_class_def
import
JavaClassDef
from
androidemu.java.java_method_def
import
java_method_def
from
androidemu.java.helpers.native_method
import
native_method
from
subprocess
import
check_output
cs
=
capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB)
cs.detail
=
True
class
MainActivity(metaclass
=
JavaClassDef, jvm_name
=
'com/kanxue/crackme/MainActivity'
):
def
__init__(
self
):
pass
@java_method_def
(name
=
'crypt2'
, args_list
=
[
'jstring'
], signature
=
'(Ljava/lang/String;)Z'
, native
=
False
)
def
crypt2(
self
,
*
args,
*
*
kwargs):
arg
=
args[
0
].value
print
(
"crypt2 args : {}"
.
format
(arg))
ret
=
check_output([
"python"
,
"crypt2_impl.py"
,arg])
ret
=
int
(ret.decode(
'utf-8'
).strip())
print
(
"crypt2 result : {}"
.
format
(ret))
return
ret
@native_method
def
sprintf(uc,
str
,
format
, args):
str_utf8
=
memory_helpers.read_utf8(uc,
str
)
format_utf8
=
memory_helpers.read_utf8(uc,
format
)
args_utf8
=
memory_helpers.read_utf8(uc, args)
print
(
"str_utf8:{}, format_utf8:{}, args_utf8:{}"
.
format
(str_utf8, format_utf8, args_utf8))
result_string
=
format_utf8
%
args_utf8
memory_helpers.write_utf8(uc,
str
, result_string)
return
len
(result_string)
@native_method
def
malloc(uc, size):
return
emulator.memory_manager.allocate(size)
@native_method
def
strcmp(uc, s1, s2):
s1_utf8
=
memory_helpers.read_utf8(uc, s1)
s2_utf8
=
memory_helpers.read_utf8(uc, s2)
if
s1_utf8
=
=
s2_utf8:
return
0
print
(
"s1:{},s2:{}"
.
format
(s1_utf8, s2_utf8))
return
len
(s1_utf8)
-
len
(s2_utf8)
logging.basicConfig(
stream
=
sys.stdout,
level
=
logging.DEBUG,
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2022-7-17 18:06
被飞翔的猫咪编辑
,原因: