Overview
This article goes over a method to detect debuggers by looking for breakpoints in executable machine code loaded in memory.
It’s naive because the notion that a program’s .text section will only contain 0xCC when there’s a breakpoint is not true.
Regardless, this is a decent starting point to illustrate the concept of breakpoint detection.
Code
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
|
#include <stdio.h>
#include <stdbool.h>
bool check_method(void* method, unsigned char start[], unsigned char stop[]);
#define BP_CHECKABLE(func_name) __attribute__((noinline, section(func_name "_bounds")))
#define BP_CHECK_PRE(func) \
extern unsigned char __start_##func##_bounds[]; \
extern unsigned char __stop_##func##_bounds[];
#define BP_CHECK(func) check_method(func, __start_##func##_bounds, __stop_##func##_bounds)
// True if there's a breakpoint
bool check_method(void* method, unsigned char start[], unsigned char stop[])
{
void* start_ptr = (void *)start;
void* stop_ptr = (void *)stop;
size_t size = stop_ptr - start_ptr;
for (long bytecode = (long)start_ptr; bytecode < (long)stop_ptr; bytecode++)
{
unsigned char op = *((unsigned char* )bytecode);
if (op == 0xCC)
{
return true;
}
}
return false;
}
BP_CHECKABLE("checkme") void checkme()
{
printf("I'm doing something\n");
}
BP_CHECKABLE ("main") int main()
{
BP_CHECK_PRE(checkme);
if (BP_CHECK(checkme))
{
printf("Breakpoints found in checkme\n");
} else {
printf("No breakpoints found in checkme\n");
}
BP_CHECK_PRE(main);
if (BP_CHECK(main))
{
printf("Breakpoints found in main\n");
} else {
printf("No breakpoints found in main\n");
}
}
|
Explanation
Macro weirdness
There are 3 macros, BP_CHECKABLE, BP_CHECK_PRE, and BP_CHECK.
BP_CHECKABLE indicates that a function can be checked later. It does 2 things, makes it so that the compiler and linker cannot optimize the function by inlining it, and declares a section around the function, which is stored in the program headers. This means you can actually see the sections with something like readelf -S (see below).