Monitor the creation of remote threads
Author: Threepenny an e-mail: zhongts@163.com Date: 2004.12.29
Remote thread technology is used in a large number of software in Trojans, worms, etc., and has a relatively high concealment by inserting a thread in other processes. For example, there are more than a dozen threads in the common Explorer.exe process, and after inserting a thread, who can't tell which one is inserted. This article provides a way to monitor the creation of remote threads, record important data such as IDs of the remote thread, so that you can easily find which process in which processes are inserted from the remote thread.
The following is the case where ICESWORD V1.06 and the remote thread recorded by this code: Since this article needs to be written driver, the reader who is not familiar with the driver can find some driving books first, here is recommended to be everyone Luo Yunbin's website downloaded to download the translated KMDTUT. At the same time, I downloaded KMDKIT because this code used this package. After installing Masm32 and Kmdkit, you can compile the code provided herein. If you compile the code, the error lnk2001: Unresolved External Symbol _psremoveCreatethreadNotifyRoutine @ 4 error, then copy the NTOSKRNL.LIB to the lib / w2k folder to overwrite the original file. I am also just learning to drive programming. The following is just a very simple example. If you want to use a lot of things to do.
The first is the creation of the monitoring thread and then distinguish what is a remote thread. To monitor the creation of threads, you need to use such a function PSsetCreThreadNotifyRoutine. With this function we register a callback function, each time you create a new thread is created, we call our callback function. In this callback function we can record all threads. If you want to monitor the creation of the process, there is another function PSSetCreateProcessNotifyRoutine to complete this feature. The function prototype of the callback function created by the monitoring thread is as follows:
Void (* pcreate_thread_notify_routine) (in Handle Processid, In Handle Threadid, In Boolean Create); ProcessID is the process number, the process number here is a process that includes the thread instead of creating the thread. ThreadID is the thread number of the thread to be created. CREATE is used to point out whether it is created a thread or destroy the thread. The prototype of the callback function created by the monitoring process is as follows:
Void (* pcreate_process_notify_routine) (in Handle Parentid, In Boolean Create);
Parentid is the parent process number, and ProcessID is the process number, create represents the creation or destruction process. With these two functions we can monitor all process and thread creation and destruction activities. Let's take a look at the code, I will raise the main code. DriverEntry proc uses esi, pDriverObject: PDRIVER_OBJECT, pusRegistryPath: PUNICODE_STRINGLOCAL status: NTSTATUSLOCAL pDeviceObject: PDEVICE_OBJECT ...... mov g_dwProcessId, 0mov g_bMainThread, FALSElea eax, _ProcessCallbackinvoke PsSetCreateProcessNotifyRoutine, eax, FALSElea eax, _ThreadCallbackinvoke PsSetCreateThreadNotifyRoutine, eaxmov status, eax .. .... DriveRentry Endp is the code part of the registered callback function, _ProcessCallback and _threadCallback are the process and thread monitor functions, respectively. The callback function is registered in the startup section of the driver, and it is also necessary to remove the registered callback function in the driver uninstall portion. code show as below:
_Driverunload Proc PDRIVEROBJECT: PDRIVER_OBJECT
lea eax, _ProcessCallbackinvoke PsSetCreateProcessNotifyRoutine, eax, TRUElea eax, _ThreadCallbackinvoke PsRemoveCreateThreadNotifyRoutine, eaxinvoke IoDeleteSymbolicLink, addr g_usSymbolicLinkNamemov eax, pDriverObjectinvoke IoDeleteDevice, (DRIVER_OBJECT PTR [eax]). DeviceObjectret_DriverUnload endp
The second parameter to the PSSetCreateProcessNotifyRoutine function can remove the registered process callback function. Removing the registered thread callback function needs to call the psremovecreatethreadNotifyRoutine function. This function is an undisclonic function. From Windows XP, it is not possible to verify because there is no Windows 2000 system in hand. You can see if there is this function in your Windows 2000 system. . Because this unappromant function, it is not possible to call it directly, it is necessary to introduce the library. The way to generate the introduction library is also very simple, using the INC2L tool comes with the MASM32 package, usage can be used to refer to Masm32 to generate the introduction library. This is this reason why the above mentioned it is to overwrite ntoskrnl.lib files.
Originally monitored remote thread only needs to register a thread callback function, because it is to be judged whether it is a remote thread, to determine if it is a remote thread depending on whether the process of creating a thread and the process containing threads. So, we also need to register a process callback function.
_ProcessCallback Proc Uses ESI, ParentID: DWORD, ProcessID: DWORD, BCREATE: DWORD
.IF BCREATE MOV EAX, Processid Mov g_dwprocessid, Eax Mov g_bmainthread, true.endifret_processCallback Endp
This is the process callback function. If you create a process, set G_BMAINTHREAD to TRUE, save the process ID to g_dwprocessid. Because a new process is created, its main thread is not its own creation, but its parent process created. Here the parent process and its own process are definitely not the same process, but the main thread that is created at this time is not a remote thread. The above code is the creation of the recording process, then the thread that is recalled immediately is not a remote thread. _ThreadCallback proc uses ebx esi edi, ProcessId: DWORD, ThreadId: DWORD, bCreate: DWORDLOCAL lpParentEProcess, lpEProcessLOCAL dwParentPID, dwParentTIDcmp g_bMainThread, TRUEje exit_0cmp bCreate, 0je exit_0cmp ProcessId, 4je exit_0invoke PsGetCurrentProcessIdmov dwParentPID, eaxcmp eax, ProcessIdje exit_0invoke PsGetCurrentThreadIdmov dwParentTID, eaxinvoke PsLookupProcessByProcessId, dwParentPID, addr lpParentEProcesscmp eax, STATUS_SUCCESSjne exit_0invoke PsLookupProcessByProcessId, ProcessId, addr lpEProcesscmp eax, STATUS_SUCCESSjne exit_0mov esi, lpParentEProcessadd esi, g_dwOffsetmov edi, lpEProcessadd edi, g_dwOffsetinvoke DbgPrint, $ CTA0 ( "caller: Name =% s PID =% d TID =% D / T / T is called: Name =% S PID =% D TID =% D / N "), / ESI, DWPARENTPID, DWPARENTTID, EDI, Processid, Threadidexit_0: Mov g_bmainthread, falseret_threadcallback endp
This code is a thread callback function and is our core code. First judge whether it is the main thread creation of a process, if it is not to be judged. Is it created a thread? If it is, it continues to judge. Is the process of the system process? If it is ignored. This is because each open folder, switch folder Explorer.exe will create a remote thread in the System process, so we ignore it. Everyone can take these two comments to take a look at the output of the program.
Because there are only two parameters in the callback function, one is ProcessID to indicate the process number containing the thread, and the other is ThreadID representing the thread number created. So we also need to find the process number that creates a thread and can compare the process of creating a thread and the process containing the thread is not the same process, so that it is not a remote thread. Here, you will mention a problem. When a process creates a thread, the system is called our thread callback function in this process, so we can get the process number through the PsgetCurrentProcessID function. The thread number is obtained by PsgetCurrentThreadID, pay attention to this thread is not the thread to be created, but a thread that creates a threaded code.
The code then invokes an undoled function PslookupProcessByProcessID to get an EPROCESS structure of a process, and the EPRocess structure has a detailed description in the W2kundoc.inc.inc on the KMDKIT, and saves the name of the process in the ImageFileName member of the EPRocess structure. Since the offset position of the system different imageFileName members is also different, so the global variable g_dwoffset is used to save this offset according to the different code of the system. The following is the code judgment system: invoke PsGetVersion, NULL, addr g_dwSystemMinorVersion, NULL, NULL.if g_dwSystemMinorVersion == 0mov g_dwOffset, 1FCh.elseif g_dwSystemMinorVersion == 1mov g_dwOffset, 174h.elseif g_dwSystemMinorVersion == 2mov g_dwOffset, 154h.endif
After all the work is done, it is to output the collected information, and can achieve this with the DBGPrint function.
Everyone can register / run the RemoteThreadMonitor.sys file generated by the CMDManager tool from Kmdkit, and then view the output of the code via the DBGVIEW or SOFTICE tool. I know the thread number of the remote thread can kill the remote thread with tools such as Process Explorer.
Reference: (1) SINISTER Writing Process / Thread Monitor http://www.xfocus.net/Articles/200303/495.html (2) DDK (3) kmdkit http://www.freewebs.com/four- F / (4) kmdtut in Chinese, Chinese translation] Yeah.net
The following article is unknown
/ * Sometimes we want to be able to dynamically monitor the creation and destruction of any process / thread in the system. For
To this purpose, I looked through the DDK manual and found the pssetcreateprocessNotifyRoutine () (),
PssetCreateThreadNotifyRoutine (), and so on can be implemented. These two functions can
Monitor processes / threads by registering a CallBalck function to the system. The function is as follows:
NTSTATUS
PSsetCreateProcessNotifyRoutine
In pcreate_process_notify_routine notifyroutine,
In Boolean Remove
);
Void
(* PCREATE_PROCESS_NOTIFY_ROUTINE)
In Handle ParentID,
In Handle Processid,
In Boolean Create
);
NTSTATUS
PSSetcreatethreadNotifyRoutine
In pcreate_thread_notify_routine notifyroutine
);
Void
(* PCREATE_THREAD_NOTIFY_ROUTINE)
In Handle Processid,
In Handle ThreadID,
In Boolean Create
);
It can be seen from the original shape that its Callback function only provides a process ID / thread ID. Not available
Process name. Then we have to further access the process name through the process ID. This requires an unobuutable
Function pslookupprocessByProcessId (). The function is as follows:
NTSTATUS PSLOKUPPROCESSBYPROCESSID
In ulong ulprocid,
Out peprocess * peprocess
);
The EPROCESS structure output from the function is also an unprecedented kernel process structure, and many people call it KPEB.
Offset 0x1FC in the EPROCESS structure points to the offset of the current process name. (This structure can be
Delivery in the driver. However, there is no structure that has a structure, there are many masters on the Internet have given its structure. Have
Interests can be searched by you, or go to IFS DDK, here is because the structure is too long, it is not posted)
With this structure, we can get the process name from it. The NT system also provides a function that can be dynamped
Load the image of the process. This function can get the DLL name and full path called when the process is planted.
Some image information. Get more detailed process loading information provides better help.
The function is as follows:
NTSTATUS
PSsetLoadImagenotifyRoutine
In PLOAD_IMAGE_NOTIFY_ROUTINE NOTIFYROUTINE
);
Void
(* PloAd_image_notify_routine)
In Punicode_String FullimageName,
In Handle Processid, // Where image is mapped
IN PIMAGE_INFO ImageInfo
);
Typedef struct _image_info {
Union {
Ulong Properties;
Struct {
Ulong ImageAddressingMode: 8; // Code Addressing Mode
Ulong systemmodeImage: 1; // system mode image
Ulong ImageMappedtoallPids: 1; // mapped in all processes
Ulong reserved: 22;
}
}
PVOID imageBASE;
Ulong imageselector;
Ulong imageSize;
Ulong imageesenumber;
Image_INFO, * PIMAGE_INFO;
Using the functions and structures provided above, we can implement a process / thread monitor. Below
The code demonstrates how to implement this function. * /
/ ************************************************** *****************
File name: WSSPROCMON.C
Description: Process / Thread Monitor
Author: sinister
Last modified: 2002-11-02
*********************************************************** *************** /
#include "ntddk.h" #include "windef.h" #include "string.h" #define sysname "system" // # define processnameoffset 0x1fc
ULONG GetProcessNameOffset (); VOID Unload (IN PDRIVER_OBJECT DriverObject); static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); NTSTATUS PsLookupProcessByProcessId (IN ULONG ulProcId, OUT PEPROCESS * pEProcess); VOID ProcessCreateMon (IN HANDLE hParentId, IN HANDLE PId, IN BOOLEAN bCreate); VOID ThreadCreateMon (IN HANDLE PId, IN HANDLE TId, IN BOOLEAN bCreate); VOID ImageCreateMon (IN PUNICODE_STRING FullImageName, IN HANDLE ProcessId, IN PIMAGE_INFO ImageInfo); // drive inlet ULONG ProcessNameOffset = 0; NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject, in Punicode_String RegistryPath)
{Unicode_String NameString, LinkString; PDEvice_Object DeviceObject; NTSTATUS STATUS; INT i; // Established
RTLinitunicodeString (& NameString, L "// device // wssprocmon); status = IocreateDevice (driverObject, 0, & namestring, file_device_unknown, 0, true, & deviceObject);
IF (! NT_Success (status)) Return Status;
RTLinitunicodeString (& linkstring, l "// dosdevices // wssprocmon); status = ocreatesymboliclink (& linkstring, & namestring);
IF (! nt_success (status)) {IodeleteDevice (driverObject-> deviceObject); Return Status;
} Processnameoffset = getProcessNameOffset (); / * status = pssetloadimagenotifyroutine (imagecreatemon);
IF (! nt_success (status)) {dbgprint ("pssetloadimagenotifyroutine () / n"); return status;} * /
Status = pssetcreatethreadNotifyRoutine (Threadcreatemon); if (! nt_success (status)) {dBGPrint ("PSSetcreThreadNotifyRoutine () / n"); Return Status;}
Status = pssetCreateProcessNotifyRoutine (ProcessCreatemon, false); if (! NT_Success (status)) {dbgprint ("PssetCreateProcessNotifyRoutine () / n"); Return status;}
for (i = 0; i
// Processing device object operation
Static NTSTATUS MYDRVDISPATCH (in PDevice_Object DeviceObject, in PIRP IRP)
{IRP-> iostatus.status = status_success; Irp-> iostatus.information = 0L; IOCOMPLETEREQUEST (IRP, 0); return IRP-> iostatus.status;}
HANDLE g_dwProcessId; BOOL g_bMainThread; VOID ProcessCreateMon (IN HANDLE hParentId, IN HANDLE PId, IN BOOLEAN bCreate) {PEPROCESS EProcess; ULONG ulCurrentProcessId; LPTSTR lpCurProc; NTSTATUS status; #ifdef _AMD64_ULONG ProcessId = HandleToUlong (PId); status = PsLookupProcessByProcessId (ProcessId, & EProcess); # else HANDLE ProcessId = PId; status = PsLookupProcessByProcessId ((ULONG) PId, & EProcess); # endifif) (NT_SUCCESS (status!) {DbgPrint ( "PsLookupProcessByProcessId () / n"); return;}
if (bCreate) {// g_dwProcessId = ProcessId; g_bMainThread = TRUE; lpCurProc = (LPTSTR) EProcess; lpCurProc = lpCurProc ProcessNameOffset; DbgPrint ( "CREATE PROCESS = PROCESS NAME:% s, PROCESS PARENTID:% d, PROCESS ID:% D, Process Address% x: / n ", lpcurproc, hparentid, pid, eprocess);} else {dbgprint (" tERMINATED == Process ID:% D / N ", PID);}}
VOID ThreadCreateMon (IN HANDLE PId, IN HANDLE TId, IN BOOLEAN bCreate) {PEPROCESS EProcess, ParentEProcess; LPTSTR lpCurProc, lpParnentProc; NTSTATUS status; #ifdef _AMD64_ULONG System = 4; ULONG dwParentPID = HandleToUlong (PsGetCurrentProcessId ()); // create the process ULONG thread ProcessId = HandleToUlong (PId); status = PsLookupProcessByProcessId (ProcessId, & EProcess); status = PsLookupProcessByProcessId (dwParentPID, & ParentEProcess); # elseHANDLE System = (HANDLE) 4; // under xp, in win2k is 8HANDLE dwParentPID = PsGetCurrentProcessId (); // Create the process of the thread Handle ProcessId = PID; // ProcessID is the process number, the process number here is the process of including the thread, not the process of creating the thread status = pslookuProcessByProcessId ((Ulong) ProcessID, & Eprocess); Status = PslookupprocessByProcessId ((Ulong) dwparentpid, & ParenteProcess); # ENDIF
IF (! NT_Success (status)) {DBGPrint ("PslookUpProcessByProcessid () / n"); Return;}
if (bCreate) {if ((g_bMainThread == TRUE) && (ProcessId = System) && (ProcessId = dwParentPID)!!) {HANDLE dwParentTID = PsGetCurrentThreadId (); lpCurProc = (LPTSTR) EProcess; lpParnentProc = (LPTSTR) ParentEProcess; LPCurProc = ProcessNameOffset; lpParNentProc = processNameOffset; dbgprint ("Caller: Name =% s pid =% d tid =% D / T / TCALLED: Name =% s pid =% D tid =% d / n", / LPPARNTPROC , dwParentPID, dwParentTID, lpCurProc, ProcessId, TId); g_bMainThread = FALSE;} lpCurProc = (LPTSTR) EProcess; lpCurProc = lpCurProc ProcessNameOffset; DbgPrint ( "CREATE THREAD = PROCESS NAME:% s PROCESS ID:% d, THREAD ID: % D / N ", LPCurProc, PID, TID);} else {DBGPRINT (" Terminated == Thread ID:% D / N ", TID);}} // Void ImageCreatemon (in Punicode_String FullimageName, in Handle Processid, in PIMAGE_INFO ImageInfo) //// {// DBGPrint ("FullimageName:% s, Process ID:); // DBGPrint (" ImageBase:% x, imagesize:% d / n ", imageinfo-> imagebase, imageinfo-> imagesize; //}
Ulong getProcessNameOffset () {peprocess curproc; int i;
Curproc = psgetcurrentprocess ();
//// scan for 12kb, hopping the kpeb never grows this big! // for (i = 0; i <3 * Page_size; i ) {
IF (! Strncmp (sysname, (pchar) Curproc I, Strlen (Sysname))) {
Return i;}}
//// name not found - OH, Well // Return 0;}
According to your change to C code, and support WinXP-64 .......