Unidbg生产环境部署

Unidbg
Unidbg可以模拟执行so的方法,对于爬虫工程师来说,需要每次调用加密算法获取结果并添加到请求中获取最终的数据,故我们需要一个算法服务器来实现上述需求.

初识Spring Boot

Spring:创建微服务应用程序的框架,比如我们常见的Web应用服务.

Spring Boot:简化了Spring的配置,让开发更快捷、高效.

Spring Boot的基本配置说明可以在下述网址进行获取.

https://start.spring.io/

SpringBoot基本配置

  1. 使用IDEA选择Maven构建一个新项目.
  2. 在上述网站中获取Spring Boot Web服务配置.

SpringWeb配置

  1. 点击EXPLORE按钮后,查看具体配置项,

SpringWebPom

  1. 将上述红框中的内容,添加我们我们新建项目的pom文件中进行同步.

  2. 进行样例代码编写如下:

 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进行结合.

UnidbgPom文件

测试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项目结构如下:

UnidbgWeb布局

  1. 入口代码如下:
 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);
    }
}
  1. 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包冲突所产生的异常,移除即可.

slf4j包冲突

再次运行上述项目,访问下述链接,结果如下:

http://127.0.0.1:8080/getMD5?data=xialuohun

UnidbgSpringWeb结果

Unidbg-boot-server

Unidbg和Spring Boot的结合,前人已帮我们封装好了,项目及其配置如下:

https://github.com/anjia0532/unidbg-boot-server

UnidbgBoot配置

环境测试

项目的入口是UnidbgServerApplication,运行该入口,结果如下:

UnidbgBoot环境测试

访问下述链接,获取模拟执行结果如下:

http://127.0.0.1:9999/api/tt-encrypt/encrypt

UnidbgBoot环境测试2

添加模拟执行

项目本身已有一个示例,我们只需按照示例去移植对应内容即可.

注意
  1. 入口文件不需要动
  2. web目录下放置的是Controller文件

项目结构如下:

UnidbgBoot添加模拟执行结构

我们只需新增上述三个文件即可.

  1. 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);
    }
}
  1. 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();
    }
}
  1. 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

UnidbgBoot模拟执行结果

参考链接

<Unidbg逆向工程 原理与实践>


相关内容

0%