Unidbg
Unidbg可以模拟执行so的方法,对于爬虫工程师来说,需要每次调用加密算法获取结果并添加到请求中获取最终的数据,故我们需要一个算法服务器来实现上述需求.
初识Spring Boot
Spring
:创建微服务应用程序的框架,比如我们常见的Web应用服务.
Spring Boot
:简化了Spring的配置,让开发更快捷、高效.
Spring Boot的基本配置说明可以在下述网址进行获取.
https://start.spring.io/
使用IDEA选择Maven构建一个新项目.
在上述网站中获取Spring Boot Web服务配置.
点击EXPLORE按钮后,查看具体配置项,
将上述红框中的内容,添加我们我们新建项目的pom文件中进行同步.
进行样例代码编写如下:
1
2
3
4
5
6
7
8
9
10
11
package org.example ;
import org.springframework.boot.SpringApplication ;
import org.springframework.boot.autoconfigure.SpringBootApplication ;
@SpringBootApplication
public class Main {
public static void main ( String [] args ) {
SpringApplication . run ( Main . class , args );
}
}
上述文件定义了整个项目的启动入口,接下来在新建的Controller中编写DemoController开发一个资源路径:
1
2
3
4
5
6
7
8
9
10
11
12
package org.example.controller ;
import org.springframework.web.bind.annotation.GetMapping ;
import org.springframework.web.bind.annotation.RestController ;
@RestController
public class DemoController {
@GetMapping ( "/test" )
public String test (){
return "Hello Unidbg" ;
}
}
运行上述项目结果如下:
接下来用浏览器访问下述链接,即可看到返回的字符串.
http://127.0.0.1:8080/test
Spring Boot和Unidbg结合
Unidbg也是基于Maven构建的Java项目,故可参考上述案例和Spring Boot进行结合.
测试so
libluodst.so
模拟执行
使用Unidbg模拟调用so中的md5函数,初始化代码如下:
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.DynarmicFactory ;
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 ();
String result = mainActivity . getMD5 ( "xialuohun" );
System . out . println ( result );
}
private final AndroidEmulator emulator ;
private final VM vm ;
private final Module module ;
public MainActivity () {
//1.创建Android模拟器实例
emulator = AndroidEmulatorBuilder
. for64Bit ()
. addBackendFactory ( new DynarmicFactory ( true ))
. build ();
//2.获取操作内存的接口
Memory memory = emulator . getMemory ();
//3.设置Android SDK 版本
LibraryResolver resolver = new AndroidResolver ( 23 );
memory . setLibraryResolver ( resolver );
//4.创建虚拟机
vm = emulator . createDalvikVM ();
//5.是否打印日志
vm . setVerbose ( false );
//6.设置jni
vm . setJni ( this );
new AndroidModule ( emulator , vm ). register ( memory );
//7.加载目标so文件
DalvikModule dm = vm . loadLibrary ( new File ( "unidbg-android/src/test/java/com/example/luodst/files/libluodst.so" ), true );
//8.将so文件对应的Module存入成员变量
module = dm . getModule ();
//9.调用JNI_OnLoad
dm . callJNI_OnLoad ( emulator );
}
public String getMD5 ( String data ) {
//执行JNI方法
DvmObject obj = vm . resolveClass ( "com.example.luodst.MainActivity" ). newObject ( null );
DvmObject dvmObject = obj . callJniMethodObject ( emulator , "md5(Ljava/lang/String;)Ljava/lang/String;" , data );
String retval = ( String ) dvmObject . getValue ();
return retval ;
}
}
Web服务
Unidbg项目结构如下:
入口代码如下:
1
2
3
4
5
6
7
8
9
10
11
package com.example.luodst ;
import org.springframework.boot.SpringApplication ;
import org.springframework.boot.autoconfigure.SpringBootApplication ;
@SpringBootApplication
public class Application {
public static void main ( String [] args ) {
SpringApplication . run ( Application . class , args );
}
}
Controller代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.example.luodst.controller ;
import com.example.luodst.MainActivity ;
import org.springframework.web.bind.annotation.GetMapping ;
import org.springframework.web.bind.annotation.RestController ;
@RestController
public class DemoController {
MainActivity mainActivity = new MainActivity ();
@GetMapping ( "/getMD5" )
public String getMD5 ( String data ){
return mainActivity . getMD5 ( data );
}
}
运行上述项目会报下述错误:
1
2
3
4
5
SLF4J ( W ): Class path contains multiple SLF4J providers .
SLF4J ( W ): Found provider [ org . slf4j . reload4j . Reload4jServiceProvider @701fc37a ]
SLF4J ( W ): Found provider [ ch . qos . logback . classic . spi . LogbackServiceProvider @4148db48 ]
SLF4J ( W ): See https : //www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J ( I ): Actual provider is of type [ org . slf4j . reload4j . Reload4jServiceProvider @701fc37a ]
这是由于SLF4J包冲突所产生的异常,移除即可.
再次运行上述项目,访问下述链接,结果如下:
http://127.0.0.1:8080/getMD5?data=xialuohun
Unidbg-boot-server
Unidbg和Spring Boot的结合,前人已帮我们封装好了,项目及其配置如下:
https://github.com/anjia0532/unidbg-boot-server
环境测试
项目的入口是UnidbgServerApplication,运行该入口,结果如下:
访问下述链接,获取模拟执行结果如下:
http://127.0.0.1:9999/api/tt-encrypt/encrypt
添加模拟执行
项目本身已有一个示例,我们只需按照示例去移植对应内容即可.
注意
入口文件不需要动
web目录下放置的是Controller文件
项目结构如下:
我们只需新增上述三个文件即可.
GetMD5Controller.java
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
package com.anjia.unidbgserver.web ;
import com.anjia.unidbgserver.service.GetMD5ServiceWorker ;
import lombok.SneakyThrows ;
import lombok.extern.slf4j.Slf4j ;
import org.springframework.http.MediaType ;
import org.springframework.web.bind.annotation.RequestMapping ;
import org.springframework.web.bind.annotation.RequestMethod ;
import org.springframework.web.bind.annotation.RestController ;
import javax.annotation.Resource ;
import java.util.concurrent.CompletableFuture ;
@Slf4j
@RestController
@RequestMapping ( path = "/api/GetMD5" , produces = MediaType . APPLICATION_JSON_VALUE )
public class GetMD5Controller {
@Resource ( name = "getMD5Worker" )
private GetMD5ServiceWorker getMd5ServiceWorker ;
@SneakyThrows
@RequestMapping ( value = "" , method = { RequestMethod . GET , RequestMethod . POST })
public CompletableFuture < String > GetMd5 ( String data ){
return getMd5ServiceWorker . getMD5 ( data );
}
}
GetMD5Service.java
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
package com.anjia.unidbgserver.service ;
import com.anjia.unidbgserver.config.UnidbgProperties ;
import com.anjia.unidbgserver.utils.TempFileUtils ;
import com.github.unidbg.AndroidEmulator ;
import com.github.unidbg.LibraryResolver ;
import com.github.unidbg.Module ;
import com.github.unidbg.arm.backend.DynarmicFactory ;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder ;
import com.github.unidbg.linux.android.AndroidResolver ;
import com.github.unidbg.linux.android.dvm.AbstractJni ;
import com.github.unidbg.linux.android.dvm.DalvikModule ;
import com.github.unidbg.linux.android.dvm.DvmObject ;
import com.github.unidbg.linux.android.dvm.VM ;
import com.github.unidbg.memory.Memory ;
import com.github.unidbg.virtualmodule.android.AndroidModule ;
import java.io.IOException ;
public class GetMD5Service extends AbstractJni {
private final AndroidEmulator emulator ;
private final VM vm ;
private final Module module ;
public GetMD5Service ( UnidbgProperties unidbgProperties ) {
//1.创建Android模拟器实例
emulator = AndroidEmulatorBuilder
. for64Bit ()
. addBackendFactory ( new DynarmicFactory ( true ))
. build ();
//2.获取操作内存的接口
Memory memory = emulator . getMemory ();
//3.设置Android SDK 版本
LibraryResolver resolver = new AndroidResolver ( 23 );
memory . setLibraryResolver ( resolver );
//4.创建虚拟机
vm = emulator . createDalvikVM ();
//5.是否打印日志
vm . setVerbose ( false );
//6.设置jni
vm . setJni ( this );
new AndroidModule ( emulator , vm ). register ( memory );
//7.加载目标so文件
DalvikModule dm = null ;
try {
dm = vm . loadLibrary ( TempFileUtils . getTempFile ( "data/apks/so/libluodst.so" ), true );
} catch ( IOException e ) {
throw new RuntimeException ( e );
}
//8.将so文件对应的Module存入成员变量
module = dm . getModule ();
//9.调用JNI_OnLoad
dm . callJNI_OnLoad ( emulator );
}
public String getMD5 ( String str ) {
//执行JNI方法
DvmObject obj = vm . resolveClass ( "com.example.luodst.MainActivity" ). newObject ( null );
DvmObject dvmObject = obj . callJniMethodObject ( emulator , "md5(Ljava/lang/String;)Ljava/lang/String;" , str );
String retval = ( String ) dvmObject . getValue ();
return retval ;
}
public void destroy () throws IOException {
emulator . close ();
}
}
GetMD5ServiceWorker.java
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
package com.anjia.unidbgserver.service ;
import com.anjia.unidbgserver.config.UnidbgProperties ;
import com.github.unidbg.worker.Worker ;
import com.github.unidbg.worker.WorkerPool ;
import com.github.unidbg.worker.WorkerPoolFactory ;
import lombok.SneakyThrows ;
import lombok.extern.slf4j.Slf4j ;
import org.springframework.beans.factory.annotation.Autowired ;
import org.springframework.beans.factory.annotation.Value ;
import org.springframework.scheduling.annotation.Async ;
import org.springframework.stereotype.Service ;
import java.util.concurrent.CompletableFuture ;
import java.util.concurrent.TimeUnit ;
@Slf4j
@Service ( "getMD5Worker" )
public class GetMD5ServiceWorker extends Worker {
private UnidbgProperties unidbgProperties ;
private WorkerPool pool ;
private GetMD5Service getMd5Service ;
@Autowired
public void init ( UnidbgProperties unidbgProperties ) {
this . unidbgProperties = unidbgProperties ;
}
public GetMD5ServiceWorker ( UnidbgProperties unidbgProperties ) {
super ( WorkerPoolFactory . create ( TTEncryptServiceWorker :: new , Runtime . getRuntime (). availableProcessors ()));
}
public GetMD5ServiceWorker ( WorkerPool pool ) {
super ( pool );
}
@Autowired
public GetMD5ServiceWorker ( UnidbgProperties unidbgProperties ,
@Value ( "${spring.task.execution.pool.core-size:4}" ) int poolSize ) {
super ( WorkerPoolFactory . create ( TTEncryptServiceWorker :: new , Runtime . getRuntime (). availableProcessors ()));
this . unidbgProperties = unidbgProperties ;
if ( this . unidbgProperties . isAsync ()) {
pool = WorkerPoolFactory . create ( pool -> new TTEncryptServiceWorker ( unidbgProperties . isDynarmic (),
unidbgProperties . isVerbose (), pool ), Math . max ( poolSize , 4 ));
log . info ( "线程池为:{}" , Math . max ( poolSize , 4 ));
} else {
this . getMd5Service = new GetMD5Service ( unidbgProperties );
}
}
public GetMD5ServiceWorker ( boolean dynarmic , boolean verbose , WorkerPool pool ) {
super ( pool );
this . unidbgProperties = new UnidbgProperties ();
unidbgProperties . setDynarmic ( dynarmic );
unidbgProperties . setVerbose ( verbose );
log . info ( "是否启用动态引擎:{},是否打印详细信息:{}" , dynarmic , verbose );
this . getMd5Service = new GetMD5Service ( unidbgProperties );
}
@Async
@SneakyThrows
public CompletableFuture < String > getMD5 ( String str ) {
GetMD5ServiceWorker worker ;
String data ;
if ( this . unidbgProperties . isAsync ()) {
while ( true ) {
if (( worker = pool . borrow ( 2 , TimeUnit . SECONDS )) == null ) {
continue ;
}
data = worker . doWork ( str );
pool . release ( worker );
break ;
}
} else {
synchronized ( this ) {
data = this . doWork ( str );
}
}
return CompletableFuture . completedFuture ( data );
}
private String doWork ( String str ) {
return getMd5Service . getMD5 ( str );
}
@SneakyThrows
@Override public void destroy () {
getMd5Service . destroy ();
}
}
运行上述项目,访问下述链接,结果如下:
http://127.0.0.1:9999/api/GetMD5?data=xialuohun
参考链接
<Unidbg逆向工程 原理与实践>