from:http://www.coredump.gr/articles/ios-anti-debugging-protections-part-2/
In the previous part (iOS Anti-Debugging Protections: Part 1) we discussed about ptrace and how it
can be used to prevent a debugger from attaching to a process. This post describes a technique that is commonly used to detect the presence of a debugger. Note that unlike the ptrace technique this method doesn’t prevent a debugger from attaching to a process.
Instead, it uses the sysctl function to retrieve information about the process and determine whether it is being debugged. Apple has an article in their Mac Technical Q&As with sample code that uses this method: Detecting
the Debugger
The sysctl call is defined as:
int
int
void
size_t
void
size_t
|
The first argument name is an array of integers that describe the type of information we are requesting. Apple describes this name as a “Management Information Base” (MIB) style name in the sysctl
man page. The second argument contains the number of integers in the name array. The third and fourth arguments hold the output buffer and the output buffer size respectively. These arguments will be populated with the requested information when the function
returns. Arguments five and six are only used when setting information.
The following block of code contains an example C program that uses a sysctl call to determine whether it is being debugged. The next paragraphs contain an analysis of the protection as well as information on how to bypass it.
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
|
#include #include #include #include #include static
void ) { int
struct
size_t
sizeof (info); info.kp_proc.p_flag name[0] name[1] name[2] name[3] if
perror ( "sysctl" ); exit (-1); } return
} int
int
const
{ printf ( "Looping ); fflush (stdout); while
{ sleep(1); if
{ printf ( "Debugger ); return
} printf ( "." ); fflush (stdout); } return
} |
The call to sysctl is on line 20:
sysctl(name, |
First, lets analyze the arguments of the sysctl call. The first argument name is initialized as:
name[0] name[1] name[2] name[3] |
The item at index 0 is set to CTL_KERN. This is the top-level name for kernel-specific information. All the available top-level names have a prefix of “CTL_” and are defined in the header file /usr/include/sys/sysctl.h. The item at index 1 is set to KERN_PROC.
This indicates that sysctl will return a struct with process entries. The next item KERN_PROC_PID specifies that the target process will be selected based on a process ID (PID). Finally, the last item is the PID of that process.
The second argument of sysctl (size) is set to 4 since this is the total number of items in the name. Arguments three and four are set to the output buffer and its size. The output buffer is a struct of type kinfo_proc which is defined in /usr/include/sys/sysctl.h.
The struct contains another struct (kp_proc) of type extern_proc that is defined in /usr/include/sys/proc.h. The kp_proc struct contains information about the process including a flag (p_flag) that describes the process state. All the valid values for p_flag
can be found in /usr/include/sys/proc.h. The following block contains some sample values from that file:
#define #define #define |
The P_TRACED value is set when the process is being debugged. The following line of code in the sample program checks if the value is set:
return
|
Bypassing the sysctl check
This type of check can be bypassed by clearing the contents of the p_flag variable after the call returns. The following paragraphs contain step-by-step instructions on how to accomplish that with the help of GDB.
First, load the application in GDB:
tl0gic:~ Reading (gdb) |
Setup a conditional breakpoint on sysctl:
(gdb) |
This breakpoint will be triggered only if the size argument of sysctl (in $r1) has a value of 4 and the first three items in the name array (at addresses $r0, $r0+4, and $r0+8) are equal to CTL_KERN (1), KERN_PROC (14) and KERN_PROC_PID (1).
Run the process until the breakpoint is hit:
(gdb) Starting Reading Looping Breakpoint (gdb) |
Save the value of $r2, this is the address of output buffer where sysctl will store the process information: (gdb) set $pinfo=$r2
Continue (gdb) Run 0x00002ed6 (gdb) |
Before we continue to the next step we need to setup a breakpoint at the end of sysctl. We will use that breakpoint later to automate this process (don’t worry about the breakpoint condition for now):
(gdb) |
Now we need to find the exact offset of the p_flag value inside the output buffer. There are two ways to accomplish that:
- Sum the bytes for each of the struct elements that precede the p_flag
- Disassemble the sample application and find how the compiler calculates it.
We will go with the second option. The following block contains the disassembly for the is_debugger_present function:
_is_debugger_present: 00002e68 00002e6a 00002e6c 00002e70 00002e74 00002e78 00002e7a 00002e7c 00002e7e 00002e80 00002e82 00002e86 00002e88 00002e8c 00002e8e 00002e92 00002e96 00002e9a 00002e9c 00002e9e 00002ea0 00002ea2 00002ea4 00002ea8 00002eaa 00002eae 00002eb0 00002eb2 00002eb6 00002eba 00002ebe 00002ec0 00002ec2 00002ec4 00002ec6 00002eca 00002ece 00002ed2 00002ed6 00002eda 00002edc 00002ee0 00002ee4 00002ee6 00002eea 00002eee 00002ef2 00002ef6 00002efa 00002efe 00002f00 00002f02 00002f04 00002f08 00002f0a 00002f0c 00002f0e 00002f10 00002f12 00002f14 00002f18 |
At 0x2eb6 the base address of the kinfo_proc struct is calculated as $sp+20 and loaded in $r9. Then, at 0x2ec4 the address is copied into $r2 (the third argument of sysctl). Once the sysctl call (at 0x2f02) has returned the p_flag value is loaded as $sp+36.
Therefore, the offset of the p_flag is $sp+20-($sp+36) = 16 bytes. However, $r2 contains the address of the kinfo_struct and not the actual contents. To access the value of the p_flag we will have to use a pointer as illustrated below:
(gdb) 0x5802 |
The value of P_TRACED is 0×800. Therefore, a logical end with the current value should return 0×800 (or 2048 in base 10) when the flag is set:
(gdb) $5 |
The flag is correctly set (since we have a debugger attached to the process). The next step is to clear it:
(gdb) (gdb) |
Let’s print the value one more time to verify that it’s properly cleared:
(gdb) $6 |
Now that the flag is cleared we can continue executing the process:
(gdb) Continuing. . Breakpoint (gdb) |
The breakpoint is hit again because the application is running the sysctl check inside a while loop. We need to have GDB execute all the commands we used above every time a breakpoint is triggered. To accomplish that we can use the “commands” gdb command: GDB
commands for the sysctl breakpoint:
commands silent set continue end |
GDB commands for the breakpoint after sysctl has returned:
commands silent set set set continue end |
On the above commands make sure to replace the numbers 1 and 2 with the correct breakpoint numbers. GDB prints the breakpoint number every time a breakpoint is set. We can also use the “info breakpoints” commands to display all the breakpoints.
Now we can resume execution.
(gdb) Continuing. ............ |
The application runs without detecting the debugger