-
Notifications
You must be signed in to change notification settings - Fork 586
Expand file tree
/
Copy pathNpcapHelper.cpp
More file actions
553 lines (493 loc) · 18.1 KB
/
NpcapHelper.cpp
File metadata and controls
553 lines (493 loc) · 18.1 KB
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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
/***********************IMPORTANT NPCAP LICENSE TERMS***********************
*
* Npcap (https://npcap.com) is a Windows packet sniffing driver and library and
* is copyright (c) 2013-2025 by Nmap Software LLC ("The Nmap Project"). All
* rights reserved.
*
* Even though Npcap source code is publicly available for review, it is not
* open source software and may not be redistributed or used in other software
* without special permission from the Nmap Project. The standard (free) version
* is usually limited to installation on five systems. For more details, see the
* LICENSE file included with Npcap and also available at
* https://github.com/nmap/npcap/blob/master/LICENSE. This header file
* summarizes a few important aspects of the Npcap license, but is not a
* substitute for that full Npcap license agreement.
*
* We fund the Npcap project by selling two types of commercial licenses to a
* special Npcap OEM edition:
*
* 1) The Npcap OEM Redistribution License allows companies distribute Npcap OEM
* within their products. Licensees generally use the Npcap OEM silent
* installer, ensuring a seamless experience for end users. Licensees may choose
* between a perpetual unlimited license or a quarterly term license, along with
* options for commercial support and updates. Prices and details:
* https://npcap.com/oem/redist.html
*
* 2) The Npcap OEM Internal-Use License is for organizations that wish to use
* Npcap OEM internally, without redistribution outside their organization. This
* allows them to bypass the 5-system usage cap of the Npcap free edition. It
* includes commercial support and update options, and provides the extra Npcap
* OEM features such as the silent installer for automated deployment. Prices
* and details: https://npcap.com/oem/internal.html
*
* Both of these licenses include updates and support as well as a warranty.
* Npcap OEM also includes a silent installer for unattended installation.
* Further details about Npcap OEM are available from https://npcap.com/oem/,
* and you are also welcome to contact us at sales@nmap.com to ask any questions
* or set up a license for your organization.
*
* Free and open source software producers are also welcome to contact us for
* redistribution requests. However, we normally recommend that such authors
* instead ask your users to download and install Npcap themselves. It will be
* free for them if they need 5 or fewer copies.
*
* If the Nmap Project (directly or through one of our commercial licensing
* customers) has granted you additional rights to Npcap or Npcap OEM, those
* additional rights take precedence where they conflict with the terms of the
* license agreement.
*
* Since the Npcap source code is available for download and review, users
* sometimes contribute code patches to fix bugs or add new features. By sending
* these changes to the Nmap Project (including through direct email or our
* mailing lists or submitting pull requests through our source code
* repository), it is understood unless you specify otherwise that you are
* offering the Nmap Project the unlimited, non-exclusive right to reuse,
* modify, and relicense your code contribution so that we may (but are not
* obligated to) incorporate it into Npcap. If you wish to specify special
* license conditions or restrictions on your contributions, just say so when
* you send them.
*
* This software is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. Warranty rights and commercial support are
* available for the OEM Edition described above.
*
* Other copyright notices and attribution may appear below this license header.
* We have kept those for attribution purposes, but any license terms granted by
* those notices apply only to their original work, and not to any changes made
* by the Nmap Project or to this entire file.
*
***************************************************************************/
/***************************************************************************
* NpcapHelper.cpp -- A program used to fetch driver handles for packet.dll*
* , it is started by packet.dll and uses Named Pipe to communicate with *
* packet.dll. This is for "Admin-only mode", as packet.dll runs on *
* non-Admin level and NpcapHelper.exe runs on Admin level. If user denies *
* the UAC prompt, NpcapHelper.exe will not start. *
* *
* This program is based on Microsoft example: *
* https://msdn.microsoft.com/en-us/library/windows/desktop/aa365592%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
* https://msdn.microsoft.com/en-us/library/windows/desktop/aa365588%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
***************************************************************************/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <strsafe.h>
//#define _DBG
#include "../debug.h"
#define BUFSIZE 512
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
int g_sourcePID = 0;
typedef struct _DeviceCache
{
CHAR SymbolicLinkA[BUFSIZE];
HANDLE handle;
_DeviceCache *next;
} DeviceCache;
DeviceCache *g_DeviceCache = NULL;
DWORD WINAPI InstanceThread(LPVOID);
VOID GetAnswerToRequest(_In_reads_bytes_(BUFSIZE) LPCSTR pchRequest,
_Out_writes_bytes_to_(BUFSIZE, *pchBytes) LPSTR pchReply,
LPDWORD pchBytes);
void terminateSelf() noexcept
{
HANDLE hself = GetCurrentProcess();
TerminateProcess(hself, 0);
}
// Slightly modified from:
// https://learn.microsoft.com/en-us/windows/win32/secauthz/enabling-and-disabling-privileges-in-c--
BOOL SetPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LookupPrivilegeValue(
NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid)) // receives LUID of privilege
{
TRACE_PRINT1("LookupPrivilegeValue error: %u\n", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
{
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
}
else
{
tp.Privileges[0].Attributes = 0;
}
// Enable the privilege or disable all privileges.
if (!AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,
(PDWORD)NULL))
{
TRACE_PRINT1("AdjustTokenPrivileges error: %u\n", GetLastError());
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
TRACE_PRINT("The token does not have the specified privilege.\n");
return FALSE;
}
return TRUE;
}
_Must_inspect_result_
_Success_(return != INVALID_HANDLE_VALUE)
HANDLE getDeviceHandleInternal(_In_ LPCSTR SymbolicLinkA, _Out_ _On_failure_(_Out_range_(1,MAXDWORD)) DWORD *pdwError)
{
HANDLE hFile = CreateFileA(SymbolicLinkA, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
HANDLE hFileDup;
BOOL bResult;
HANDLE hClientProcess;
HANDLE hMyToken;
TRACE_PRINT1("Original handle: %08p.\n", hFile);
if (hFile == INVALID_HANDLE_VALUE)
{
*pdwError = GetLastError();
TRACE_PRINT1("CreateFileA failed, GLE=%d.\n", *pdwError);
return INVALID_HANDLE_VALUE;
}
bResult = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hMyToken);
if (!bResult)
{
*pdwError = dwError = GetLastError();
TRACE_PRINT1("OpenProcessToken failed, GLE=%d.\n", dwError);
return INVALID_HANDLE_VALUE;
}
bResult = SetPrivilege(hMyToken, SE_DEBUG_NAME, TRUE);
if (!bResult)
{
*pdwError = dwError = GetLastError();
TRACE_PRINT1("SetPrivilege failed, GLE=%d.\n", dwError);
return INVALID_HANDLE_VALUE;
}
hClientProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, g_sourcePID);
if (hClientProcess == NULL)
{
*pdwError = GetLastError();
TRACE_PRINT1("OpenProcess failed, GLE=%d.\n", *pdwError);
CloseHandle(hFile);
return INVALID_HANDLE_VALUE;
}
bResult = DuplicateHandle(GetCurrentProcess(),
hFile,
hClientProcess,
&hFileDup,
GENERIC_WRITE | GENERIC_READ,
FALSE,
// hFile will be closed regardless of error:
DUPLICATE_CLOSE_SOURCE);
if (!bResult)
{
TRACE_PRINT1("DuplicateHandle failed, GLE=%d.\n", GetLastError());
*pdwError = 1234;
return INVALID_HANDLE_VALUE;
}
else
{
TRACE_PRINT1("Duplicated handle: %08p.\n", hFileDup);
*pdwError = 0;
return hFileDup;
}
}
BOOL createPipe(LPCSTR pipeName) noexcept
{
BOOL fConnected = FALSE;
DWORD dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
HANDLE hHeap = GetProcessHeap();
char lpszPipename[BUFSIZE];
sprintf_s(lpszPipename, BUFSIZE, "\\\\.\\pipe\\%s", pipeName);
// Create a DACL that allows only the same user as the PID we were given to access the pipe
HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, g_sourcePID);
if (hProc == NULL)
{
TRACE_PRINT1("OpenProcess(PROCESS_QUERY_INFORMATION) failed: %#x\n", GetLastError());
return FALSE;
}
HANDLE hToken;
if (!OpenProcessToken(hProc, TOKEN_READ, &hToken))
{
TRACE_PRINT1("OpenProcessToken(TOKEN_READ) failed: %#x\n", GetLastError());
CloseHandle(hProc);
return FALSE;
}
struct {
TOKEN_USER tokenUser;
BYTE buffer[SECURITY_MAX_SID_SIZE];
} tokenInfoBuffer;
DWORD dwTokenSize;
ZeroMemory(&tokenInfoBuffer, sizeof(tokenInfoBuffer));
if (!GetTokenInformation(hToken, TokenUser, &tokenInfoBuffer.tokenUser, sizeof(tokenInfoBuffer), &dwTokenSize))
{
TRACE_PRINT1("GetTokenInformation failed: %#x\n", GetLastError());
CloseHandle(hToken);
CloseHandle(hProc);
return FALSE;
}
CloseHandle(hToken);
CloseHandle(hProc);
if (!IsValidSid(tokenInfoBuffer.tokenUser.User.Sid))
{
TRACE_PRINT("Invalid owner SID\n");
return FALSE;
}
SID creatorOwnerRightsSid{ 1, 1, SECURITY_CREATOR_SID_AUTHORITY, {SECURITY_CREATOR_OWNER_RIGHTS_RID} };
SECURITY_DESCRIPTOR sd;
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
{
TRACE_PRINT1("InitializeSecurityDescriptor failed: %#x\n", GetLastError());
return FALSE;
}
DWORD cbDacl = sizeof(ACL) + 2 * sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD);
cbDacl += GetLengthSid(&creatorOwnerRightsSid);
cbDacl += GetLengthSid(tokenInfoBuffer.tokenUser.User.Sid);
PACL pDacl = (PACL) HeapAlloc(hHeap, 0, cbDacl);
if (pDacl == NULL)
{
TRACE_PRINT("Allocate for DACL failed\n");
return FALSE;
}
if (!InitializeAcl(pDacl,cbDacl,ACL_REVISION))
{
TRACE_PRINT1("InitializeACL failed: %#x\n", GetLastError());
HeapFree(hHeap, 0, pDacl);
return FALSE;
}
if (!AddAccessAllowedAce(pDacl, ACL_REVISION, GENERIC_ALL, &creatorOwnerRightsSid))
{
TRACE_PRINT1("AddAccessAllowedAce failed: %#x\n", GetLastError());
HeapFree(hHeap, 0, pDacl);
return FALSE;
}
if (!AddAccessAllowedAce(pDacl, ACL_REVISION, GENERIC_ALL, tokenInfoBuffer.tokenUser.User.Sid))
{
TRACE_PRINT1("AddAccessAllowedAce failed: %#x\n", GetLastError());
HeapFree(hHeap, 0, pDacl);
return FALSE;
}
if (!SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE))
{
TRACE_PRINT1("SetSecurityDescriptorDacl failed: %#x\n", GetLastError());
return FALSE;
}
SECURITY_ATTRIBUTES sa = { sizeof sa, &sd, FALSE };
// The main loop creates an instance of the named pipe and
// then waits for a client to connect to it. When the client
// connects, a thread is created to handle communications
// with that client, and this loop is free to wait for the
// next client connect request. It is an infinite loop.
for (;;)
{
TRACE_PRINT1("\nPipe Server: Main thread awaiting client connection on %s\n", lpszPipename);
hPipe = CreateNamedPipeA(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
0, // client time-out
&sa); // default security attribute
if (hPipe == INVALID_HANDLE_VALUE)
{
TRACE_PRINT1("CreateNamedPipe failed, GLE=%d.\n", GetLastError());
return FALSE;
}
// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected)
{
TRACE_PRINT("Client connected, creating a processing thread.\n");
// Create a thread for this client.
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
InstanceThread, // thread proc
(LPVOID) hPipe, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID
if (hThread == NULL)
{
TRACE_PRINT1("CreateThread failed, GLE=%d.\n", GetLastError());
return FALSE;
}
else CloseHandle(hThread);
}
else
// The client could not connect, so close the pipe.
CloseHandle(hPipe);
}
HeapFree(hHeap, 0, pDacl);
return TRUE;
}
DWORD WINAPI InstanceThread(LPVOID lpvParam)
// This routine is a thread processing function to read from and reply to a client
// via the open pipe connection passed from the main loop. Note this allows
// the main loop to continue executing, potentially creating more threads of
// of this procedure to run concurrently, depending on the number of incoming
// client connections.
{
HANDLE hHeap = GetProcessHeap();
char* pchRequest = (char*) HeapAlloc(hHeap, 0, BUFSIZE * sizeof(TCHAR));
char* pchReply = (char*) HeapAlloc(hHeap, 0, BUFSIZE * sizeof(char));
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
BOOL fSuccess = FALSE;
HANDLE hPipe = NULL;
TRACE_ENTER("InstanceThread");
// Do some extra error checking since the app will keep running even if this
// thread fails.
if (lpvParam == NULL)
{
TRACE_PRINT( "\nERROR - Pipe Server Failure:\n");
TRACE_PRINT( " InstanceThread got an unexpected NULL value in lpvParam.\n");
TRACE_PRINT( " InstanceThread exitting.\n");
if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
return (DWORD)-1;
}
if (pchRequest == NULL)
{
TRACE_PRINT( "\nERROR - Pipe Server Failure:\n");
TRACE_PRINT( " InstanceThread got an unexpected NULL heap allocation.\n");
TRACE_PRINT( " InstanceThread exitting.\n");
if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
return (DWORD)-1;
}
if (pchReply == NULL)
{
TRACE_PRINT( "\nERROR - Pipe Server Failure:\n");
TRACE_PRINT( " InstanceThread got an unexpected NULL heap allocation.\n");
TRACE_PRINT( " InstanceThread exitting.\n");
if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
return (DWORD)-1;
}
// Print verbose messages. In production code, this should be for debugging only.
TRACE_PRINT("InstanceThread created, receiving and processing messages.\n");
// The thread's parameter is a handle to a pipe object instance.
hPipe = (HANDLE) lpvParam;
// Loop until done reading
while (1)
{
// Read client requests from the pipe. This simplistic code only allows messages
// up to BUFSIZE characters in length.
fSuccess = ReadFile(
hPipe, // handle to pipe
pchRequest, // buffer to receive data
BUFSIZE*sizeof(TCHAR), // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
if (!fSuccess || cbBytesRead == 0)
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
TRACE_PRINT("InstanceThread: client disconnected.\n");
}
else
{
TRACE_PRINT1("InstanceThread ReadFile failed, GLE=%d.\n", GetLastError());
}
break;
}
// Process the incoming message.
GetAnswerToRequest(pchRequest, pchReply, &cbReplyBytes);
// Write the reply to the pipe.
fSuccess = WriteFile(
hPipe, // handle to pipe
pchReply, // buffer to write from
cbReplyBytes, // number of bytes to write
&cbWritten, // number of bytes written
NULL); // not overlapped I/O
if (!fSuccess || cbReplyBytes != cbWritten)
{
TRACE_PRINT1("InstanceThread WriteFile failed, GLE=%d.\n", GetLastError());
break;
}
}
// Flush the pipe to allow the client to read the pipe's contents
// before disconnecting. Then disconnect the pipe, and close the
// handle to this pipe instance.
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
HeapFree(hHeap, 0, pchRequest);
HeapFree(hHeap, 0, pchReply);
TRACE_EXIT("InstanceThread");
terminateSelf();
return 1;
}
_Use_decl_annotations_
VOID GetAnswerToRequest( LPCSTR pchRequest,
LPSTR pchReply,
LPDWORD pchBytes )
// This routine is a simple function to print the client request to the console
// and populate the reply buffer with a default data string. This is where you
// would put the actual client request processing code that runs in the context
// of an instance thread. Keep in mind the main thread will continue to wait for
// and receive other client connections while the instance thread is working.
{
TRACE_PRINT1("Client Request String:\"%s\"\n", pchRequest);
DWORD dwError;
HANDLE hFile = getDeviceHandleInternal(pchRequest, &dwError);
TRACE_PRINT1("Driver Handle: %0p\n", hFile);
if (hFile)
{
char buf[BUFSIZE];
sprintf_s(buf, BUFSIZE, "%p,%lu", hFile, dwError);
strcpy_s(pchReply, BUFSIZE, buf);
*pchBytes = (DWORD) strlen(buf) * sizeof(char);
}
else
{
// Check the outgoing message to make sure it's not too long for the buffer.
if (FAILED(StringCchCopyA( pchReply, BUFSIZE, "default answer from server")))
{
*pchBytes = 0;
pchReply[0] = 0;
TRACE_PRINT("StringCchCopy failed, no outgoing message.\n");
return;
}
*pchBytes = (DWORD) (strlen(pchReply) + 1) * sizeof(char);
}
}
int main(int argc, char* argv[])
{
char *pipeName = NULL;
if (argc != 3)
{
return -1;
}
else
{
pipeName = argv[1];
g_sourcePID = atoi(argv[2]);
}
createPipe(pipeName);
#pragma warning(suppress: 6031)
getchar();
return 0;
}