iOS security series: Anti-debugging techniques
The first part of the iOS security miniseries, where I’m describing my attempts to make apps more secure.
When developing various banking apps, I came across some techniques to increase our app security.
No method is 100% bulletproof, but our job is to make it harder for the attacker to penetrate.
One of the methods is to kill the app when the debugger is being attached.
We can achieve that by using a lower level system call, called ptrace
.
Ptrace
According to the documentation ptrace
(process trace) is a system call built inside kernel. It provides a mechanism by which one process can inspect, control, manipulate the execution of another process. It is used by debuggers and other code-analysis tools.
So how can we use that?
Create PtraceC.c
and PtraceC.h
files.
Add to PtraceC.h
this.
void disable_gdb();
Add these lines to PtraceC.c
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#import <dlfcn.h>
#import <sys/types.h>
#include "PtraceC.h"
typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);
#define PT_DENY_ATTACH 31
void disable_gdb() {
ptrace_ptr_t ptrace_ptr = dlsym(RTLD_SELF, "ptrace");
ptrace_ptr(31, 0, 0, 0); // PTRACE_DENY_ATTACH = 31
}
Now, let’s walk through the code and understand what have we just done.
- In line
6
we have defined a ptrace type. It takes in 4 parameters. We are only interested in the first parameter.int _request
, which tells the function what to do. We usePT_DENY_ATTACH 31
an apple-specific constant, which will prevent debuggers from debugging our binary. - With
dlsym
function we will obtain the address to aptrace
call. Now callingptrace_ptr(PT_DENY_ATTACH, 0, 0, 0)
will sendSEGFAULT
to its tracing parent. - With
RTLD_SELF
we specify that the search for symbols will begin from the object that invokeddsym
.
The above implementation works, but thanks to these guys we know that it could be easily bypassed. So let’s go deeper.
Assembly code
We will use inline assembly instructions to make direct system calls. In order to bypass this, the attacker must be somehow able to decrypt and override assembly instructions, that should be harder to crack.
void disable_gdb() {
__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"
);
}
The above code is basically ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0)
written in ARM64 assembly. Ptrace is listed as number 26
in system call. This code won’t compile for simulators or OS X apps (our Macs are x86_64 Intel based) so add below assembly x86_64 implementation for other cases.
void disable_gdb() {
#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"
);
#elif defined(__x86_64__)
__asm(
"movq $0, %rcx\n"
"movq $0, %rdx\n"
"movq $0, %rsi\n"
"movq $0x1f, %rdi\n"
"movq $0x200001A, %rax\n"
"syscall\n"
);
#endif
}
After adding little obfuscation to our PtraceC.h
header file, it should look like this
#define disable_gdb Dnnfg5Gh68k3Nmq2Evcr
void disable_gdb();
Using C code in Swift Project
Now we have our C implementation. There are several ways to embed it into our Swift project. The easiest way is to include our PtraceC.h
file to ObjC YourProject-Bridging-Header.h
.
OR we can do it the fancy way!
Swift package
Open your terminal and type
mkdir Ptrace && cd Ptrace && swift package init --type library
The package should be initialized by now. Open your Xcode using
xed .
Now we will configure our Package.swift
file.
import PackageDescription
let package = Package(
name: "Ptrace",
products: [
.library(name: "Ptrace", targets: ["Ptrace"])
],
dependencies: [],
targets: [
.target(
name: "Ptrace",
dependencies: ["PtraceC"],
path: "Sources/PtraceSwift"
),
.target(
name: "PtraceC",
path: "Sources/PtraceC"
)
]
)
Our output library will be Ptrace. We will also make a tiny Swift wrapper. Now create your project structure and files like this.
Ptrace
├── Package.swift
├── Sources
├──PtraceC
├── include
├── PtraceC.h
└── PtraceC.c
├── PtraceSwift
├── PtraceSwift.swift
In PtraceSwift file, we will define a simple wrapper.
import PtraceC
public enum Ptrace {
public static func denyAttach() {
Dnnfg5Gh68k3Nmq2Evcr()
}
}
We should be able to build the package now. The last step is to include this in our projects.
- Copy the package folder into your project directory
cp -R Ptrace toYourProjectFolder
- Drag the package folder into your project in Xcode
- In Link Binary with Libraries section, locate the package and select the gray library icon inside the package.
You should now be able to use it anywhere in your app and use it like this:
import Ptrace
Ptrace.dennyAttach()
Make sure to only use it in your app store releases.
Source:
- owasp
- iphonedevwiki.net
- Complete example here.