Unidbg
Unidbg的作用是模拟执行so中的函数,处于Native层.而Java层的函数可以通过JNI调用Native层函数,那么Native层也可以通过JNI去调用Java层的函数.
在Native层调用Java层函数的时候,因为Unidbg中没有这些函数的实现,模拟执行必然失败.故我们需要在Unidbg中补充这些Java层函数,让Native层的函数去调用.
补环境入门
测试Apk
DogPro.apk
模拟执行
使用Unidbg模拟执行so中的getHash函数,初始化代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.example.luodst ;
import com.github.unidbg.AndroidEmulator ;
import com.github.unidbg.LibraryResolver ;
import com.github.unidbg.Module ;
import com.github.unidbg.arm.backend.Unicorn2Factory ;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder ;
import com.github.unidbg.linux.android.AndroidResolver ;
import com.github.unidbg.linux.android.dvm.* ;
import com.github.unidbg.memory.Memory ;
import com.github.unidbg.virtualmodule.android.AndroidModule ;
import java.io.File ;
public class MainActivity extends AbstractJni {
public static void main ( String [] args ) {
MainActivity mainActivity = new MainActivity ();
mainActivity . getHash ();
}
private final AndroidEmulator emulator ;
private final VM vm ;
private final Module module ;
private MainActivity () {
//1.创建Android模拟器实例
emulator = AndroidEmulatorBuilder
. for32Bit ()
. addBackendFactory ( new Unicorn2Factory ( true ))
. setRootDir ( new File ( "unidbg-android/src/test/java/com/example/luodst/rootfs" ))
. build ();
//2.获取操作内存的接口
Memory memory = emulator . getMemory ();
//3.设置Android SDK 版本
LibraryResolver resolver = new AndroidResolver ( 23 );
memory . setLibraryResolver ( resolver );
//4.创建虚拟机
vm = emulator . createDalvikVM ( new File ( "unidbg-android/src/test/java/com/example/luodst/files/DogPro.apk" ));
//5.是否打印日志
vm . setVerbose ( false );
//6.设置jni
vm . setJni ( this );
new AndroidModule ( emulator , vm ). register ( memory );
//7.加载目标so文件
DalvikModule dm = vm . loadLibrary ( "dogpro" , true );
//8.将so文件对应的Module存入成员变量
module = dm . getModule ();
//9.调用JNI_OnLoad
dm . callJNI_OnLoad ( emulator );
}
private void getHash () {
DvmObject <?> dvmObject = vm . resolveClass ( "com/example/dogpro/MainActivity" ). newObject ( null );
String input = "unidbg-android/src/test/java/com/example/luodst/files/dogpro.apk" ;
DvmObject <?> ret = dvmObject . callJniMethodObject ( emulator , "getHash(Ljava/lang/String;)Ljava/lang/String;" , input );
System . out . println ( "result ==> " + ret . getValue ());
}
}
运行上述代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / util / zip / ZipFile ->< init > ( Ljava / lang / String ;) V
at com . github . unidbg . linux . android . dvm . AbstractJni . newObjectV ( AbstractJni . java : 803 )
at com . github . unidbg . linux . android . dvm . AbstractJni . newObjectV ( AbstractJni . java : 758 )
at com . github . unidbg . linux . android . dvm . DvmMethod . newObjectV ( DvmMethod . java : 214 )
at com . github . unidbg . linux . android . dvm . DalvikVM$26 . handle ( DalvikVM . java : 415 )
这是一个构造方法,传入的参数类型为String,可以用下述代码去进行对象构造.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public DvmObject <?> newObjectV ( BaseVM vm , DvmClass dvmClass , String signature , VaList vaList ) {
switch ( signature ) {
case "java/util/zip/ZipFile-><init>(Ljava/lang/String;)V" :{
String name = ( String ) vaList . getObjectArg ( 0 ). getValue ();
try {
ZipFile zipFile = new ZipFile ( name );
return vm . resolveClass ( "java/util/zip/ZipFile" ). newObject ( zipFile );
} catch ( IOException e ) {
throw new RuntimeException ( e );
}
}
}
return super . newObjectV ( vm , dvmClass , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / util / zip / ZipFile -> entries () Ljava / util / Enumeration ;
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 417 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 262 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callObjectMethodV ( DvmMethod . java : 89 )
at com . github . unidbg . linux . android . dvm . DalvikVM$32 . handle ( DalvikVM . java : 553 )
上述代码提示,缺少了ZipFile的entries方法,修补代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/util/zip/ZipFile->entries()Ljava/util/Enumeration;" : {
//拿操作的对象
ZipFile zipFile = ( ZipFile ) dvmObject . getValue ();
//通过对象来调用方法
Enumeration <? extends ZipEntry > entries = zipFile . entries ();
return vm . resolveClass ( "java/util/Enumeration" ). newObject ( entries );
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . ClassCastException : class com . github . unidbg . linux . android . dvm . DvmObject cannot be cast to class com . github . unidbg . linux . android . dvm . Enumeration ( com . github . unidbg . linux . android . dvm . DvmObject and com . github . unidbg . linux . android . dvm . Enumeration are in unnamed module of loader ' app ' )
at com . github . unidbg . linux . android . dvm . AbstractJni . callBooleanMethodV ( AbstractJni . java : 610 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callBooleanMethodV ( AbstractJni . java : 603 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callBooleanMethodV ( DvmMethod . java : 119 )
at com . github . unidbg . linux . android . dvm . DalvikVM$35 . handle ( DalvikVM . java : 630 )
上述提示xxx.DvmObject cannot be cast to xxx.Enumeration,即类型转换失败.出现这种问题,首先在Unidbg中搜索,看有无相关处理逻辑.通过搜索,可知在Unidbg中处理逻辑如下:
进而可查看Enumeration在Unidbg中的实现如下:
Enumeration在Java中同样有实现,Unidbg对Java中的基本数据类型做了封装,并优先使用它们.
比如要返回一个String对象,写法如下:
1
return new StringObject ( vm , "" );
而不是使用resolveClass的方式:
1
return vm . resolveClass ( "java/lang/String" ). newObject ( "" );
如果Unidbg封装了基本数据类型,那就要使用它们.查看Enumeration在Unidbg中的构造方法如下:
1
2
3
4
public Enumeration ( VM vm , List <? extends DvmObject <?>> value ) {
super ( vm . resolveClass ( "java/util/Enumeration" ), value );
this . iterator = value == null ? null : value . iterator ();
}
可以看到需要传入一个List类型的对象,故最终代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/util/zip/ZipFile->entries()Ljava/util/Enumeration;" : {
ZipFile zipFile = ( ZipFile ) dvmObject . getValue ();
Enumeration <? extends ZipEntry > entries = zipFile . entries ();
//return vm.resolveClass("java/util/Enumeration").newObject(entries);
List < DvmObject <?>> objs = new ArrayList <> ();
while ( entries . hasMoreElements ()){
ZipEntry zipEntry = entries . nextElement ();
objs . add ( vm . resolveClass ( "java/util/zip/ZipEntry" ). newObject ( zipEntry ));
}
return new com . github . unidbg . linux . android . dvm . Enumeration ( vm , objs );
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / util / zip / ZipEntry -> getName () Ljava / lang / String ;
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 417 )
at com . example . luodst . MainActivity . callObjectMethodV ( MainActivity . java : 95 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 262 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callObjectMethodV ( DvmMethod . java : 89 )
同样是在调用对象的方法,我们需要先获取对象,再用对象调用对应的方法.故补代码如下:
1
2
3
4
5
6
7
8
9
10
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/util/zip/ZipEntry->getName()Ljava/lang/String;" :{
ZipEntry zipEntry = ( ZipEntry ) dvmObject . getValue ();
String name = zipEntry . getName ();
return new StringObject ( vm , name );
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / lang / String -> endsWith ( Ljava / lang / String ;) Z
at com . github . unidbg . linux . android . dvm . AbstractJni . callBooleanMethodV ( AbstractJni . java : 625 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callBooleanMethodV ( AbstractJni . java : 603 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callBooleanMethodV ( DvmMethod . java : 119 )
at com . github . unidbg . linux . android . dvm . DalvikVM$35 . handle ( DalvikVM . java : 630 )
同理,我们需要先拿到对象,而且endsWith中有参数传递,我们也需要把参数取出来.故补代码如下:
1
2
3
4
5
6
7
8
9
10
public boolean callBooleanMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/lang/String->endsWith(Ljava/lang/String;)Z" :{
String strObj = ( String ) dvmObject . getValue ();
String suffix = ( String ) vaList . getObjectArg ( 0 ). getValue ();
return strObj . endsWith ( suffix );
}
}
return super . callBooleanMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / util / zip / ZipFile -> getInputStream ( Ljava / util / zip / ZipEntry ;) Ljava / io / InputStream ;
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 417 )
at com . example . luodst . MainActivity . callObjectMethodV ( MainActivity . java : 100 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 262 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callObjectMethodV ( DvmMethod . java : 89 )
同理,补代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/util/zip/ZipFile->getInputStream(Ljava/util/zip/ZipEntry;)Ljava/io/InputStream;" :{
ZipFile zipFile = ( ZipFile ) dvmObject . getValue ();
ZipEntry zipEntry = ( ZipEntry ) vaList . getObjectArg ( 0 ). getValue ();
try {
InputStream inputStream = zipFile . getInputStream ( zipEntry );
return vm . resolveClass ( "java/io/InputStream" ). newObject ( inputStream );
} catch ( IOException e ) {
throw new RuntimeException ( e );
}
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / io / InputStream -> read ( [ B ) I
at com . github . unidbg . linux . android . dvm . AbstractJni . callIntMethodV ( AbstractJni . java : 563 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callIntMethodV ( AbstractJni . java : 529 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callIntMethodV ( DvmMethod . java : 109 )
at com . github . unidbg . linux . android . dvm . DalvikVM$47 . handle ( DalvikVM . java : 827 )
需要补的是InputStream中的read方法,根据参数和返回值,补代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int callIntMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/io/InputStream->read([B)I" :{
InputStream inputStream = ( InputStream ) dvmObject . getValue ();
byte [] byteArray = ( byte [] ) vaList . getObjectArg ( 0 ). getValue ();
try {
return inputStream . read ( byteArray );
} catch ( IOException e ) {
throw new RuntimeException ( e );
}
}
}
return super . callIntMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / security / MessageDigest -> update ( [ B ) V
at com . github . unidbg . linux . android . dvm . AbstractJni . callVoidMethodV ( AbstractJni . java : 1007 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callVoidMethodV ( AbstractJni . java : 990 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callVoidMethodV ( DvmMethod . java : 229 )
at com . github . unidbg . linux . android . dvm . DalvikVM$59 . handle ( DalvikVM . java : 1051 )
这里调用了Java SDK中的MessageDigest类中的update方法,补代码如下:
1
2
3
4
5
6
7
8
9
10
11
public void callVoidMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ){
case "java/security/MessageDigest->update([B)V" :{
MessageDigest messageDigest = ( MessageDigest ) dvmObject . getValue ();
byte [] byteArray = ( byte [] ) vaList . getObjectArg ( 0 ). getValue ();
messageDigest . update ( byteArray );
return ;
}
}
super . callVoidMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / security / MessageDigest -> digest () [ B
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 417 )
at com . example . luodst . MainActivity . callObjectMethodV ( MainActivity . java : 112 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 262 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callObjectMethodV ( DvmMethod . java : 89 )
还缺少MessageDigest类中的digest方法,补代码如下:
1
2
3
4
5
6
7
8
9
10
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/security/MessageDigest->digest()[B" :{
MessageDigest messageDigest = ( MessageDigest ) dvmObject . getValue ();
byte [] digest = messageDigest . digest ();
return new ByteArray ( vm , digest );
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
再次运行代码,成功输出如下:
1
result ==> D3E550889725A6A7C5E834ECCDB4B73E
JNI_OnLoad
测试Apk
BossLast.apk
模拟执行
使用Unidbg模拟调用so中的JNI_OnLoad函数,初始化代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.example.luodst ;
import com.github.unidbg.AndroidEmulator ;
import com.github.unidbg.LibraryResolver ;
import com.github.unidbg.Module ;
import com.github.unidbg.arm.backend.Unicorn2Factory ;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder ;
import com.github.unidbg.linux.android.AndroidResolver ;
import com.github.unidbg.linux.android.dvm.* ;
import com.github.unidbg.memory.Memory ;
import com.github.unidbg.virtualmodule.android.AndroidModule ;
import java.io.File ;
public class MainActivity extends AbstractJni {
public static void main ( String [] args ) {
MainActivity mainActivity = new MainActivity ();
}
private final AndroidEmulator emulator ;
private final VM vm ;
private final Module module ;
private MainActivity () {
//1.创建Android模拟器实例
emulator = AndroidEmulatorBuilder
. for32Bit ()
. addBackendFactory ( new Unicorn2Factory ( true ))
. setRootDir ( new File ( "unidbg-android/src/test/java/com/example/luodst/rootfs" ))
. build ();
//2.获取操作内存的接口
Memory memory = emulator . getMemory ();
//3.设置Android SDK 版本
LibraryResolver resolver = new AndroidResolver ( 23 );
memory . setLibraryResolver ( resolver );
//4.创建虚拟机
vm = emulator . createDalvikVM ( new File ( "unidbg-android/src/test/java/com/example/luodst/files/boss_last.apk" ));
//5.是否打印日志
vm . setVerbose ( false );
//6.设置jni
vm . setJni ( this );
new AndroidModule ( emulator , vm ). register ( memory );
//7.加载目标so文件
DalvikModule dm = vm . loadLibrary ( "yzwg" , true );
//8.将so文件对应的Module存入成员变量
module = dm . getModule ();
//9.调用JNI_OnLoad
dm . callJNI_OnLoad ( emulator );
}
}
运行上述代码,报错如下:
1
2
3
4
5
6
7
8
9
10
11
12
[ main ] D / YZWG : JNI_OnLoad called
Exception in thread "main" java . lang . IllegalStateException : Illegal JNI version : 0xffffffff
at com . github . unidbg . linux . android . dvm . BaseVM . checkVersion ( BaseVM . java : 228 )
at com . github . unidbg . linux . android . dvm . DalvikModule . callJNI_OnLoad ( DalvikModule . java : 39 )
at com . example . luodst . MainActivity . < init > ( MainActivity . java : 49 )
at com . example . luodst . MainActivity . main ( MainActivity . java : 17 )
[ 14 : 37 : 26 484 ] WARN [ com . github . unidbg . linux . ARM32SyscallHandler ] ( ARM32SyscallHandler : 540 ) - handleInterrupt intno = 2 , NR = 0 , svcNumber = 0x18d , PC = unidbg @0xfffe0964 , LR = RX @0x4000a87b [ libyzwg . so ] 0xa87b , syscall = null
java . lang . UnsupportedOperationException : com / twl / signer / YZWG -> gContext : Landroid / content / Context ;
at com . github . unidbg . linux . android . dvm . AbstractJni . getStaticObjectField ( AbstractJni . java : 103 )
at com . github . unidbg . linux . android . dvm . AbstractJni . getStaticObjectField ( AbstractJni . java : 53 )
at com . github . unidbg . linux . android . dvm . DvmField . getStaticObjectField ( DvmField . java : 106 )
at com . github . unidbg . linux . android . dvm . DalvikVM$142 . handle ( DalvikVM . java : 2276 )
报错内容中有Illegal JNI version,是因为JNi_OnLoad调用失败,没有返回JNI版本信息.故Unidbg报非法的JNI版本.
报错内容中还包含Context,Context是维持Android程序中各个组件正常工作的核心功能类.
Unidbg运行在Java环境下,Context是Android中的类,我们不能像MessageDigest(Java SDK提供的工具类)那样去补这类环境,但是我们可以遵循一个补环境的准测:新建一个这种类,将类的对象置空.
与此同时,我们需要知道这里的报错是在做什么事情,以及它所构造参数的数据类型是什么,以便下次再遇到时,能给对象一个合理的值.
这里补的Context代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> getStaticObjectField ( BaseVM vm , DvmClass dvmClass , String signature ) {
switch ( signature ) {
case "com/twl/signer/YZWG->gContext:Landroid/content/Context;" :{
return vm . resolveClass ( "android/content/Context" ). newObject ( null );
}
}
return super . getStaticObjectField ( vm , dvmClass , signature );
}
我们将setVerbose的参数置为true,运行代码,来看下JNI的执行流.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//查找类
JNIEnv -> FindClass ( com / twl / signer / YZWG ) was called from RX @0x4000a561 [ libyzwg . so ] 0xa561
//获取类中的Context字段
JNIEnv -> GetStaticFieldID ( com / twl / signer / YZWG . gContextLandroid / content / Context ;) => 0x200e062f was called from RX @0x4000a577 [ libyzwg . so ] 0xa577
//获取类中的Context对象
JNIEnv -> GetStaticObjectField ( class com / twl / signer / YZWG , gContext Landroid / content / Context ; => android . content . Context @14ec4505 ) was called from RX @0x4000a87b [ libyzwg . so ] 0xa87b
//获取Context中的getPackageManager方法ID
JNIEnv -> GetMethodID ( android / content / Context . getPackageManager () Landroid / content / pm / PackageManager ;) => 0x3acc78f0 was called from RX @0x40027d63 [ libyzwg . so ] 0x27d63
//调用getPackageManager方法
JNIEnv -> CallObjectMethod ( android . content . Context @14ec4505 , getPackageManager () => android . content . pm . PackageManager @4b168fa9 ) was called from RX @0x40027d71 [ libyzwg . so ] 0x27d71
//调用getPackagesForUid方法
JNIEnv -> GetMethodID ( android / content / pm / PackageManager . getPackagesForUid ( I ) [ Ljava / lang / String ;) => 0x3fc7d353 was called from RX @0x40027d93 [ libyzwg . so ] 0x27d93
[ 17 : 20 : 46 663 ] WARN [ com . github . unidbg . linux . ARM32SyscallHandler ] ( ARM32SyscallHandler : 540 ) - handleInterrupt intno = 2 , NR =- 130448 , svcNumber = 0x11e , PC = unidbg @0xfffe0274 , LR = RX @0x40027da5 [ libyzwg . so ] 0x27da5 , syscall = null
java . lang . UnsupportedOperationException : android / content / pm / PackageManager -> getPackagesForUid ( I ) [ Ljava / lang / String ;
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethod ( AbstractJni . java : 933 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethod ( AbstractJni . java : 867 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callObjectMethod ( DvmMethod . java : 69 )
at com . github . unidbg . linux . android . dvm . DalvikVM$31 . handle ( DalvikVM . java : 527 )
从JNI的执行流可以我们,前面获取的Context只是为了能够调用getPackageManager方法,进而调用getPackagesForUid方法.执行到了getPackagesForUid这里就报错.可以通过下述网址来看下这个方法是做什么的.
https://developer.android.com/reference/
通过查阅资料可知它是通过UID获取包名,故补代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> callObjectMethod ( BaseVM vm , DvmObject <?> dvmObject , String signature , VarArg varArg ) {
switch ( signature ) {
case "android/content/pm/PackageManager->getPackagesForUid(I)[Ljava/lang/String;" :{
return new ArrayObject ( new StringObject ( vm , vm . getPackageName ()));
}
}
return super . callObjectMethod ( vm , dvmObject , signature , varArg );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / lang / String -> hashCode () I
at com . github . unidbg . linux . android . dvm . AbstractJni . callIntMethod ( AbstractJni . java : 965 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callIntMethod ( AbstractJni . java : 938 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callIntMethod ( DvmMethod . java : 129 )
at com . github . unidbg . linux . android . dvm . DalvikVM$46 . handle ( DalvikVM . java : 801 )
根据异常信息,补充的环境代码如下:
1
2
3
4
5
6
7
8
9
public int callIntMethod ( BaseVM vm , DvmObject <?> dvmObject , String signature , VarArg varArg ) {
switch ( signature ) {
case "java/lang/String->hashCode()I" :{
String strObj = ( String ) dvmObject . getValue ();
return strObj . hashCode ();
}
}
return super . callIntMethod ( vm , dvmObject , signature , varArg );
}
再次运行代码,成功输出如下:
1
2
[ main ] D / YZWG : JNI_OnLoad called
[ main ] D / YZWG : register method , method count : 5
文件标识
测试Apk
DogLite.apk
detectFile
使用Unidbg模拟执行so中的detectFile函数,初始化代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.example.luodst ;
import com.github.unidbg.AndroidEmulator ;
import com.github.unidbg.LibraryResolver ;
import com.github.unidbg.Module ;
import com.github.unidbg.arm.backend.Unicorn2Factory ;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder ;
import com.github.unidbg.linux.android.AndroidResolver ;
import com.github.unidbg.linux.android.dvm.* ;
import com.github.unidbg.memory.Memory ;
import com.github.unidbg.virtualmodule.android.AndroidModule ;
import java.io.File ;
public class MainActivity extends AbstractJni {
public static void main ( String [] args ) {
MainActivity mainActivity = new MainActivity ();
mainActivity . detectFile ();
}
private final AndroidEmulator emulator ;
private final VM vm ;
private final Module module ;
private MainActivity () {
//1.创建Android模拟器实例
emulator = AndroidEmulatorBuilder
. for32Bit ()
. addBackendFactory ( new Unicorn2Factory ( true ))
. setRootDir ( new File ( "unidbg-android/src/test/java/com/example/luodst/rootfs" ))
. build ();
//2.获取操作内存的接口
Memory memory = emulator . getMemory ();
//3.设置Android SDK 版本
LibraryResolver resolver = new AndroidResolver ( 23 );
memory . setLibraryResolver ( resolver );
//4.创建虚拟机
vm = emulator . createDalvikVM ( new File ( "unidbg-android/src/test/java/com/example/luodst/files/DogLite.apk" ));
//5.是否打印日志
vm . setVerbose ( false );
//6.设置jni
vm . setJni ( this );
new AndroidModule ( emulator , vm ). register ( memory );
//7.加载目标so文件
DalvikModule dm = vm . loadLibrary ( "doglite" , true );
//8.将so文件对应的Module存入成员变量
module = dm . getModule ();
//9.调用JNI_OnLoad
dm . callJNI_OnLoad ( emulator );
}
private void detectFile () {
DvmObject obj = vm . resolveClass ( "com/example/doglite/MainActivity" ). newObject ( null );
obj . callJniMethod ( emulator , "detectFile()V" );
}
}
运行上述代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / io / File ->< init > ( Ljava / lang / String ;) V
at com . github . unidbg . linux . android . dvm . AbstractJni . newObjectV ( AbstractJni . java : 803 )
at com . github . unidbg . linux . android . dvm . AbstractJni . newObjectV ( AbstractJni . java : 758 )
at com . github . unidbg . linux . android . dvm . DvmMethod . newObjectV ( DvmMethod . java : 214 )
at com . github . unidbg . linux . android . dvm . DalvikVM$26 . handle ( DalvikVM . java : 415 )
按惯例,补代码如下:
1
2
3
4
5
6
7
8
9
10
11
public DvmObject <?> newObjectV ( BaseVM vm , DvmClass dvmClass , String signature , VaList vaList ) {
switch ( signature ) {
case "java/io/File-><init>(Ljava/lang/String;)V" :{
String path = ( String ) vaList . getObjectArg ( 0 ). getValue ();
//System.out.println("path: " +path);
File file = new File ( path );
return vm . resolveClass ( "java/io/File" ). newObject ( file );
}
}
return super . newObjectV ( vm , dvmClass , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / io / File -> exists () Z
at com . github . unidbg . linux . android . dvm . AbstractJni . callBooleanMethodV ( AbstractJni . java : 625 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callBooleanMethodV ( AbstractJni . java : 603 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callBooleanMethodV ( DvmMethod . java : 119 )
at com . github . unidbg . linux . android . dvm . DalvikVM$35 . handle ( DalvikVM . java : 630 )
按惯例,补代码如下:
从上述路径来看,主要是检测电池的电量和模拟器.按照一般逻辑,电池的电量需要被检测,模拟器不能被检测.故我们需要根据不同文件标识做不同的处理,修正代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public boolean callBooleanMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/io/File->exists()Z" :{
File file = ( File ) dvmObject . getValue ();
String path = file . getPath ();
/* System.out.println("path: " +path);
return file.exists();*/
switch ( path ){
case "\\sys\\class\\power_supply\\battery\\voltage_now" :
return true ;
case "\\data\\local\\tmp\\Nox" :
return false ;
}
}
}
return super . callBooleanMethodV ( vm , dvmObject , signature , vaList );
}
detectFileNew
使用Unidbg模拟执行so中的detectFileNew函数,初始化代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.example.luodst ;
import com.github.unidbg.AndroidEmulator ;
import com.github.unidbg.LibraryResolver ;
import com.github.unidbg.Module ;
import com.github.unidbg.arm.backend.Unicorn2Factory ;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder ;
import com.github.unidbg.linux.android.AndroidResolver ;
import com.github.unidbg.linux.android.dvm.* ;
import com.github.unidbg.memory.Memory ;
import com.github.unidbg.virtualmodule.android.AndroidModule ;
import java.io.File ;
public class MainActivity extends AbstractJni {
public static void main ( String [] args ) {
MainActivity mainActivity = new MainActivity ();
mainActivity . detectFileNew ();
}
private final AndroidEmulator emulator ;
private final VM vm ;
private final Module module ;
private MainActivity () {
//1.创建Android模拟器实例
emulator = AndroidEmulatorBuilder
. for32Bit ()
. addBackendFactory ( new Unicorn2Factory ( true ))
. setRootDir ( new File ( "unidbg-android/src/test/java/com/example/luodst/rootfs" ))
. build ();
//2.获取操作内存的接口
Memory memory = emulator . getMemory ();
//3.设置Android SDK 版本
LibraryResolver resolver = new AndroidResolver ( 23 );
memory . setLibraryResolver ( resolver );
//4.创建虚拟机
vm = emulator . createDalvikVM ( new File ( "unidbg-android/src/test/java/com/example/luodst/files/DogLite.apk" ));
//5.是否打印日志
vm . setVerbose ( false );
//6.设置jni
vm . setJni ( this );
new AndroidModule ( emulator , vm ). register ( memory );
//7.加载目标so文件
DalvikModule dm = vm . loadLibrary ( "doglite" , true );
//8.将so文件对应的Module存入成员变量
module = dm . getModule ();
//9.调用JNI_OnLoad
dm . callJNI_OnLoad ( emulator );
}
private void detectFileNew () {
DvmObject obj = vm . resolveClass ( "com/example/doglite/MainActivity" ). newObject ( null );
obj . callJniMethod ( emulator , "detectFileNew()V" );
}
}
运行上述代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / io / File -> allocObject
at com . github . unidbg . linux . android . dvm . AbstractJni . allocObject ( AbstractJni . java : 812 )
at com . github . unidbg . linux . android . dvm . DvmClass . allocObject ( DvmClass . java : 74 )
at com . github . unidbg . linux . android . dvm . DalvikVM$24 . handle ( DalvikVM . java : 366 )
at com . github . unidbg . linux . ARM32SyscallHandler . hook ( ARM32SyscallHandler . java : 133 )
从上述报错来看是调用了allocObject函数,这里把newObject拿来做对比,两者的共同点都是用于构建新的类对象.两者区别如下:
allocObject
:只构建新的类对象(仅仅为类对象分配内存空间),既不初始化成员变量,也不调用构造方法.
newObject
:需要指明调用的构造方法,构建新的类对象,并初始化成员变量,调用指定的构造方法.
也就是说我们新模拟执行的这个方法再一次调用了File类,只不过初始化操作需要我们自己来做.补代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> allocObject ( BaseVM vm , DvmClass dvmClass , String signature ) {
switch ( signature ) {
case "java/io/File->allocObject" : {
return vm . resolveClass ( "java/io/File" ). newObject ( null );
}
}
return super . allocObject ( vm , dvmClass , signature );
}
运行上述代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / io / File ->< init > ( Ljava / lang / String ;) V
at com . github . unidbg . linux . android . dvm . AbstractJni . callVoidMethodV ( AbstractJni . java : 1007 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callVoidMethodV ( AbstractJni . java : 990 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callVoidMethodV ( DvmMethod . java : 229 )
at com . github . unidbg . linux . android . dvm . DalvikVM$59 . handle ( DalvikVM . java : 1051 )
再一次调用了init方法,既然在allocObject中创建了对象,那么我们可以通过一些技术手段来区分这些对象的创建.allocObject的修正代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
private static int nCount = 0 ;
private int getnCount () {
return ++ nCount ;
}
public DvmObject <?> allocObject ( BaseVM vm , DvmClass dvmClass , String signature ) {
switch ( signature ) {
case "java/io/File->allocObject" : {
return vm . resolveClass ( "java/io/File" ). newObject ( getnCount ());
}
}
return super . allocObject ( vm , dvmClass , signature );
}
上述我们使用了一个int类型的数值来标识不同的对象.接下来,补File的构造函数:
1
2
3
4
5
6
7
8
9
10
11
public void callVoidMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/io/File-><init>(Ljava/lang/String;)V" : {
String key = dvmObject . getValue (). toString ();
String path = ( String ) vaList . getObjectArg ( 0 ). getValue ();
emulator . set ( key , path );
return ;
}
}
super . callVoidMethodV ( vm , dvmObject , signature , vaList );
}
上述我们在模拟器上给它绑定了对象和要传入的参数.
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : java / io / File -> exists () Z
at com . github . unidbg . linux . android . dvm . AbstractJni . callBooleanMethodV ( AbstractJni . java : 625 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callBooleanMethodV ( AbstractJni . java : 603 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callBooleanMethodV ( DvmMethod . java : 119 )
at com . github . unidbg . linux . android . dvm . DalvikVM$35 . handle ( DalvikVM . java : 630 )
同理补环境代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public boolean callBooleanMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ){
case "java/io/File->exists()Z" :{
String key = dvmObject . getValue (). toString ();
String path = emulator . get ( key );
//System.out.println("path: " +path);
switch ( path ) {
case "/sys/class/power_supply/battery/voltage_now" :
return true ;
case "/data/local/tmp/nox" :
return false ;
}
}
}
return super . callBooleanMethodV ( vm , dvmObject , signature , vaList );
}
再次运行代码,成功输出如下:
1
2
[ main ] E / 引导 : 可以访问电池信息
[ main ] E / 引导 : 未检测到模拟器文件
设备风控
测试Apk
DogLite.apk
SysInfo
使用Unidbg模拟执行so中的SysInfo函数,初始化代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.example.luodst ;
import com.github.unidbg.AndroidEmulator ;
import com.github.unidbg.LibraryResolver ;
import com.github.unidbg.Module ;
import com.github.unidbg.arm.backend.Unicorn2Factory ;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder ;
import com.github.unidbg.linux.android.AndroidResolver ;
import com.github.unidbg.linux.android.dvm.* ;
import com.github.unidbg.memory.Memory ;
import com.github.unidbg.virtualmodule.android.AndroidModule ;
import java.io.File ;
import java.util.Base64 ;
public class MainActivity extends AbstractJni {
public static void main ( String [] args ) {
MainActivity mainActivity = new MainActivity ();
mainActivity . SysInfo ();
}
private final AndroidEmulator emulator ;
private final VM vm ;
private final Module module ;
private MainActivity () {
//1.创建Android模拟器实例
emulator = AndroidEmulatorBuilder
. for32Bit ()
. addBackendFactory ( new Unicorn2Factory ( true ))
. setRootDir ( new File ( "unidbg-android/src/test/java/com/example/luodst/rootfs" ))
. build ();
//2.获取操作内存的接口
Memory memory = emulator . getMemory ();
//3.设置Android SDK 版本
LibraryResolver resolver = new AndroidResolver ( 23 );
memory . setLibraryResolver ( resolver );
//4.创建虚拟机
vm = emulator . createDalvikVM ( new File ( "unidbg-android/src/test/java/com/example/luodst/files/DogLite.apk" ));
//5.是否打印日志
vm . setVerbose ( false );
//6.设置jni
vm . setJni ( this );
new AndroidModule ( emulator , vm ). register ( memory );
//7.加载目标so文件
DalvikModule dm = vm . loadLibrary ( "doglite" , true );
//8.将so文件对应的Module存入成员变量
module = dm . getModule ();
//9.调用JNI_OnLoad
dm . callJNI_OnLoad ( emulator );
}
private void SysInfo () {
DvmObject obj = vm . resolveClass ( "com/example/doglite/MainActivity" ). newObject ( null );
obj . callJniMethod ( emulator , "SysInfo()V" );
}
}
运行上述代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : android / app / ActivityThread -> getApplication () Landroid / app / Application ;
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 417 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 262 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callObjectMethodV ( DvmMethod . java : 89 )
at com . github . unidbg . linux . android . dvm . DalvikVM$32 . handle ( DalvikVM . java : 553 )
对于Android中的类,可以先置空.故补代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "android/app/ActivityThread->getApplication()Landroid/app/Application;" :{
return vm . resolveClass ( "android/app/Application" ). newObject ( null );
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
从上述代码可以看到是在获取android_id,我们可以根据返回值类型构造一个合理的数值.故补代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> callStaticObjectMethodV ( BaseVM vm , DvmClass dvmClass , String signature , VaList vaList ) {
switch ( signature ) {
case "android/provider/Settings$Secure->getString(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;" :{
return new StringObject ( vm , "Xia Luo Hun !" );
}
}
return super . callStaticObjectMethodV ( vm , dvmClass , signature , vaList );
}
再次运行代码,成功输出如下:
1
[ main ] E / 引导 : android id : Xia Luo Hun !
getAppFilesDir
使用Unidbg模拟执行so中的getAppFilesDir函数,初始化代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.example.luodst ;
import com.github.unidbg.AndroidEmulator ;
import com.github.unidbg.LibraryResolver ;
import com.github.unidbg.Module ;
import com.github.unidbg.arm.backend.Unicorn2Factory ;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder ;
import com.github.unidbg.linux.android.AndroidResolver ;
import com.github.unidbg.linux.android.dvm.* ;
import com.github.unidbg.memory.Memory ;
import com.github.unidbg.virtualmodule.android.AndroidModule ;
import java.io.File ;
import java.util.Base64 ;
public class MainActivity extends AbstractJni {
public static void main ( String [] args ) {
MainActivity mainActivity = new MainActivity ();
mainActivity . getAppFilesDir ();
}
private final AndroidEmulator emulator ;
private final VM vm ;
private final Module module ;
private MainActivity () {
//1.创建Android模拟器实例
emulator = AndroidEmulatorBuilder
. for32Bit ()
. addBackendFactory ( new Unicorn2Factory ( true ))
. setRootDir ( new File ( "unidbg-android/src/test/java/com/example/luodst/rootfs" ))
. build ();
//2.获取操作内存的接口
Memory memory = emulator . getMemory ();
//3.设置Android SDK 版本
LibraryResolver resolver = new AndroidResolver ( 23 );
memory . setLibraryResolver ( resolver );
//4.创建虚拟机
vm = emulator . createDalvikVM ( new File ( "unidbg-android/src/test/java/com/example/luodst/files/DogLite.apk" ));
//5.是否打印日志
vm . setVerbose ( false );
//6.设置jni
vm . setJni ( this );
new AndroidModule ( emulator , vm ). register ( memory );
//7.加载目标so文件
DalvikModule dm = vm . loadLibrary ( "doglite" , true );
//8.将so文件对应的Module存入成员变量
module = dm . getModule ();
//9.调用JNI_OnLoad
dm . callJNI_OnLoad ( emulator );
}
private void getAppFilesDir () {
DvmObject obj = vm . resolveClass ( "com/example/doglite/MainActivity" ). newObject ( null );
obj . callJniMethod ( emulator , "getAppFilesDir()V" );
}
}
运行上述代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : android / os / Environment -> getExternalStorageDirectory () Ljava / io / File ;
at com . github . unidbg . linux . android . dvm . AbstractJni . callStaticObjectMethodV ( AbstractJni . java : 504 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callStaticObjectMethodV ( AbstractJni . java : 438 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callStaticObjectMethodV ( DvmMethod . java : 59 )
at com . github . unidbg . linux . android . dvm . DalvikVM$113 . handle ( DalvikVM . java : 1816 )
按惯例,补代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> callStaticObjectMethodV ( BaseVM vm , DvmClass dvmClass , String signature , VaList vaList ) {
switch ( signature ) {
case "android/os/Environment->getExternalStorageDirectory()Ljava/io/File;" :{
return vm . resolveClass ( "java/io/File" ). newObject ( null );
}
}
return super . callStaticObjectMethodV ( vm , dvmClass , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . NullPointerException : Cannot invoke "java.io.File.getAbsolutePath()" because "file" is null
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 307 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 262 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callObjectMethodV ( DvmMethod . java : 89 )
at com . github . unidbg . linux . android . dvm . DalvikVM$32 . handle ( DalvikVM . java : 553 )
这是因为上一步中给的File类创建的对象为空,故不能通过File实例对象来调用getAbsolutePath方法.我们需要重写的内容如下:
1
2
3
4
5
6
7
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/io/File->getAbsolutePath()Ljava/lang/String;" :{
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
这里调用getAbsolutePath方法,我们依旧使用标识的方式来确定当前使用的是哪个对象.最终代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public DvmObject <?> callStaticObjectMethodV ( BaseVM vm , DvmClass dvmClass , String signature , VaList vaList ) {
switch ( signature ) {
case "android/os/Environment->getExternalStorageDirectory()Ljava/io/File;" :{
return vm . resolveClass ( "java/io/File" ). newObject ( signature );
}
}
return super . callStaticObjectMethodV ( vm , dvmClass , signature , vaList );
}
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/io/File->getAbsolutePath()Ljava/lang/String;" :{
String tag = dvmObject . getValue (). toString ();
if ( tag . equals ( "android/os/Environment->getExternalStorageDirectory()Ljava/io/File;" )){
return new StringObject ( vm , "/sdcard" );
}
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
上述在创建对象的时候使用signature作为标识,在callObjectMethodV方法中取出tag.根据tag的内容来区分对象.
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : android / os / Environment -> getStorageDirectory () Ljava / io / File ;
at com . github . unidbg . linux . android . dvm . AbstractJni . callStaticObjectMethodV ( AbstractJni . java : 504 )
at com . example . luodst . MainActivity . callStaticObjectMethodV ( MainActivity . java : 66 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callStaticObjectMethodV ( AbstractJni . java : 438 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callStaticObjectMethodV ( DvmMethod . java : 59 )
同理,补代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public DvmObject <?> callStaticObjectMethodV ( BaseVM vm , DvmClass dvmClass , String signature , VaList vaList ) {
switch ( signature ) {
case "android/os/Environment->getExternalStorageDirectory()Ljava/io/File;" :
case "android/os/Environment->getStorageDirectory()Ljava/io/File;" :{
return vm . resolveClass ( "java/io/File" ). newObject ( signature );
}
}
return super . callStaticObjectMethodV ( vm , dvmClass , signature , vaList );
}
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/io/File->getAbsolutePath()Ljava/lang/String;" :{
String tag = dvmObject . getValue (). toString ();
if ( tag . equals ( "android/os/Environment->getExternalStorageDirectory()Ljava/io/File;" )){
return new StringObject ( vm , "/sdcard" );
}
if ( tag . equals ( "android/os/Environment->getStorageDirectory()Ljava/io/File;" )){
return new StringObject ( vm , "/" );
}
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
再次运行代码,成功输出如下:
1
2
[main]E/引导: /sdcard
[main]E/引导: /
补环境加强
下面以一个无障碍服务为切入点,介绍如何补这种类型的Android系统函数.同时,也强调了补环境的一个基本准测:先将流程跑通,再处理值的问题.
测试Apk
DogPlus.apk
模拟执行
使用Unidbg模拟执行so中的detectAccessibilityManager函数,初始化代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.example.luodst ;
import com.github.unidbg.AndroidEmulator ;
import com.github.unidbg.LibraryResolver ;
import com.github.unidbg.Module ;
import com.github.unidbg.arm.backend.Unicorn2Factory ;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder ;
import com.github.unidbg.linux.android.AndroidResolver ;
import com.github.unidbg.linux.android.dvm.* ;
import com.github.unidbg.memory.Memory ;
import com.github.unidbg.virtualmodule.android.AndroidModule ;
import java.io.File ;
public class MainActivity extends AbstractJni {
public static void main ( String [] args ) {
MainActivity mainActivity = new MainActivity ();
mainActivity . detectAccessibilityManager ();
}
private final AndroidEmulator emulator ;
private final VM vm ;
private final Module module ;
private MainActivity () {
//1.创建Android模拟器实例
emulator = AndroidEmulatorBuilder
. for32Bit ()
. addBackendFactory ( new Unicorn2Factory ( true ))
. setRootDir ( new File ( "unidbg-android/src/test/java/com/example/luodst/rootfs" ))
. build ();
//2.获取操作内存的接口
Memory memory = emulator . getMemory ();
//3.设置Android SDK 版本
LibraryResolver resolver = new AndroidResolver ( 23 );
memory . setLibraryResolver ( resolver );
//4.创建虚拟机
vm = emulator . createDalvikVM ( new File ( "unidbg-android/src/test/java/com/example/luodst/files/DogPlus.apk" ));
//5.是否打印日志
vm . setVerbose ( false );
//6.设置jni
vm . setJni ( this );
new AndroidModule ( emulator , vm ). register ( memory );
//7.加载目标so文件
DalvikModule dm = vm . loadLibrary ( "dogplus" , true );
//8.将so文件对应的Module存入成员变量
module = dm . getModule ();
//9.调用JNI_OnLoad
dm . callJNI_OnLoad ( emulator );
}
private void detectAccessibilityManager () {
DvmObject obj = vm . resolveClass ( "com/example/dogplus/MainActivity" ). newObject ( null );
obj . callJniMethod ( emulator , "detectAccessibilityManager()V" );
}
}
运行上述代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : android / app / ActivityThread -> getApplication () Landroid / app / Application ;
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 417 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 262 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callObjectMethodV ( DvmMethod . java : 89 )
at com . github . unidbg . linux . android . dvm . DalvikVM$32 . handle ( DalvikVM . java : 553 )
按惯例,补代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "android/app/ActivityThread->getApplication()Landroid/app/Application;" :{
return vm . resolveClass ( "android/app/Application" ). newObject ( null );
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : android / view / accessibility / AccessibilityManager -> getInstalledAccessibilityServiceList () Ljava / util / List ;
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 417 )
at com . example . luodst . MainActivity . callObjectMethodV ( MainActivity . java : 64 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 262 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callObjectMethodV ( DvmMethod . java : 89 )
通过下述网址,查询getInstalledAccessibilityServiceList方法是返回已安装辅助功能服务的AccessibilityServiceInfos
https://developer.android.com/reference
这里先不用管值的正确性,补代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "android/view/accessibility/AccessibilityManager->getInstalledAccessibilityServiceList()Ljava/util/List;" :{
List < DvmObject <?>> list = new ArrayList <> ();
DvmClass dvmClass = vm . resolveClass ( "android/accessibilityservice/AccessibilityServiceInfo" );
list . add ( dvmClass . newObject ( null ));
list . add ( dvmClass . newObject ( null ));
list . add ( dvmClass . newObject ( null ));
return new ArrayListObject ( vm , list );
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : android / accessibilityservice / AccessibilityServiceInfo -> getResolveInfo () Landroid / content / pm / ResolveInfo ;
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 417 )
at com . example . luodst . MainActivity . callObjectMethodV ( MainActivity . java : 74 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 262 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callObjectMethodV ( DvmMethod . java : 89 )
按惯例,补代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "android/accessibilityservice/AccessibilityServiceInfo->getResolveInfo()Landroid/content/pm/ResolveInfo;" :{
return vm . resolveClass ( "android/content/pm/ResolveInfo" ). newObject ( null );
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : android / content / pm / ResolveInfo -> serviceInfo : Landroid / content / pm / ServiceInfo ;
at com . github . unidbg . linux . android . dvm . AbstractJni . getObjectField ( AbstractJni . java : 171 )
at com . github . unidbg . linux . android . dvm . AbstractJni . getObjectField ( AbstractJni . java : 141 )
at com . github . unidbg . linux . android . dvm . DvmField . getObjectField ( DvmField . java : 126 )
at com . github . unidbg . linux . android . dvm . DalvikVM$92 . handle ( DalvikVM . java : 1409 )
按惯例,补代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> getObjectField ( BaseVM vm , DvmObject <?> dvmObject , String signature ){
switch ( signature ){
case "android/content/pm/ResolveInfo->serviceInfo:Landroid/content/pm/ServiceInfo;" :{
return vm . resolveClass ( "android/content/pm/ServiceInfo" ). newObject ( null );
}
}
return super . getObjectField ( vm , dvmObject , signature );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : android / content / pm / ServiceInfo -> name : Ljava / lang / String ;
at com . github . unidbg . linux . android . dvm . AbstractJni . getObjectField ( AbstractJni . java : 171 )
at com . example . luodst . MainActivity . getObjectField ( MainActivity . java : 86 )
at com . github . unidbg . linux . android . dvm . AbstractJni . getObjectField ( AbstractJni . java : 141 )
at com . github . unidbg . linux . android . dvm . DvmField . getObjectField ( DvmField . java : 126 )
这里我们不知道name是什么值,随意给值即可.补代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> getObjectField ( BaseVM vm , DvmObject <?> dvmObject , String signature ){
switch ( signature ){
case "android/content/pm/ServiceInfo->name:Ljava/lang/String;" :{
return new StringObject ( vm , "XiaLuoHun" );
}
}
return super . getObjectField ( vm , dvmObject , signature );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : android / content / pm / ServiceInfo -> packageName : Ljava / lang / String ;
at com . github . unidbg . linux . android . dvm . AbstractJni . getObjectField ( AbstractJni . java : 171 )
at com . example . luodst . MainActivity . getObjectField ( MainActivity . java : 89 )
at com . github . unidbg . linux . android . dvm . AbstractJni . getObjectField ( AbstractJni . java : 141 )
at com . github . unidbg . linux . android . dvm . DvmField . getObjectField ( DvmField . java : 126 )
同理,补代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> getObjectField ( BaseVM vm , DvmObject <?> dvmObject , String signature ){
switch ( signature ){
case "android/content/pm/ServiceInfo->packageName:Ljava/lang/String;" :{
return new StringObject ( vm , "com.example.LuoHun" );
}
}
return super . getObjectField ( vm , dvmObject , signature );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : android / content / pm / ServiceInfo -> loadLabel ( Landroid / content / pm / PackageManager ;) Ljava / lang / CharSequence ;
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 417 )
at com . example . luodst . MainActivity . callObjectMethodV ( MainActivity . java : 77 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 262 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callObjectMethodV ( DvmMethod . java : 89 )
按惯例,补代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "android/content/pm/ServiceInfo->loadLabel(Landroid/content/pm/PackageManager;)Ljava/lang/CharSequence;" :{
return vm . resolveClass ( "java/lang/CharSequence" ). newObject ( null );
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . NullPointerException : Cannot invoke "Object.toString()" because "dvmObject.value" is null
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 399 )
at com . example . luodst . MainActivity . callObjectMethodV ( MainActivity . java : 80 )
at com . github . unidbg . linux . android . dvm . AbstractJni . callObjectMethodV ( AbstractJni . java : 262 )
at com . github . unidbg . linux . android . dvm . DvmMethod . callObjectMethodV ( DvmMethod . java : 89 )
上述报错提示dvmObject为空,是因为上述在构造CharSequence对象时,我们传了null.先暂时按当前流程进行补,代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "java/lang/CharSequence->toString()Ljava/lang/String;" : {
return new StringObject ( vm , "xialuohun.top" );
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
继续运行代码,报错如下:
1
2
3
4
5
java . lang . UnsupportedOperationException : android / accessibilityservice / AccessibilityServiceInfo -> packageNames : [ Ljava / lang / String ;
at com . github . unidbg . linux . android . dvm . AbstractJni . getObjectField ( AbstractJni . java : 171 )
at com . example . luodst . MainActivity . getObjectField ( MainActivity . java : 98 )
at com . github . unidbg . linux . android . dvm . AbstractJni . getObjectField ( AbstractJni . java : 141 )
at com . github . unidbg . linux . android . dvm . DvmField . getObjectField ( DvmField . java : 126 )
按惯例, 补代码如下:
1
2
3
4
5
6
7
8
public DvmObject <?> getObjectField ( BaseVM vm , DvmObject <?> dvmObject , String signature ){
switch ( signature ){
case "android/accessibilityservice/AccessibilityServiceInfo->packageNames:[Ljava/lang/String;" :{
return new ArrayObject ( new StringObject ( vm , "com.example.luo" ));
}
}
return super . getObjectField ( vm , dvmObject , signature );
}
再次运行代码,成功输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
[ main ] E / 引导 : 无障碍服务的名字 : XiaLuoHun
[ main ] E / 引导 : 无障碍服务所属的包名 : com . example . LuoHun
[ main ] E / 引导 : 无障碍服务所属app的标签名 : xialuohun . top
[ main ] E / 引导 : 作用于App : com . example . luo
[ main ] E / 引导 : 无障碍服务的名字 : XiaLuoHun
[ main ] E / 引导 : 无障碍服务所属的包名 : com . example . LuoHun
[ main ] E / 引导 : 无障碍服务所属app的标签名 : xialuohun . top
[ main ] E / 引导 : 作用于App : com . example . luo
[ main ] E / 引导 : 无障碍服务的名字 : XiaLuoHun
[ main ] E / 引导 : 无障碍服务所属的包名 : com . example . LuoHun
[ main ] E / 引导 : 无障碍服务所属app的标签名 : xialuohun . top
[ main ] E / 引导 : 作用于App : com . example . luo
从上述输出来看,确实模拟执行成功了.但是输出的内容肯定不对,这是因为每次调用相应的函数获取属性时,我们都返回了同一个值,最后追根溯源,回到最开始的List问题上.
这里我们将setVerbose置为true,来看下JNI执行流.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//调用getInstalledAccessibilityServiceList
JNIEnv -> FindClass ( android / view / accessibility / AccessibilityManager ) was called from RX @0x4000108b [ libdogplus . so ] 0x108b
JNIEnv -> GetMethodID ( android / view / accessibility / AccessibilityManager . getInstalledAccessibilityServiceList () Ljava / util / List ;) => 0xac86e356 was called from RX @0x40001161 [ libdogplus . so ] 0x1161
JNIEnv -> CallObjectMethodV ( android . view . accessibility . AccessibilityManager @17c386de , getInstalledAccessibilityServiceList () => java . util . ArrayList @5ef60048 ) was called from RX @0x400011b3 [ libdogplus . so ] 0x11b3
//获取ArrayList大小
JNIEnv -> GetMethodID ( java / util / ArrayList . size () I ) => 0x7c07529f was called from RX @0x40001161 [ libdogplus . so ] 0x1161
JNIEnv -> CallIntMethodV ( java . util . ArrayList @5ef60048 , size () => 0x3 ) was called from RX @0x40001267 [ libdogplus . so ] 0x1267
//获取ArrayList中的元素
JNIEnv -> GetMethodID ( java / util / ArrayList . get ( I ) Ljava / lang / Object ;) => 0x11309d56 was called from RX @0x40001161 [ libdogplus . so ] 0x1161
JNIEnv -> CallObjectMethodV ( java . util . ArrayList @5ef60048 , get ( 0x0 ) => android . accessibilityservice . AccessibilityServiceInfo @1d548a08 ) was called from RX @0x400011b3 [ libdogplus . so ] 0x11b3
//调用getResolveInfo
JNIEnv -> GetMethodID ( android / accessibilityservice / AccessibilityServiceInfo . getResolveInfo () Landroid / content / pm / ResolveInfo ;) => 0x76537b64 was called from RX @0x40001161 [ libdogplus . so ] 0x1161
JNIEnv -> CallObjectMethodV ( android . accessibilityservice . AccessibilityServiceInfo @1d548a08 , getResolveInfo () => android . content . pm . ResolveInfo @16aa0a0a ) was called from RX @0x400011b3 [ libdogplus . so ] 0x11b3
//获取ServiceInfo字段
JNIEnv -> GetFieldID ( android / content / pm / ResolveInfo . serviceInfo Landroid / content / pm / ServiceInfo ;) => 0x7c5b124e was called from RX @0x400012b9 [ libdogplus . so ] 0x12b9
JNIEnv -> GetObjectField ( android . content . pm . ResolveInfo @16aa0a0a , serviceInfo Landroid / content / pm / ServiceInfo ; => android . content . pm . ServiceInfo @691a7f8f ) was called from RX @0x400012df [ libdogplus . so ] 0x12df
//获取ServiceInfo.name字段
JNIEnv -> GetFieldID ( android / content / pm / ServiceInfo . name Ljava / lang / String ;) => 0x9f6455a1 was called from RX @0x400012b9 [ libdogplus . so ] 0x12b9
JNIEnv -> GetObjectField ( android . content . pm . ServiceInfo @691a7f8f , name Ljava / lang / String ; => "XiaLuoHun" ) was called from RX @0x400012df [ libdogplus . so ] 0x12df
JNIEnv -> GetStringUtfChars ( "XiaLuoHun" ) was called from RX @0x40001305 [ libdogplus . so ] 0x1305
//获取ServiceInfo.packageName字段
JNIEnv -> GetFieldID ( android / content / pm / ServiceInfo . packageName Ljava / lang / String ;) => 0xac13d6eb was called from RX @0x400012b9 [ libdogplus . so ] 0x12b9
JNIEnv -> GetObjectField ( android . content . pm . ServiceInfo @691a7f8f , packageName Ljava / lang / String ; => "com.example.LuoHun" ) was called from RX @0x400012df [ libdogplus . so ] 0x12df
JNIEnv -> GetStringUtfChars ( "com.example.LuoHun" ) was called from RX @0x40001305 [ libdogplus . so ] 0x1305
//调用getPackageManager
JNIEnv -> GetMethodID ( android / app / Application . getPackageManager () Landroid / content / pm / PackageManager ;) => 0x630dae39 was called from RX @0x40001161 [ libdogplus . so ] 0x1161
JNIEnv -> CallObjectMethodV ( android . app . Application @14a2f921 , getPackageManager () => android . content . pm . PackageManager @48524010 ) was called from RX @0x400011b3 [ libdogplus . so ] 0x11b3
//调用ServiceInfo.loadLabel
JNIEnv -> GetMethodID ( android / content / pm / ServiceInfo . loadLabel ( Landroid / content / pm / PackageManager ;) Ljava / lang / CharSequence ;) => 0x3425649b was called from RX @0x40001161 [ libdogplus . so ] 0x1161
JNIEnv -> CallObjectMethodV ( android . content . pm . ServiceInfo @691a7f8f , loadLabel ( android . content . pm . PackageManager @48524010 ) => java . lang . CharSequence @4b168fa9 ) was called from RX @0x400011b3 [ libdogplus . so ] 0x11b3
//调用CharSequence.toString
JNIEnv -> GetMethodID ( java / lang / CharSequence . toString () Ljava / lang / String ;) => 0x13c3c453 was called from RX @0x40001161 [ libdogplus . so ] 0x1161
JNIEnv -> CallObjectMethodV ( java . lang . CharSequence @4b168fa9 , toString () => "xialuohun.top" ) was called from RX @0x400011b3 [ libdogplus . so ] 0x11b3
JNIEnv -> GetStringUtfChars ( "xialuohun.top" ) was called from RX @0x40001305 [ libdogplus . so ] 0x1305
//获取AccessibilityServiceInfo.packageNames字段
JNIEnv -> GetFieldID ( android / accessibilityservice / AccessibilityServiceInfo . packageNames [ Ljava / lang / String ;) => 0x81285afb was called from RX @0x400012b9 [ libdogplus . so ] 0x12b9
JNIEnv -> GetObjectField ( android . accessibilityservice . AccessibilityServiceInfo @1d548a08 , packageNames [ Ljava / lang / String ; => [ "com.example.luo" ] ) was called from RX @0x400012df [ libdogplus . so ] 0x12df
按照上述流程,从开发角度实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//获取Application
Application application = getApplication ();
//获取AccessibilityManager
AccessibilityManager accessibilityManager = ( AccessibilityManager ) application . getSystemService ( ACCESSIBILITY_SERVICE );
//调用getInstalledAccessibilityServiceList
List < AccessibilityServiceInfo > installedAccessibilityServiceList = accessibilityManager . getInstalledAccessibilityServiceList ();
//遍历AccessibilityServiceInfo数组
for ( AccessibilityServiceInfo info : installedAccessibilityServiceList ){
ResolveInfo resolveInfo = info . getResolveInfo ();
ServiceInfo serviceInfo = resolveInfo . serviceInfo ;
CharSequence charSequence = serviceInfo . loadLabel ( getPackageManager ());
String s = charSequence . toString ();
String [] packageNames = info . packageNames ;
Log . d ( "引导" , "无障碍服务的名字:" + serviceInfo . name );
Log . d ( "引导" , "无障碍服务的包名:" + serviceInfo . packageName );
Log . d ( "引导" , "无障碍服务所属app的标签名:" + s );
if ( packageNames == null ) continue ;
for ( String ss : packageNames ){
Log . d ( "引导" , "作用于App:" + ss );
}
}
为了保证值的正确性,我们可以根据真实情况的值,在Unidbg中新建类如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.luodst ;
public class AccessibilityServiceInfo {
public String name ;
public String packageName ;
public String lable ;
public AccessibilityServiceInfo ( String name , String packageName , String lable ) {
this . name = name ;
this . packageName = packageName ;
this . lable = lable ;
}
}
在补AccessibilityServiceInfos时,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "android/view/accessibility/AccessibilityManager->getInstalledAccessibilityServiceList()Ljava/util/List;" :{
List < DvmObject <?>> list = new ArrayList <> ();
DvmClass dvmClass = vm . resolveClass ( "android/accessibilityservice/AccessibilityServiceInfo" );
AccessibilityServiceInfo info1 = new AccessibilityServiceInfo ( "TalkBackService" , "com.google.android.marvin.talkback" , "TalkBack" );
AccessibilityServiceInfo info2 = new AccessibilityServiceInfo ( "SelectToSpeakService" , "com.google.android.marvin.talkback" , "随选朗读" );
list . add ( dvmClass . newObject ( info1 ));
list . add ( dvmClass . newObject ( info2 ));
return new ArrayListObject ( vm , list );
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
上述构造完成后,只需在之前所有填null的地方配置为dvmObject对象即可.完整代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package com.example.luodst ;
import com.github.unidbg.AndroidEmulator ;
import com.github.unidbg.LibraryResolver ;
import com.github.unidbg.Module ;
import com.github.unidbg.arm.backend.Unicorn2Factory ;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder ;
import com.github.unidbg.linux.android.AndroidResolver ;
import com.github.unidbg.linux.android.dvm.* ;
import com.github.unidbg.linux.android.dvm.array.ArrayObject ;
import com.github.unidbg.memory.Memory ;
import com.github.unidbg.virtualmodule.android.AndroidModule ;
import java.io.File ;
import java.util.ArrayList ;
import java.util.List ;
public class MainActivity extends AbstractJni {
public static void main ( String [] args ) {
MainActivity mainActivity = new MainActivity ();
mainActivity . detectAccessibilityManager ();
}
private final AndroidEmulator emulator ;
private final VM vm ;
private final Module module ;
private MainActivity () {
//1.创建Android模拟器实例
emulator = AndroidEmulatorBuilder
. for32Bit ()
. addBackendFactory ( new Unicorn2Factory ( true ))
. setRootDir ( new File ( "unidbg-android/src/test/java/com/example/luodst/rootfs" ))
. build ();
//2.获取操作内存的接口
Memory memory = emulator . getMemory ();
//3.设置Android SDK 版本
LibraryResolver resolver = new AndroidResolver ( 23 );
memory . setLibraryResolver ( resolver );
//4.创建虚拟机
vm = emulator . createDalvikVM ( new File ( "unidbg-android/src/test/java/com/example/luodst/files/DogPlus.apk" ));
//5.是否打印日志
vm . setVerbose ( false );
//6.设置jni
vm . setJni ( this );
new AndroidModule ( emulator , vm ). register ( memory );
//7.加载目标so文件
DalvikModule dm = vm . loadLibrary ( "dogplus" , true );
//8.将so文件对应的Module存入成员变量
module = dm . getModule ();
//9.调用JNI_OnLoad
dm . callJNI_OnLoad ( emulator );
}
private void detectAccessibilityManager () {
DvmObject obj = vm . resolveClass ( "com/example/dogplus/MainActivity" ). newObject ( null );
obj . callJniMethod ( emulator , "detectAccessibilityManager()V" );
}
public DvmObject <?> callObjectMethodV ( BaseVM vm , DvmObject <?> dvmObject , String signature , VaList vaList ) {
switch ( signature ) {
case "android/app/ActivityThread->getApplication()Landroid/app/Application;" :{
return vm . resolveClass ( "android/app/Application" ). newObject ( null );
}
case "android/view/accessibility/AccessibilityManager->getInstalledAccessibilityServiceList()Ljava/util/List;" :{
List < DvmObject <?>> list = new ArrayList <> ();
DvmClass dvmClass = vm . resolveClass ( "android/accessibilityservice/AccessibilityServiceInfo" );
AccessibilityServiceInfo info1 = new AccessibilityServiceInfo ( "TalkBackService" , "com.google.android.marvin.talkback" , "TalkBack" );
AccessibilityServiceInfo info2 = new AccessibilityServiceInfo ( "SelectToSpeakService" , "com.google.android.marvin.talkback" , "随选朗读" );
list . add ( dvmClass . newObject ( info1 ));
list . add ( dvmClass . newObject ( info2 ));
return new ArrayListObject ( vm , list );
}
case "android/accessibilityservice/AccessibilityServiceInfo->getResolveInfo()Landroid/content/pm/ResolveInfo;" :{
return vm . resolveClass ( "android/content/pm/ResolveInfo" ). newObject ( dvmObject . getValue ());
}
case "android/content/pm/ServiceInfo->loadLabel(Landroid/content/pm/PackageManager;)Ljava/lang/CharSequence;" :{
AccessibilityServiceInfo info = ( AccessibilityServiceInfo ) dvmObject . getValue ();
return vm . resolveClass ( "java/lang/CharSequence" ). newObject ( info . lable );
}
case "java/lang/CharSequence->toString()Ljava/lang/String;" : {
return new StringObject ( vm , dvmObject . getValue (). toString ());
}
}
return super . callObjectMethodV ( vm , dvmObject , signature , vaList );
}
public DvmObject <?> getObjectField ( BaseVM vm , DvmObject <?> dvmObject , String signature ){
switch ( signature ){
case "android/content/pm/ResolveInfo->serviceInfo:Landroid/content/pm/ServiceInfo;" :{
return vm . resolveClass ( "android/content/pm/ServiceInfo" ). newObject ( dvmObject . getValue ());
}
case "android/content/pm/ServiceInfo->name:Ljava/lang/String;" :{
AccessibilityServiceInfo accessibilityServiceInfo = ( AccessibilityServiceInfo ) dvmObject . getValue ();
return new StringObject ( vm , accessibilityServiceInfo . name );
}
case "android/content/pm/ServiceInfo->packageName:Ljava/lang/String;" :{
AccessibilityServiceInfo accessibilityServiceInfo = ( AccessibilityServiceInfo ) dvmObject . getValue ();
return new StringObject ( vm , accessibilityServiceInfo . packageName );
}
case "android/accessibilityservice/AccessibilityServiceInfo->packageNames:[Ljava/lang/String;" :{
//return new ArrayObject(new StringObject(vm, "com.example.luo"));
return new ArrayObject ();
}
}
return super . getObjectField ( vm , dvmObject , signature );
}
}
最终输出内容如下:
1
2
3
4
5
6
[ main ] E / 引导 : 无障碍服务的名字 : TalkBackService
[ main ] E / 引导 : 无障碍服务所属的包名 : com . google . android . marvin . talkback
[ main ] E / 引导 : 无障碍服务所属app的标签名 : TalkBack
[ main ] E / 引导 : 无障碍服务的名字 : SelectToSpeakService
[ main ] E / 引导 : 无障碍服务所属的包名 : com . google . android . marvin . talkback
[ main ] E / 引导 : 无障碍服务所属app的标签名 : 随选朗读
补环境通用规则
基本类型,如Byte、Short、Int、Long、Double、Float、Boolean、Char等类型,直接传递即可.
1
2
3
case "android/content/pm/PackageManager->GET_SIGNATURES:I" :{
return 0x40 ;
}
注意
在准备参数,发起函数调用时,Long类型的参数必须显示声明,即在整数后面加L,不能使用隐式声明.
基本类型的包装类、字符串、基本类型数组、对象数组等直接传递,对象内部会做封装,也可以自己调用new StringPbject(vm, st)、new ByteArray(vm, value)等.
1
2
3
4
case "android/app/ActivityThread->currentPackageName()Ljava/lang/String;" :{
//return ProxyDvmObject.createObject(vm, vm.getPackageName());
return new StringObject ( vm , vm . getPackageName ());
}
ProxyDvmObject.createObject内部做了类型判断,会根据不同的类型进行转换.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//ProxyDvmObject.java
public static DvmObject <?> createObject ( VM vm , Object value ) {
if ( value == null ) {
return null ;
}
if ( value instanceof Class <?> ) {
return getObjectType ( vm , ( Class <?> ) value );
}
if ( value instanceof DvmObject ) {
return ( DvmObject <?> ) value ;
}
if ( value instanceof byte [] ) {
return new ByteArray ( vm , ( byte [] ) value );
}
if ( value instanceof short [] ) {
return new ShortArray ( vm , ( short [] ) value );
}
if ( value instanceof int [] ) {
return new IntArray ( vm , ( int [] ) value );
}
if ( value instanceof float [] ) {
return new FloatArray ( vm , ( float [] ) value );
}
if ( value instanceof double [] ) {
return new DoubleArray ( vm , ( double [] ) value );
}
if ( value instanceof String ) {
return new StringObject ( vm , ( String ) value );
}
Class <?> clazz = value . getClass ();
if ( clazz . isArray ()) {
if ( clazz . getComponentType (). isPrimitive ()) {
throw new UnsupportedOperationException ( String . valueOf ( value ));
}
Object [] array = ( Object [] ) value ;
DvmObject <?>[] dvmArray = new DvmObject [ array . length ] ;
for ( int i = 0 ; i < array . length ; i ++ ) {
dvmArray [ i ] = createObject ( vm , array [ i ] );
}
return new ArrayObject ( dvmArray );
}
return new ProxyDvmObject ( vm , value );
}
JDK标准库对象,如HashMap、JSONObject等,使用ProxyDvmObject.createObject(vm, value)处理.
1
2
3
case "java/util/HashMap-><init>()V" :{
return ProxyDvmObject . createObject ( vm , new HashMap <> ());
}
非JDK标准库对象,如Android Context、SharedPreference等,使用vm.resolveClass(vm, className).newObject(value)处理.
1
2
3
case "android/app/ActivityThread->getApplication()Landroid/app/Application;" :{
return vm . resolveClass ( "android/app/Application" ). newObject ( null );
}
参考链接
<Unidbg逆向工程 原理与实践>