Today's task is to extract the original command line text
for a running process.
There is a discussion about this subject on
StackOverflow.
The most useful hint was "You should be accessing the memory
in-context during your capture phase, after which point you should
operate only on the captured values. Otherwise you have a security
vulnerability." Raymond Chen Dec 16 '11 at 15:23. This made
sense, so I rewrote my Process module to capture the information at
first contact into a lookup table, then return the recorded info
during the NetMon queries.
12:45> My first BSoD. I tried to run too much untested code at the
same time. I lost the VerProd.h, Control/VerFile.h, and Control.rc
files; I lost the ProcInfo dialog box I created yesterday. I am again
reminded that it is a good idea to commit early and often. This is why
I use VM's for development, although I wish I weren't using my VS12
VM...
13:04> Doh! Another BSoD! I need to revive Win7, I can't keep
clobbering VS12.
13:28> Doh! BAD_POOL_CALLER. Time to go for a walk.
14:10> I think I found it. I added the initial process list as a
statically declared array. When the first new process was added I
reallocated the list, then tried to free the original list. Since the
original list was not allocated from the pool, this was a bad pool
pointer and triggered the kernel panic. I also added an IRQL check,
but I think it is probably not necessary.
14:21> The driver is working again, with the command line probe
still disabled.
14:30> Success. I am now seeing the original command line. Now I
need to recreate my lost ProcInfo dialog and print it out in the
details window.
15:15> Hmmm... Now that I know how svchost was invoked, I need
to follow it further down the rabbit hole. I most curious about
"svchost.exe -k NetworkService" which seems to be pinging servers
all over the world.
This blog
has some tips. For example: Process: [29C] svchost.exe
Command line: C:\WINDOWS\system32\svchost.exe -k NetworkService
I should be able to find a running Service with a matching command
line.
I still don't have the path to the "NetworkService" DLL.
This SO
pointed me to
this page
that showed the way. The -k parameter points to a key under \\HKLM\Software\Microsoft\Windows NT\CurrentVersion\SvcHost\
But there is nothing there! Then I notice that the service name
in the Services dialog is "NlaSvc". That is where I find the path
to the DLL: %SystemRoot%\System32\nlasvc.dll.
There doesn't seem to be any way for me to make the jump from the
command line parameter "NetworkService" back to the originating
service key. The only link I see is the value "ObjectName=NT
AUTHORITY\NetworkService". The name of the DLL to be loaded is in the
service registry key, but this information is not included in the
command line. Somehow svchost knows to load, or is told to load, the
library after the process is launched. This leaves me with no clear
way to trace from a running process back to the service key. The best
I can do is to enumerate all the modules loaded by the process and
present that list en masse to Control, then pick through the list and
cross-reference the library name against the parameters in service
keys. I could also flag any unsigned/untrusted modules for further
investigation.
Gathering the list of loaded modules for a running process will
require some peeking into the internal Windows structures.
Now that I have access to the process PEB, I can go spelunking...
This would be easy if I had WinDbg running, but I'm not quite to
the point where I think spending the time reviving Win7 is worth it.
I'll need to pound my head against a few more BSoD's.
I found
this link
that helped explain how to walk the LDR_DATA_TABLE_ENTRY list.
17:30> I added the library lists. A lot of code went in, here's
hoping it doesn't crash... The module list was empty in the Detail
window, then it BSoD'ed after a few minutes with
DRIVER_IRQL_NOT_LESS_OR_EQUAL. I know from experience that this error
can be a red herring, it is most likely just a bad pointer crash.
I will need to take this one step at a time...
21:00> Well, this is why I shouldn't debug drivers on the dev machine:
S:\Src\HQ\Dev\SB\Chip\win8\NetMon>git commit -m "NetMon: Debugging ProcProbeLibraries."
error: inflate: data stream error (unknown compression method)
fatal: loose object c3bb6658899cebd09ce5513170c5eb34490c7a82 (stored in .git/objects/c3/bb6658899cebd09ce5513170c5eb3449
0c7a82) is corrupt
S:\Src\HQ\Dev\SB\Chip\win8\NetMon>
22:00> I think I have recovered. But it is clearly time to
revive Win7 before doing any more driver testing. Tomorrow's task:
Bring the Win7 VM back to life. Remote Kernel Debugging: Reviving Win7
12:50> (Two days later.) I am back in business with Win7. I
added some code to prevent re-entry into ProcProbeLibraries() while I
am stepping through it. Remote debugging is painfully, inexplicably,
slow even when both machines are running on the same physical
hardware. 8GB of RAM is simply not enough to run 3 complete copies of
Windows without thrashing the swapfile. It takes about 3-4 seconds to
step from one line to the next, but the ability to examine live data
saves me from spending days writing throw-away hex dump code,
rebooting crashed machines, and basically going insane.
13:10> It is one baffling problem after another... Now Process.cpp
refuses to build, complaining that PEPROCESS is being defined more
than once in WDM.h and NtIfs.h. Why? I guess I had uncommented NtDDK.h
while trying to "clean up" the includes. Weird.
13:22> I am finally stepping through an un-optimized version of
ProcProbeLibraries(). I am not seeing my printf output in the debugger.
I will need to copy the text to a global buffer so I can monitor it
in the watch window.
13:40> OK, it took about 20 minutes to step through a single iteration
of the module loop. It seemed as though it crashed the first time
it tried to use the Flink of the pEntry. I added gPrintText to hold the
text from the most recent Print(). This time 'round I will try to walk
the module list by hand.
13:56> It took 9 minutes from the time I launched Win7 to gaining
control in VS15 at my breakpoint in ProcProbLibraries(). Now that I
am there... There is some wierd syntax going on in the watch window,
it is unable to evaluate "gPrintText" even though it works in the
immediate window as "db gPrintText"
:
pPeb= 7FF'FFFD:A000
pPeb->Ldr= 77A7:2640
pPeb->Ldr->InMemoryOrderModuleList= 0'77A7:2640
pPeb->Ldr->InMemoryOrderModuleList->Flink= 0'001B:2D40
pPeb->Ldr->InMemoryOrderModuleList->Blink= 0'001F:4790
The list terminator will be 0'77A7:2660, which is &InMemoryOrderModuleList.
This is also the value of InMemoryOrderModuleList.Blink
Following the links, I can validate each link by checking that
pNext->Blink == pEntry->Flink.
FullDllName.Buffer is &Flink + 0x40
1st Module: 0'001B:2D40
Flink= 0'001B:2E30
Blink= 0'77A7:2660 (Which is &InMemoryOrderModuleList->Flink)
FullDllName.Buffer= +0x40= 1B:2C18= 'C:\NetMon\NetMonUI.exe'
2nd Module: 0'001B:2E30
Flink= 0'001B:31B0
Blink= 0'001B:2D40 (1st Module)
FullDllName.Buffer= 1B:2CA0= 'C:\Windows\SYSTEM32\ntdll.dll'
3rd Module: 0'001B:31B0
Flink= 0'001B:3320
Blink= 0'001B:2E30 (2nd Module)
FullDllName.Buffer= 1B:3150= 'C:\Windows\system32\kernel32.dll'
4th Module: 0'001B:3320
Flink= 0'001B:4040
Blink= 0'001B:31B0
...
0'001B:4040= 0'001B:41B0
0'001B:41B0= 0'001B:43A0
0'001B:43A0= 0'001B:45D0
0'001B:45D0=
001B:4710 > 001E:F380 > 001E:F6F0 > 001E:F4C0 > 001E:FC30 >
001E:F8F0 > 001E:F9E0 > 001F:0800 > 001f:3ee0 > 001f:4010 >
001f4100 > 001f41f0 > 001f43d0 > 001f44c0 > 001f45b0 >
001f46a0 > 001f4790 > 77a72660
77A7:2660 terminates the list by being equal to &InMemoryOrderModuleList->Flink.
Oct 25 2015
Debugging ProcProbeLibraries is excruciatingly slow, as I spend
most of my time waiting for the target system to reboot. It was
especially painful today as I had to wait over two hours while Windows
installed 53 updates.
Oct 26 2015
I spent most of yesterday watching the very interesting motoGP
race in Sapang. My opinion is that Rossi knew exactly what he was
doing. Nick's opinion is that Marc was asking for it and there was
nothing wrong with what Rossi did.
Kernel debugging using the VS15 embedded debugger is unbearably
slow. I am now trying to use the stand-alone WinDbg from the Windows
10 DDK. (C:\Programs and Files (x86)\Windows Kits\10\Debuggers\x64\windbg)
At least I can edit files while I am waiting for the debugger. WinDbg
responds to the hard breakpoint in DrvEntry much faster, there is no
5 minute delay. I can also see the debug text in real-time. This makes
the debugger much more useful, but dramatically slows the overall
performance of the target system.
I converted Process.cpp to ProcList.cpp and moved all the code into
the ProcList class. I added a mutex to control access to the process
list. Now the driver crashes every time during load.
12:35> NetMon no longer crashes after I completely disabled all
code in ProcList. Now I will enable only ProcListCreate() and
ProcListDestroy().
12:40> NetMon crashed, pointing the fickle finger of blame at
ProcListCreate(), probably OSMUTEX. Using WinDbg is much better than
VS15; not only can I edit and rebuild the code, but I can leave WinDbg
connected and run multiple debug sessions without rebooting the
target. Now I need to see if I can step through the code using
WinDbg.
13:08> I stepped through DrvEntry and everything was fine, the
crash happens after I return. I paid a bit more attention to the
bugcheck and found that bugcheck 0x39 is SYSTEM_EXIT_OWNED_MUTEX.
I had neglected to release hProbeMtx in ProcList::Create2(). Netmon
now loads and unloads OK.
14:23> Enabling ProcListRecord()... This seems to be working, but
printing the names of up to 126 modules through WinDbg causes big
problems. I need to disable the debug print and try again.
14:52> ProcList is finally working. I am able to load, enumerate,
download, and unload without any problems.
WinDbg is still slow, but much better than using VS15.
16:18> A crash during testing (IRQL_NOT_LESS_THAN) was fixed by
lowering the required IRQL in ProcListRecord() to PASSIVE_LEVEL(0). A
mutex with a timeout requires APC_LEVEL(1) or lower.
ProcList.cpp:
/*************************************************************************/
/** ProcList.cpp: Manage information about running process. **/
/** (C)2015 nlited systems inc, Chip Doran **/
/*************************************************************************/
#include <ntifs.h>
#include <ntstrsafe.h>
#include <WinDef.h>
#include "StdTypes.h"
#include "Errors.h"
#include "VerID.h"
#include "OS.h"
#include "NetMon.h"
#include "Globals.h"
#include "PEB.h"
#pragma message(__FILE__": Optimizer disabled.")
#pragma optimize("",off)
static ZWQUERYINFORMATIONPROCESS _ZwQueryInformationProcess=0;
struct ProcessInfo_s {
UINT64 ProcID;
WCHAR ImagePath[260];
WCHAR CmdLine[260];
UINT ChkCt;
LARGE_INTEGER LastChk;
WCHAR Libraries[4096]; //Packed strings, terminated by 0-length string.
};
class ProcList {
public:
static ProcList *Ptr(HPROCLIST hProcList);
static int Create(HPROCLIST *phProcList);
int Destroy();
int Record(void);
int Query(UINT Type, UINT Index, UINT64 ProcID, WCHAR *pDst, UINT DstSz);
//Data
UINT Signature;
private:
ProcList(void);
~ProcList(void);
int Create2(void);
int RecordCreate(UINT64 ProcID);
int RecordUpdate(struct ProcessInfo_s *pProc);
int GetImagePath(UINT64 ProcID, WCHAR *pDst, UINT DstSz);
int GetCmdLine(UINT64 ProcID, WCHAR *pDst, UINT DstSz);
int GetLibrary(UINT64 ProcID, UINT Index, WCHAR *pDst, UINT DstSz);
ProcessInfo_s *FindID(UINT64 ProcID);
int Grow(void);
int Open(HANDLE *phProc, UINT64 ProcID);
void *FindFunction(const WCHAR *Name);
int ProbeImagePath(HANDLE hProc, UINT64 ProcID, WCHAR *pDst, UINT DstSz);
int ProbeCmdLine(HANDLE hProc, UINT64 ProcID, WCHAR *pDst, UINT DstSz);
int ProbeLibraries(HANDLE hProc, UINT64 ProcID, WCHAR *pDst, UINT DstSz);
int ProbeLibraries2(HANDLE hProc, UINT64 ProcID, WCHAR *pDst, UINT DstSz);
//Data
OSMUTEX hProbeMtx;
UINT ListSz;
UINT ListCt;
struct ProcessInfo_s *pList;
};
static HPROCLIST ghProcList;
static const struct ProcessDflt_s {
UINT64 ProcID;
const WCHAR *Name;
} ProcDefaults[]={
{ 4, L"NtKernel" },
{ (UINT64)-1 }
};
/*************************************************************************/
/** Public interface **/
/*************************************************************************/
int ProcListCreate(HPROCLIST *phProcList) {
int Err= ERR_OK;
HPROCLIST hProcList;
if(IsErr(Err= ProcList::Create(&hProcList))) {
Err= ErrorA(Err,FUNCA"Unable to create ProcList.");
} else {
if(!ghProcList)
ghProcList= hProcList;
if(phProcList)
*phProcList= hProcList;
}
return(Err);
}
int ProcListDestroy(HPROCLIST hProcList) {
int Err= ERR_OK;
ProcList *pList= ProcList::Ptr(hProcList);
if(!pList) {
Err= WarnA(ERR_NO_INIT,FUNCA"Bad hProcList[%X]",hProcList);
} else if(!IsErr(Err= pList->Destroy())) {
if((HPROCLIST)pList==ghProcList)
ghProcList= 0;
}
return(Err);
}
int ProcListRecord(void) {
int Err= ERR_OK;
ProcList *pList= ProcList::Ptr(ghProcList);
if(!pList) {
Err= ERR_NO_INIT;
} else {
Err= pList->Record();
}
return(Err);
}
int ProcListQuery(UINT Type, UINT Index, UINT64 ProcID, WCHAR *pDst, UINT DstSz) {
int Err= ERR_OK;
ProcList *pList= ProcList::Ptr(ghProcList);
if(!pList) {
Err= ERR_NO_INIT;
} else {
Err= pList->Query(Type,Index,ProcID,pDst,DstSz);
}
return(Err);
}
/*************************************************************************/
/** Public internals **/
/*************************************************************************/
ProcList *ProcList::Ptr(HPROCLIST hList) {
ProcList *pList=(ProcList*)(hList ? hList : ghProcList);
if(!pList || IsBadPtr(pList,sizeof(*pList),BADPTR_RW) || pList->Signature!=SIGNATURE_PROCLIST)
return(0);
return(pList);
}
int ProcList::Create(HPROCLIST *phProcList) {
int Err=ERR_OK;
ProcList *pList= new ProcList();
if(!pList) {
Err=ErrorA(ERR_NO_MEM, FUNCA"Unable to alloc %d.", sizeof(*pList));
} else if(IsErr(Err= pList->Create2())) {
delete pList;
} else {
*phProcList=(HPROCLIST)pList;
}
return(Err);
}
int ProcList::Destroy(void) {
int Err=ERR_OK;
delete this;
return(Err);
}
int ProcList::Record(void) {
int Err=ERR_OK;
UINT64 ProcID= (UINT64)PsGetCurrentProcessId();
struct ProcessInfo_s *pProc;
//Need to be at DISPATCH_LEVEL to do anything.
if(IsErr(IrqlChk(DISPATCH_LEVEL, "ProcRecord"))) {
Err=ERR_OK;
} else {
OsMutexAcquire(hProbeMtx,1000,"Record");
if(pProc= FindID(ProcID)) {
//Err= RecordUpdate(pProc);
} else {
Err= RecordCreate(ProcID);
}
OsMutexRelease(hProbeMtx);
}
return(Err);
}
int ProcList::Query(UINT Type, UINT Index, UINT64 ProcID, WCHAR *pDst, UINT DstSz) {
int Err=ERR_OK;
switch(Type) {
case CMD_PROCESS_QUERY_PATH: Err= GetImagePath(ProcID, pDst, DstSz); break;
case CMD_PROCESS_QUERY_CMDLINE: Err= GetCmdLine(ProcID, pDst, DstSz); break;
case CMD_PROCESS_QUERY_LIBRARY: Err= GetLibrary(ProcID, Index, pDst, DstSz); break;
default: Err= WarnA(ERR_BAD_ARG1, FUNCA"Invalid query type.");
}
return(Err);
}
/*************************************************************************/
/** Internals **/
/*************************************************************************/
ProcList::ProcList(void) {
Signature= SIGNATURE_PROCLIST;
OsMutexCreate(&hProbeMtx,"ProcProbeMtx");
}
ProcList::~ProcList(void) {
Signature|= SIGNATURE_INVALID;
if(pList)
MemFree(pList);
OsMutexDestroy(hProbeMtx);
}
int ProcList::Create2(void) {
int Err= ERR_OK;
OsMutexAcquire(hProbeMtx,1000,"Create2");
for(UINT n1=0;!IsErr(Err) && ProcDefaults[n1].ProcID!=-1;n1++) {
if(!IsErr(Err= Grow())) {
struct ProcessInfo_s *pEntry= &pList[ListCt++];
pEntry->ProcID= ProcDefaults[n1].ProcID;
wcsncpy(pEntry->ImagePath,ProcDefaults[n1].Name,STRSIZE(pEntry->ImagePath));
}
}
OsMutexRelease(hProbeMtx);
return(Err);
}
int ProcList::RecordCreate(UINT64 ProcID) {
int Err2, Err=ERR_OK;
HANDLE hProc=0;
struct ProcessInfo_s *pProc;
if(IsErr(Err=Grow()))
return(ErrorA(ERR_NO_MEM, FUNCA"Unable to grow process list beyond %d.", ListSz));
pProc=&pList[ListCt++];
pProc->ProcID=ProcID;
if(IsErr(Err=Open(&hProc, ProcID))) {
Err=WarnA(Err, FUNCA"Unable to open ProcID[%llX]", ProcID);
} else {
if(IsErr(Err2=ProbeImagePath(hProc, ProcID, pProc->ImagePath, STRSIZE(pProc->ImagePath))))
Err=WarnA(Err2, FUNCA"Unable to determine ProcID[%llX] image path.", ProcID);
if(IsErr(Err2=ProbeCmdLine(hProc, ProcID, pProc->CmdLine, STRSIZE(pProc->CmdLine))))
Err=WarnA(Err2, FUNCA"Unable to determine ProcID[%llX] command line.", ProcID);
if(IsErr(Err2=ProbeLibraries(hProc, ProcID, pProc->Libraries, STRSIZE(pProc->Libraries))))
Err=WarnA(Err2, FUNCA"Unable to read ProcID[%llX] library list.", ProcID);
else
PrintA(PRINT_DEBUG,"%S: %d modules.",pProc->ImagePath,Err2);
ZwClose(hProc);
}
return(Err);
}
int ProcList::RecordUpdate(struct ProcessInfo_s *pProc) {
int Err2, Err=ERR_OK;
HANDLE hProc=0;
//TODO: Reduce the number of times I walk the module list.
LARGE_INTEGER tick;
KeQueryTickCount(&tick);
if(pProc->ChkCt < 3 && tick.QuadPart>=pProc->LastChk.QuadPart) {
ULONG NextChk=(++pProc->ChkCt*100000000);
pProc->LastChk.QuadPart=tick.QuadPart+NextChk/KeQueryTimeIncrement();
if(IsErr(Err=Open(&hProc, pProc->ProcID))) {
Err=WarnA(Err, FUNCA"Unable to open ProcID[%llX]", pProc->ProcID);
} else {
if(IsErr(Err2=ProbeLibraries(hProc, pProc->ProcID, pProc->Libraries, STRSIZE(pProc->Libraries))))
Err=WarnA(Err2, FUNCA"Unable to read ProcID[%llX] library list.", pProc->ProcID);
else
PrintA(PRINT_DEBUG,"%S: %d modules",pProc->ImagePath,Err2);
ZwClose(hProc);
}
}
return(Err);
}
int ProcList::GetImagePath(UINT64 ProcID, WCHAR *pDst, UINT DstSz) {
int Err=ERR_OK;
struct ProcessInfo_s *pProc;
if(!(pProc= FindID(ProcID))) {
Err=WarnA(ERR_NOT_FOUND, FUNCA"ProcID[%llX] not found.", ProcID);
} else {
RtlStringCchCopyW(pDst, DstSz, pProc->ImagePath);
}
return(Err);
}
int ProcList::GetCmdLine(UINT64 ProcID, WCHAR *pDst, UINT DstSz) {
int Err=ERR_OK;
struct ProcessInfo_s *pProc;
if(!(pProc= FindID(ProcID))) {
Err=WarnA(ERR_NOT_FOUND, FUNCA"ProcID[%llX] not found.", ProcID);
} else {
RtlStringCchCopyW(pDst, DstSz, pProc->CmdLine);
}
return(Err);
}
int ProcList::GetLibrary(UINT64 ProcID, UINT Index, WCHAR *pDst, UINT DstSz) {
int Err=ERR_OK;
struct ProcessInfo_s *pProc;
if(!(pProc= FindID(ProcID))) {
Err=WarnA(ERR_NOT_FOUND, FUNCA"ProcID[%llX] not found.", ProcID);
} else {
UINT LibCt, ChrCt;
for(LibCt=ChrCt=0;LibCt<Index && ChrCt<STRSIZE(pProc->Libraries);ChrCt++) {
if(pProc->Libraries[ChrCt]==0) {
LibCt++;
if(pProc->Libraries[ChrCt+1]==0)
ChrCt= STRSIZE(pProc->Libraries);
}
}
if(ChrCt>=STRSIZE(pProc->Libraries)) {
Err= ERR_NOT_FOUND;
} else {
RtlStringCchCopyW(pDst, DstSz, &pProc->Libraries[ChrCt]);
}
}
return(Err);
}
ProcessInfo_s *ProcList::FindID(UINT64 ProcID) {
UINT n1;
for(n1=0;n1<ListCt;n1++) {
if(pList[n1].ProcID==ProcID) {
return(&pList[n1]);
}
}
return(0);
}
int ProcList::Grow(void) {
int Err=ERR_OK;
struct ProcessInfo_s *pNew;
UINT NewSz= ListSz+100;
UINT MemCt= NewSz*sizeof(pNew[0]);
if(ListCt>=ListSz) {
if(!(pNew=(struct ProcessInfo_s*)MemAlloc("ProcList", MemCt))) {
Err= ErrorA(ERR_NO_MEM, FUNCA"Unable to alloc %d.", MemCt);
} else {
if(pList) {
RtlCopyMemory(pNew, pList, ListCt*sizeof(pList[0]));
MemFree(pList);
}
ListSz= NewSz;
pList= pNew;
}
}
return(Err);
}
int ProcList::Open(HANDLE *phProc, UINT64 ProcID) {
int Err=ERR_OK;
NTSTATUS Status;
PEPROCESS eProc=0;
if(!_ZwQueryInformationProcess)
_ZwQueryInformationProcess=(ZWQUERYINFORMATIONPROCESS)FindFunction(L"ZwQueryInformationProcess");
if(!_ZwQueryInformationProcess) {
Err=WarnA(ERR_NOT_SUPPORTED, FUNCA "Bummer, ZwQueryInformationProcess not found.");
} else if(!NT_SUCCESS(Status=PsLookupProcessByProcessId((HANDLE)ProcID, &eProc))) {
Err=WarnA(ERR_NOT_FOUND, FUNCA"ProcID %X not found.", ProcID);
} else if(!NT_SUCCESS(Status=ObOpenObjectByPointer(eProc, 0, 0, 0, 0, KernelMode, phProc))||!*phProc) {
Err=WarnA(ERR_NOT_FOUND, FUNCA"Unable to transform eProc[%X] to hProc.", eProc);
}
if(eProc)
ObDereferenceObject(eProc);
return(Err);
}
void *ProcList::FindFunction(const WCHAR *Name) {
UNICODE_STRING NameU;
void *pFunc;
RtlInitUnicodeString(&NameU, Name);
pFunc=MmGetSystemRoutineAddress(&NameU);
if(!pFunc)
WarnA(ERR_NOT_SUPPORTED, FUNCA" System function '%S' not found.", Name);
return(pFunc);
}
int ProcList::ProbeImagePath(HANDLE hProc, UINT64 ProcID, WCHAR *pDst, UINT DstSz) {
int Err=ERR_OK;
NTSTATUS Status;
UNICODE_STRING *pName=0;
ULONG MaxLength=1024; //Buffer length (bytes)
if(!(pName=(UNICODE_STRING*)ExAllocatePoolWithTag(NonPagedPool, MaxLength, 'ipgD'))) {
Err=WarnA(ERR_NO_MEM, FUNCA"Unable to alloc %d.", MaxLength);
} else {
RtlZeroMemory(pName, MaxLength);
//pName will be initialized by ZwQuery()
if(!NT_SUCCESS(Status=_ZwQueryInformationProcess(hProc, ProcessImageFileName, pName, MaxLength, &MaxLength))) {
Err=WarnA(ERR_FAILED, FUNCA"ZwQueryInformationProcess failed. [%X]", Status);
} else if(!pName->Length) {
Err=WarnA(ERR_NOT_FOUND, FUNCA"No info for ProcID[%X]", ProcID);
} else {
PrintA(PRINT_DEBUG, FUNCA"ProcID[%X] is '%wZ'", ProcID, pName);
RtlStringCchCopyUnicodeString(pDst, DstSz, pName);
}
ExFreePoolWithTag(pName, 'ipgD');
}
return(Err);
}
int ProcList::ProbeCmdLine(HANDLE hProc, UINT64 ProcID, WCHAR *pDst, UINT DstSz) {
int Err=ERR_OK;
NTSTATUS Status;
PROCESS_BASIC_INFORMATION Peb;
ULONG readCt;
RtlZeroMemory(&Peb, sizeof(Peb));
if(!NT_SUCCESS(Status=_ZwQueryInformationProcess(hProc, ProcessBasicInformation, &Peb, sizeof(Peb), &readCt))) {
Err=WarnA(ERR_FAILED, FUNCA"ZwQuery(PEB) failed.");
} else {
PrintA(PRINT_DEBUG, FUNCA"PEB[%llX,%d]", Peb.PebBaseAddress, sizeof(Peb.PebBaseAddress));
__try {
struct _PEB *pPeb=(_PEB*)Peb.PebBaseAddress;
RTL_USER_PROCESS_PARAMETERS *pParms=pPeb->ProcessParameters;
RtlStringCchCopyUnicodeString(pDst, DstSz, &pParms->CommandLine);
PrintA(PRINT_DEBUG, FUNCA"ProcID[%llX] command line: '%S'", ProcID, pDst);
} __except(EXCEPTION_EXECUTE_HANDLER) {
Err=WarnA(ERR_FAILED, FUNCA"Exception while probing command line.");
}
}
return(Err);
}
int ProcList::ProbeLibraries(HANDLE hProc, UINT64 ProcID, WCHAR *pDst, UINT DstSz) {
int Err=ERR_OK;
static BYTE IsProbing=0;
if(!gpCfg->DisableProcProbe && !IsProbing++) {
Err=ProbeLibraries2(hProc, ProcID, pDst, DstSz);
IsProbing=0;
}
return(Err);
}
int ProcList::ProbeLibraries2(HANDLE hProc, UINT64 ProcID, WCHAR *pDst, UINT DstSz) {
int Err=ERR_OK;
NTSTATUS Status;
PROCESS_BASIC_INFORMATION Peb;
ULONG readCt;
RtlZeroMemory(&Peb, sizeof(Peb));
if(!NT_SUCCESS(Status=_ZwQueryInformationProcess(hProc, ProcessBasicInformation, &Peb, sizeof(Peb), &readCt))) {
Err=WarnA(ERR_FAILED, FUNCA"ZwQuery(PEB) failed.");
} else __try {
struct _PEB *pPeb=(_PEB*)Peb.PebBaseAddress;
if(!pPeb->Ldr) {
PrintA(PRINT_DEBUG, FUNCA"ProcID[%llX] No module list?", ProcID);
} else {
//InMemoryOrderModuleList is the entry point into a circular linked
//list of LIST_ENTRY records. The list is terminated by a Flink that
//points back to &InMemoryOrderModuleList to close the circle.
//Each LIST_ENTRY (except the head) is embedded in a LDR_DATA_TABLE_ENTRY.
//I created the alias LDR_DATA_TABLE_ENTRY2 that removes the need to do
//calculate the offset to the module struct.
UINT ChrCt=0, ModuleCt=0;
LIST_ENTRY *pEntry, *pHead=&pPeb->Ldr->InMemoryOrderModuleList;
LIST_ENTRY *pTerm=pHead->Blink; //Not necessary?
LDR_DATA_TABLE_ENTRY2 *pModule;
PrintA(PRINT_DEBUG, FUNCA"ProcID[%llX] Module list %llX (Term=%llX)", ProcID, pHead, pTerm);
for(pEntry=pHead->Flink;pEntry!=0&&pEntry!=pTerm;pEntry=pEntry->Flink) {
ModuleCt++;
pModule=(LDR_DATA_TABLE_ENTRY2*)pEntry;
const WCHAR *pModPath=pModule->FullDllName.Buffer;
//PrintA(PRINT_DEBUG,FUNCA" pModule=%llX Dll.Buffer=%llX[%llX]",pModule,&pModPath,pModPath);
//PrintA(PRINT_DEBUG, FUNCA" Module %d: %S", ModuleCt, pModPath);
//Copy the FullDllName text into the packed string buffer pDst.
if(ChrCt+pModule->FullDllName.Length/2+1 >= DstSz) {
WarnA(ERR_TOO_SMALL,FUNCA"Ran out of room in Libraries[]");
break;
} else {
RtlStringCchCopyUnicodeString(&pDst[ChrCt],DstSz-ChrCt,&pModule->FullDllName);
ChrCt+= pModule->FullDllName.Length/2;
pDst[ChrCt++]= 0; //Redundant, but just to make sure.
}
}
pDst[ChrCt]=0; //Terminate the packed list.
if(!IsErr(Err))
Err= ModuleCt;
}
} __except(EXCEPTION_EXECUTE_HANDLER) {
Err=WarnA(ERR_FAILED, FUNCA"Exception while probing library list.");
}
return(Err);
}