2015年9月20日星期日

根据PEB获取命令行参数

#include <ntifs.h>
//#include <ntddk.h> //这两个次序不能乱(乱会出错的),有上面的,这个可以注释掉。
//#include <winternl.h> //在Windows Kits\8.0\Include\um\winternl.h。可是不能用:fatal error C1083: 无法打开包括文件:“winternl.h”: No such file or directory
#include <windef.h>

/*
功能:获取程序的命令行参数。

尽管有Windows Kits\8.0\Include\um\winternl.h。
但是:不包含64位的,因为:

32位系统下:
kd> dt _PEB ProcessParameters
ntdll!_PEB
   +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
64位系统下:
0: kd> dt _PEB ProcessParameters
nt!_PEB
   +0x020 ProcessParameters : Ptr64 _RTL_USER_PROCESS_PARAMETERS
并且64位系统下的WOW64也是这样的。

注意:WDK7600.16385.1这个是没有的PEB的定义的。

made by correy
made at 2015.08.13
*/

#define tag  'tset' //test

//https://msdn.microsoft.com/en-us/library/windows/desktop/aa813708(v=vs.85).aspx
typedef struct _PEB_LDR_DATA {
  BYTE       Reserved1[8];
  PVOID      Reserved2[3];
  LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

//https://msdn.microsoft.com/en-us/library/windows/desktop/aa813741(v=vs.85).aspx
typedef struct _RTL_USER_PROCESS_PARAMETERS {
  BYTE           Reserved1[16];
  PVOID          Reserved2[10];
  UNICODE_STRING ImagePathName;
  UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;

//Windows Kits\8.0\Include\um\winternl.h
typedef
VOID
(NTAPI *PPS_POST_PROCESS_INIT_ROUTINE) (
    VOID
    );

#if defined(_WIN64) //defined(_AMD64_) || defined(_IA64_) //

//https://msdn.microsoft.com/en-us/library/windows/desktop/aa813706(v=vs.85).aspx
typedef struct _PEB {
    BYTE Reserved1[2];
    BYTE BeingDebugged;
    BYTE Reserved2[21];
    PPEB_LDR_DATA LoaderData;
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
    BYTE Reserved3[520];
    PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
    BYTE Reserved4[136];
    ULONG SessionId;
} PEB;

#else

//https://msdn.microsoft.com/en-us/library/windows/desktop/aa813706(v=vs.85).aspx
typedef struct _PEB {
  BYTE                          Reserved1[2];
  BYTE                          BeingDebugged;
  BYTE                          Reserved2[1];
  PVOID                         Reserved3[2];
  PPEB_LDR_DATA                 Ldr;
  PRTL_USER_PROCESS_PARAMETERS  ProcessParameters;
  BYTE                          Reserved4[104];
  PVOID                         Reserved5[52];
  PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
  BYTE                          Reserved6[128];
  PVOID                         Reserved7[1];
  ULONG                         SessionId;
} PEB, *PPEB;

#endif


/*
摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\base\ntos\inc\ps.h
此函数在XP 32上就已经导出,应该可以放心使用。
或者ZwQueryInformationProcess 的 ProcessBasicInformation.
*/
NTKERNELAPI
PPEB
PsGetProcessPeb(
    __in PEPROCESS Process
    );

//--------------------------------------------------------------------------------------

/*
这个是ZwQuerySystemInformation用的结构。
http://msdn.microsoft.com/en-us/library/windows/desktop/ms725506(v=vs.85).aspx
*/
typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    BYTE Reserved1[48];
    PVOID Reserved2[3];
    HANDLE UniqueProcessId;
    PVOID Reserved3;
    ULONG HandleCount;
    BYTE Reserved4[4];
    PVOID Reserved5[11];
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION;


/*
一下定义摘自:
C:\Program Files (x86)\Windows Kits\8.0\Include\um\winternl.h或者
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include\winternl.h
更多的信息,可看:
http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/System%20Information/SYSTEM_INFORMATION_CLASS.html#SystemProcessInformation
http://doxygen.reactos.org/d2/d5c/ntddk__ex_8h_source.html
*/
typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemBasicInformation = 0,
    SystemPerformanceInformation = 2,
    SystemTimeOfDayInformation = 3,
    SystemProcessInformation = 5,
    SystemProcessorPerformanceInformation = 8,
    SystemInterruptInformation = 23,
    SystemExceptionInformation = 33,
    SystemRegistryQuotaInformation = 37,
    SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;


/*
摘自:http://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx。
有修改。
*/
NTSTATUS /* WINAPI NtQuerySystemInformation */ ZwQuerySystemInformation(
  _In_       SYSTEM_INFORMATION_CLASS SystemInformationClass,
  _Inout_    PVOID SystemInformation,
  _In_       ULONG SystemInformationLength,
  _Out_opt_  PULONG ReturnLength
);


VOID print_commandLine(IN HANDLE PId)
/*
注意:
1.用这个办法取ImagePathName,IDLE和system这两个应该获取不到。
2.此函数用在进程回调中获取不到内容,线程回调的就不说了。
3.添加对类似进程回调等类似的情况,正常情况下没有试验。
*/
{
    PEPROCESS    m_process;
    NTSTATUS     status = STATUS_SUCCESS;
    KAPC_STATE   m_kapc_state;
    PPEB peb = 0;
    PRTL_USER_PROCESS_PARAMETERS  ProcessParameters = 0;
    UNICODE_STRING CommandLine = {0};
    PPEB_LDR_DATA LoaderData = 0;

    if (0 == PId)
    {
        return ;//IDLE
    }

    if (PsGetProcessId(PsInitialSystemProcess) == PId) //PsIsSystemThread
    {
        return;//system
    }

    status=PsLookupProcessByProcessId(PId, &m_process);
    if(!NT_SUCCESS(status))
    {
        return ;//无效进程。
    }

    KeStackAttachProcess (m_process,&m_kapc_state);

    peb = PsGetProcessPeb(m_process);//注意:IDLE和system这两个应该获取不到。

    ProcessParameters = peb->ProcessParameters;

#if defined(_WIN64)

    //LoaderData = peb->LoaderData;//64位下这个是有的,不用判断。
    KdPrint(("ProcessParameters->CommandLine:%wZ\r\n", &ProcessParameters->CommandLine));//WOW64进程也是这样的。

#else
    LoaderData = peb->Ldr;

    //32位下,至少XP这样,!PEB可显示CommandLine,但是_PEB的结构下的ProcessParameters看到的内容为空。
    if(LoaderData == NULL) //http://bbs.pediy.com/showthread.php?t=88730&page=2
    {
        if(ProcessParameters != NULL && ProcessParameters->CommandLine.Length != 0 && ProcessParameters->CommandLine.MaximumLength != 0 && ProcessParameters->CommandLine.Buffer != NULL)
        {
            //思路参考:http://www.douban.com/note/176759449/
            CommandLine.Buffer = (PWCH)((SIZE_T)ProcessParameters + (SIZE_T)ProcessParameters->CommandLine.Buffer);
            CommandLine.Length = ProcessParameters->CommandLine.Length;
            CommandLine.MaximumLength = ProcessParameters->CommandLine.MaximumLength;

            KdPrint(("ProcessParameters->CommandLine:%wZ\r\n", &CommandLine));
        }
    }
    else
    {
        KdPrint(("ProcessParameters->CommandLine:%wZ\r\n", &ProcessParameters->CommandLine));
    }

#endif  

    KeUnstackDetachProcess(&m_kapc_state);

    ObDereferenceObject(m_process);  
}


VOID ProcessCreateMon ( IN HANDLE hParentId, IN HANDLE PId,IN BOOLEAN bCreate )
{
    UCHAR * lpCurProc;

    if ( bCreate )
    {
        print_commandLine(PId);
    }
}


NTSTATUS show_all_process_commandLine(VOID)
    /*
    功能:
    */
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    SYSTEM_PROCESS_INFORMATION * pspi = 0;
    SYSTEM_PROCESS_INFORMATION * pspi_temp = 0;
    ULONG SystemInformationLength = 0;
    ULONG ReturnLength = 0;
 
    //获取需要的内存。
    status = ZwQuerySystemInformation(SystemProcessInformation, pspi, SystemInformationLength, &ReturnLength);
    if( !NT_SUCCESS( status ) && status != STATUS_INFO_LENGTH_MISMATCH)
    {
        KdPrint(("ZwQuerySystemInformation fail with 0x%x in line %d\n",status, __LINE__));
        return status;
    }
    ReturnLength *= 2;//第一次需求0x9700,第二次需求0x9750,所以乘以2.
    SystemInformationLength = ReturnLength;
    pspi = ExAllocatePoolWithTag( NonPagedPool, ReturnLength, tag);
    if (pspi == NULL) {
        KdPrint(("ExAllocatePoolWithTag fail with 0x%x\n",status));
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    RtlZeroMemory(pspi, ReturnLength);  

    status = ZwQuerySystemInformation(SystemProcessInformation, pspi, SystemInformationLength, &ReturnLength);
    if( !NT_SUCCESS( status ) )
    {
        KdPrint(("ZwQuerySystemInformation fail with 0x%x in line %d\n",status, __LINE__));
        ExFreePoolWithTag( pspi, tag );
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    for (pspi_temp = pspi ; /* pspi_temp->NextEntryOffset != 0 */; /*pspi_temp++*/) //注释的都是有问题的,如少显示一个等。
    {
        print_commandLine(pspi_temp->UniqueProcessId);    

        //KdPrint(("PID:%d\tNumberOfThreads:%d\tHandleCount:%d\n",pspi_temp->UniqueProcessId, pspi_temp->NumberOfThreads, pspi_temp->HandleCount));
       
        /*
        The start of the next item in the array is the address of the previous item plus the value in the NextEntryOffset member.
        For the last item in the array, NextEntryOffset is 0.
        摘自:http://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx。
        说明:NextEntryOffset的值是不固定的,更不是SYSTEM_PROCESS_INFORMATION结构的大小。所以不能加一个结构的大小来遍历。
        */

        if (pspi_temp->NextEntryOffset == 0)
        {
            break;
        }

        pspi_temp = (SYSTEM_PROCESS_INFORMATION *)((char *)pspi_temp + pspi_temp->NextEntryOffset);
    }

    ExFreePoolWithTag( pspi, tag );

    return status;//STATUS_SUCCESS
}


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;

    status = PsSetCreateProcessNotifyRoutine(ProcessCreateMon, TRUE);
    if (!NT_SUCCESS( status ))
    {
        DbgPrint( "PsSetCreateProcessNotifyRoutine fail %d\n", status);
    }
}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    BOOLEAN b = 0;

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;  

    status = show_all_process_commandLine();  
    if (!NT_SUCCESS( status ))
    {
        DbgPrint("fail %d\n", status);
        //return status;
    }

    status = PsSetCreateProcessNotifyRoutine(ProcessCreateMon, FALSE);  
    if (!NT_SUCCESS( status ))
    {
        DbgPrint( "PsSetCreateProcessNotifyRoutine fail %d\n", status);
    }

    return status;
}