如何实现PT_DENY_ATTACH(iOS中的反调试)

时间:2016-10-10 11:12:47

标签: ios ibm-mobilefirst

PT_DENY_ATTACH是一种反调试功能,可帮助防止调试程序附加到应用程序。以下代码可以在main()中实现,以防止GDB附加到应用程序。 :

#import <dlfcn.h>
#import <sys/types.h>
typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);
#define PT_DENY_ATTACH 31
void disable_gdb() {
void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
dlclose(handle);
}
int main(int argc, char *argv[]) {
@autoreleasepool {
#ifdef DEBUG
//do nothing
#else
disable_gdb();
#endif
}}

5 个答案:

答案 0 :(得分:1)

Guys it is true that this trick has been worked around and can be bypassed by overriding but have a look at that

Here the guys from iphonedevwiki.net are showing another way to implement the same thing in assembly, making it much harder to be patched. So to bypass this assembly implementation one has to have the Mach-O binary decrypted and override the implementation of the function, for example an attacker could swap all assembly instructions from the function with only nop instructions which do nothing. But if you obfuscate your assembly routine, it will be much harder for attacker to recognise what is happening, so only the most experienced folks will be able bypass that.

For those of you who don't know how to write assembly in Swift 5 project, here is what you will need to do:

  1. Create a new C File + Header
  2. lets name them disable_debuggers_advanced.c and disable_debuggers_advanced.h
  3. In disable_debuggers_advanced.c add the following:
#include "disable_debug_advanced.h"

void mRiYXNnZnZmZGF2Ym() {

    // No Need to encode these strings, because they will be directly compiled, they are not going to be present in the 'DATA' segment of the binary.
    __asm (
           "mov r0, #31\n" // set #define PT_DENY_ATTACH (31) to r0
           "mov r1, #0\n"   // clear r1
           "mov r2, #0\n"   // clear r2
           "mov r3, #0\n"   // clear r3
           "mov ip, #26\n"  // set the instruction pointer to syscal 26
           "svc #0x80\n"    // SVC (formerly SWI) generates a supervisor call. Supervisor calls are normally used to request privileged operations or access to system resources from an operating system
           );
}

  1. In disable_debuggers_advanced.h add the following:

#ifndef disable_debug_advanced_h
#define disable_debug_advanced_h

#include <stdio.h>


/**
 Original name: `disable_gdb_advanced()`

 This function makes the process deny the attaching request by debugers by
 simulating what is already available on macOS:

 ptrace(PT_DENY_ATTACH, 0, 0, 0);

 by implementing it directly in assembly language.

 This method of disabling debugers is well known to attackers but there is nothing they can do to easily bypass it
 because it is a part of the kernel memory space.

 Please note that this will make XCode not being able to attach its debuger to the process too,
 so run it only in release builds.

 source: https://iphonedevwiki.net/index.php/Crack_prevention#PT_DENY_ATTACH
 */
void mRiYXNnZnZmZGF2Ym(void) __attribute__((always_inline));

#endif /* disable_debug_advanced_h */

  1. Add #include "disable_debug_advanced.h" to your bridging header and a bridging header if your project does not already have one.

Now you can call this function to disable debuggers.

Note that this will break even the XCode debugger, so use it only on release builds

If you are wondering about the __attribute__((always_inline)) part and the weird name it is because we want our function to be hidden as much as possible. If you are not familiar with function inlining, you can watch my video about function inlining here where i describe it in depth or you can read in wikipedia about it. Even thought it is a speed compiler optimisation, it could give us benefits here because if you call it from multiple locations it will be harder for attacker to patch all function body copies in the app.

And the name is written like this, because this inlining is just a hint to the compiler, it is not guaranteed that it will happen. And if it does not happen and our function for disabling the debugger is clearly defined in the TEXT segment of our binary, we want it to be at least with obfuscated name, so that no one could guess what it does just by reading its assembly label.

In my opinion one should call this as a top level function even before the UIApplication object is created.

In order to do that:

  1. Add a new file to your Swift project and name it 'main.swift' (NAME IS IMPORTANT)
  2. Remove the @UIApplicationMain tag from your AppDelegates class definition.
  3. In your main.swift file write the following:

mRiYXNnZnZmZGF2Ym() // disable debuggers

// initialises the UIApplication and fires the event loop.
// This function never returns!
UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))

If you want to see a video of how to execute top level code in main.swift, you can see me doing it on this video

In attempt to make it even more obfuscated and harder to crack, one could use assembly obfuscators like this one Movfuscator so that only a few people would be able to recognise what these assembly instructions mean.

答案 1 :(得分:0)

注意:根据此工具的Wiki页面,它一直是怪异的,不得使用:

  

重要提示:这个技巧已经被海盗解决了。不要依赖它!

假设您指的是在MobileFirst Studio 7.1中创建的混合应用程序 - 这与其他任何iOS应用程序没有什么不同。

Hybrid应用程序的最终结果仍然是您在Xcode中打开以构建应用程序的Xcode项目。这意味着您需要将代码放在Xcode项目中的相同位置,就好像这是wiki page for PT_DENY_ATTACH的常规Xcode项目(就是这样)。

答案 2 :(得分:0)

正如@Idan Adar所指出的那样 - 这个技巧是有效的,但是如果您想在任何教育或其他目的中尝试它,您可以尝试做以下事项:

  1. 为你的app添加define以允许你运行并在需要时进行调试

    #define SOME
    
  2. 在main.m中添加以下代码

    #define SOME
    
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    
    #ifdef SOME
    
    #import <dlfcn.h>
    #import <sys/types.h>
    
     typedef int (*ptrace_ptr_t)(int request, pid_t pid, caddr_t addr, int data);
    #if !defined(PT_DENY_ATTACH)
    #define PT_DENY_ATTACH 31
    #endif  // !defined(PT_DENY_ATTACH)
    
     void disable_gdb();
    
     int main(int argc, char *argv[])
    {
    //   disable_gdb();
    @autoreleasepool {
              return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
    }
    
     void disable_gdb()
    {
              void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
              ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
              ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
              dlclose(handle);
    }
    
     int main3(int argc, char *argv[])
    {
              return -1;
    }
    
    #else
    
     int main(int argc, char *argv[])
    {
    @autoreleasepool {
              return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
      }
    }
    
    #endif
    
  3. Here - 关于

    的一些好处

答案 3 :(得分:0)

我看到此问题尚未标记为正确答案。我已经在下面的代码块中使用了反调试功能。请打开main.m文件,以添加以下代码片段。

#import "AppDelegate.h"
#import "Application.h"

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/sysctl.h>
#include <stdlib.h>

typedef int (*PYStdWriter)(void *, const char *, int);
static PYStdWriter _oldStdWrite;
#define   PT_DENY_ATTACH  31

int __pyStderrWrite(void *inFD, const char *buffer, int size)
{
    if ( strncmp(buffer, "AssertMacros:", 13) == 0 ) {
        return 0;
    }
    return _oldStdWrite(inFD, buffer, size);
}

static int is_debugger_present(void)
{
    int name[4];
    struct kinfo_proc info;
    size_t info_size = sizeof(info);

    info.kp_proc.p_flag = 0;

    name[0] = CTL_KERN;
    name[1] = KERN_PROC;
    name[2] = KERN_PROC_PID;
    name[3] = getpid();

    if (sysctl(name, 4, &info, &info_size, NULL, 0) == -1) {
        perror("sysctl");
        exit(-1);
    }
    return ((info.kp_proc.p_flag & P_TRACED) != 0);
}

int main(int argc, char *argv[]) {


#ifdef DEBUG
    _oldStdWrite = stderr->_write;
    stderr->_write = __pyStderrWrite;
    @autoreleasepool {
        return UIApplicationMain(argc, argv, NSStringFromClass([Application class]), NSStringFromClass([AppDelegate class]));
    }
#else
    //Anti Debugging Code
    //https://coredump.gr/articles/ios-anti-debugging-protections-part-2/

    //ptrace(PT_DENY_ATTACH, 0, 0, 0);
    printf("Looping forever");
    fflush(stdout);
    while (1)
    {
        sleep(1);
        if (is_debugger_present())
        {
            //[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isAntiDebug"];
            printf("Debugger detected! Terminating...\n");
            return -1;
        }
        printf(".");
        fflush(stdout);

        _oldStdWrite = stderr->_write;
        stderr->_write = __pyStderrWrite;
        @autoreleasepool {
            //[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"isAntiDebug"];
            int retVal = UIApplicationMain(argc, argv, NSStringFromClass([Application class]), NSStringFromClass([AppDelegate class]));
            return retVal;
        }
    }

#endif
}

答案 4 :(得分:0)

Stoyan所指出。您的实现很容易被绕开。您应该添加一个汇编代码。上面的答案缺少的一件事是,您必须添加arm64宏,否则将无法在模拟器上构建项目(OS X使用x86_64 arch)

所以添加这个:

#if defined (__arm64__)
    __asm(
        "mov x0, #26\n" // ptrace
        "mov x1, #31\n" // PT_DENY_ATTACH
        "mov x2, #0\n"
        "mov x3, #0\n"
        "mov x16, #0\n"
        "svc #128\n"
    );

为此我写了一个简单的guide。看看