使用Android ART进行反调试

发表于:2017-04-03 11:30:10 来源:  MottoIN 阅读数(0人)

使用Android ART进行反调试


使JDWP相关的内存结构发生变化,这是很好的反调试技巧。我第一次听到这个方法是在Bluebox Security的一份2013演讲中,一年之后他们就被授予这项技术的软件专利。专利包括Dalvik DvmGlobals结构中的某些指针。然而,他们的方法只适用于Dalvik,所以我决定研究如何用ART来达到类似的目的。希望不要太晚!


首先,我们来看看Bluebox所描述的方法。在Dalvik中,有一个名为gDvm的全局变量 ,其中包含指向DvmGlobals结构的指针。 这使得直接访问JDWP相关数据变得容易。例如,gDvm。jdwpState指向包含全局调试数据和函数指针的结构。操作数据会导致JDWP线程发生故障或崩溃。以下是他们申请专利的实例:


JNIEXPORT jboolean JNICALL Java_com_example_disable(JNIENV* env, jobject dontuse ){
  // gDvm==struct DvmGlobals
  gDvm.jdwpState = NULL;
  return JNI_TRUE;
}

乍一看,在ART实现起来并不容易。没有指向重要数据结构的全局符号(见这里这里的源代码)。我们有一个名为gJdwpState的全局变量, 指向主结构体 JdwpState,但不幸的是 gJdwpState是一个本地符号,所以链接器不会为我们解决这个问题。


有趣的是,libart.so将JDWP相关类的一些vtables导出为全局符号。我不知道是什么原因,以及这是否正常(计算机科学家可以评论一下),但是它给了我们一些很好的方式来篡改JDWP线程的行为。有趣的类包括JdwpSocketState和JdwpAdbState – 这两个分别通过network sockets和ADB处理JDWP连接。




我们可以以各种方式覆盖方法指针。简单的归零它们不是一个好主意,因为它会崩溃这个过程。我找到的一个好方法是用“JdwpAdbState :: Shutdown()”的地址覆盖“jdwpAdbState :: ProcessIncoming()”的地址。注意:我想在去年被黑客攻击之一的 soft tokens中完成了类似的操作,所以我当然不是第一个提出这个想法的人。


我用本机实现:


#include <jni.h>
#include <string>
#include <android/log.h>
#include <dlfcn.h>
#include <sys/mman.h>
#include <jdwp/jdwp.h>

#define log(FMT, ...) __android_log_print(ANDROID_LOG_VERBOSE, "JDWPFun", FMT, ##__VA_ARGS__)

// Vtable structure. Just to make messing around with it more intuitive

struct VT_JdwpAdbState {
    unsigned long x;
    unsigned long y;
    void * JdwpSocketState_destructor;
    void * _JdwpSocketState_destructor;
    void * Accept;
    void * showmanyc;
    void * ShutDown;
    void * ProcessIncoming;
};

extern "C"

JNIEXPORT void JNICALL Java_sg_vantagepoint_jdwptest_MainActivity_JDWPfun(
        JNIEnv *env,
        jobject /* this */) {

    void* lib = dlopen("libart.so", RTLD_NOW);

    if (lib == NULL) {
        log("Error loading libart.so");
        dlerror();
    }else{

        struct VT_JdwpAdbState *vtable = ( struct VT_JdwpAdbState *)dlsym(lib, "_ZTVN3art4JDWP12JdwpAdbStateE");

        if (vtable == 0) {
            log("Couldn't resolve symbol '_ZTVN3art4JDWP12JdwpAdbStateE'.\n");
        }else {

            log("Vtable for JdwpAdbState at: %08x\n", vtable);

            // Let the fun begin!

            unsigned long pagesize = sysconf(_SC_PAGE_SIZE);
            unsigned long page = (unsigned long)vtable & ~(pagesize-1);

            mprotect((void *)page, pagesize, PROT_READ | PROT_WRITE);

            vtable->ProcessIncoming = vtable->ShutDown;

            // Reset permissions & flush cache

            mprotect((void *)page, pagesize, PROT_READ);

        }
    }
}

一旦运行此功能,任何连接的Java调试器都将断开,任何进一步的连接尝试都将失败。令人惊讶的是,这一切都在安静地发生,没有任何有用的解释在日志中:


Pyramidal Neuron:~ berndt$ adb jdwp

2926

Pyramidal Neuron:~ berndt$ adb forward tcp:7777 jdwp:2926

Pyramidal Neuron:~ berndt$ jdb -attach localhost:7777

java.io.IOException: handshake failed - connection prematurally closed

    at com.sun.tools.jdi.SocketTransportService.handshake(SocketTransportService.java:136)

    at com.sun.tools.jdi.SocketTransportService.attach(SocketTransportService.java:232)

    at com.sun.tools.jdi.GenericAttachingConnector.attach(GenericAttachingConnector.java:116)

    at com.sun.tools.jdi.SocketAttachingConnector.attach(SocketAttachingConnector.java:90)

    at com.sun.tools.example.debug.tty.VMConnection.attachTarget(VMConnection.java:519)

    at com.sun.tools.example.debug.tty.VMConnection.open(VMConnection.java:328)

    at com.sun.tools.example.debug.tty.Env.init(Env.java:63)

    at com.sun.tools.example.debug.tty.TTY.main(TTY.java:1066)

这个方法相当隐秘的 – 通过欺骗和隐藏实现。请注意,我们只尝试了ADB连接 – 您可能需要修补JdwpSocketState,以完全防止Java调试。


如何防范这种漏洞


绕过这种防御有很多方法。你需要修补应用程序漏洞,防止vtable被篡改。如果你不能及时修复,那以后还会再次受到这种攻击。我们正在更新OWASP移动测试指南中的所有必要技术,此外,您还将发现更多的反调试技巧,你可以在下面找到对你有帮助的文章:


Tampering and Reverse Engineering on Android


Testing Resiliency Against Reverse Engineering on Android


Assessing Anti-Reversing Schemes


关于OWASP移动安全测试指南


我为 OWASP移动安全测试指南(MSTG)写了一个手册,用于测试移动应用的安全性。MSTG是一个开源的工作,我们欢迎贡献和反馈。


相关新闻

大家都在学

课程详情

Android反编译

课程详情

互联网安全实践

课程详情

分析Android