2009 01 15 23 50 [winddk] 如何寫 NT native application (4)

再來就是一般程式會需要得到的 command line 裡的參數。

而再之前有提到 NtProcessStartup 的參數是 struct PEB ,

而你可以從微軟網站得到這個struct的定義,如下:

typedef struct _RTL_USER_PROCESS_PARAMETERS {
UCHAR Reserved1[16];
PVOID Reserved2[10];
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS,
*PRTL_USER_PROCESS_PARAMETERS;

typedef struct _PEB_LDR_DATA {
UCHAR Reserved1[8];
PVOID Reserved2[3];
LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

typedef PVOID PPS_POST_PROCESS_INIT_ROUTINE;

struct _PEB {
UCHAR Reserved1[2];
UCHAR BeingDebugged;
UCHAR Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
UCHAR Reserved4[104];
PVOID Reserved5[52];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
UCHAR Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
};

但因為,MSDN網站並沒有 PPS_POST_PROCESS_INIT_ROUTINE 的資料,

而從名稱看起來應該是個指標,因此,就把它定義成 PVOID 。

到這裡就可以看到 CommandLine 這個 element 。

你就可以從 pPEB->ProcessParameters->CommandLine 拿到。

但是距離一般的 main function 會有的 int argc, char *argv[] 的用法,

還有一段距離。

我自己就寫了一個小 function 來處理,如下:

int parseArgs(WCHAR * pArgs, int nArgsLen, int *pArgc, WCHAR *** pArgv)
{
int argc = 0;
int i;
int nArgv = 1;

*pArgc = 0;
*pArgv = NULL;
if (pArgs == NULL) {
return 0;
}
(*pArgv) = (WCHAR **) malloc(sizeof(WCHAR *) * nArgv);
if (pArgv == NULL) {
return -1;
}
memset((*pArgv), 0, sizeof(WCHAR *) * nArgv);

for (i = 0; i < nArgsLen; i++) {
while (pArgs[i] == L' ') {
if ((i + 1) >= nArgsLen) {
break;
}
i++;
}
if (i >= nArgsLen)
break;

(*pArgv)[argc] = (pArgs + i);
argc++;
if (argc >= nArgv) {
WCHAR **pTmpArgv;
pTmpArgv = (WCHAR **) malloc(sizeof(WCHAR *) * (nArgv + 16));
if (pTmpArgv == NULL) {
free(*pArgv);
*pArgv = NULL;
return -1;
}
memset(pTmpArgv, 0, sizeof(WCHAR *) * (nArgv + 16));
memcpy(pTmpArgv, (*pArgv), argc * sizeof(WCHAR *));
free(*pArgv);
*pArgv = pTmpArgv;
nArgv = nArgv + 16;
}

if (pArgs[i] == L'\"') {
(*pArgv)[argc - 1] = (pArgs + i + 1);
do {
i++;
if ((i + 1) < nArgsLen && pArgs[i] == L'\\') {
i += 2;
}
} while (i < nArgsLen && pArgs[i] != L'\"');
if (i < nArgsLen && pArgs[i] == L'\"') {
pArgs[i] = 0;
}
} else {
do {
i++;
} while (i < nArgsLen && pArgs[i] != L' ');
if (i < nArgsLen && pArgs[i] == L' ') {
pArgs[i] = 0;
}
}
}
*pArgc = argc;
return 0;
}

第一個參數是傳進原始沒有處理過的 Commandline ,

因為之後會修改裡面的資料,

所以,並不能原來的 CommandLine 的資料,

而是另外拷貝一份出來。

第二個參數則是 CommandLine 的個數。

第三個參數是處理過後的參數個數,也就是int argc。

第四個參數是個 wchar * 的指標矩陣,也就是 WCHAR *argv[]。

 

以下就是完整的原始碼:

#include "ntifs.h"
#include "ntddk.h"
#include "stdio.h"

NTSTATUS NTAPI NtDisplayString(PUNICODE_STRING String);
NTSTATUS NTAPI NtTerminateProcess(HANDLE ProcessHandle, LONG ExitStatus);

HANDLE g_hHeap = NULL;

HANDLE InitHeapMemory(void)
{
g_hHeap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0x100000, 0x1000, NULL,NULL);

return g_hHeap;
}

BOOLEAN DeinitHeapMemory()
{
PVOID pRet;

pRet = RtlDestroyHeap(g_hHeap);
if (pRet == NULL) {
g_hHeap = NULL;
return TRUE;
}
return FALSE;
}

void free(void *pMem)
{
RtlFreeHeap(g_hHeap, 0, pMem);
}


void *malloc(unsigned long ulSize)
{
return RtlAllocateHeap(g_hHeap, 0, ulSize);
}

typedef struct _RTL_USER_PROCESS_PARAMETERS {
UCHAR Reserved1[16];
PVOID Reserved2[10];
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS,
*PRTL_USER_PROCESS_PARAMETERS;

typedef struct _PEB_LDR_DATA {
UCHAR Reserved1[8];
PVOID Reserved2[3];
LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

typedef PVOID PPS_POST_PROCESS_INIT_ROUTINE;

struct _PEB {
UCHAR Reserved1[2];
UCHAR BeingDebugged;
UCHAR Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
UCHAR Reserved4[104];
PVOID Reserved5[52];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
UCHAR Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
};

void print(PWCHAR pwmsg)
{
UNICODE_STRING msg;
RtlInitUnicodeString(&msg,pwmsg);
NtDisplayString(&msg);
}

int parseArgs(WCHAR * pArgs, int nArgsLen, int *pArgc, WCHAR *** pArgv)
{
int argc = 0;
int i;
int nArgv = 1;

*pArgc = 0;
*pArgv = NULL;
if (pArgs == NULL) {
return 0;
}
(*pArgv) = (WCHAR **) malloc(sizeof(WCHAR *) * nArgv);
if (pArgv == NULL) {
return -1;
}
memset((*pArgv), 0, sizeof(WCHAR *) * nArgv);

for (i = 0; i < nArgsLen; i++) {
while (pArgs[i] == L' ') {
if ((i + 1) >= nArgsLen) {
break;
}
i++;
}
if (i >= nArgsLen)
break;

(*pArgv)[argc] = (pArgs + i);
argc++;
if (argc >= nArgv) {
WCHAR **pTmpArgv;
pTmpArgv = (WCHAR **) malloc(sizeof(WCHAR *) * (nArgv + 16));
if (pTmpArgv == NULL) {
free(*pArgv);
*pArgv = NULL;
return -1;
}
memset(pTmpArgv, 0, sizeof(WCHAR *) * (nArgv + 16));
memcpy(pTmpArgv, (*pArgv), argc * sizeof(WCHAR *));
free(*pArgv);
*pArgv = pTmpArgv;
nArgv = nArgv + 16;
}

if (pArgs[i] == L'\"') {
(*pArgv)[argc - 1] = (pArgs + i + 1);
do {
i++;
if ((i + 1) < nArgsLen && pArgs[i] == L'\\') {
i += 2;
}
} while (i < nArgsLen && pArgs[i] != L'\"');
if (i < nArgsLen && pArgs[i] == L'\"') {
pArgs[i] = 0;
}
} else {
do {
i++;
} while (i < nArgsLen && pArgs[i] != L' ');
if (i < nArgsLen && pArgs[i] == L' ') {
pArgs[i] = 0;
}
}
}
*pArgc = argc;
return 0;
}

int UserMain(int argc, WCHAR * argv[])
{
int i;
WCHAR buf[64];

for (i = 0; i < argc; i++) {
_snwprintf((WCHAR *)buf, 64, L"%d : ",i);
print(buf);
print(argv[i]);
print(L"\n");
}
return i;
}

void NtProcessStartup(PPEB pPEB)
{
int rc = 0;
WCHAR *pArgs = NULL;
int argc;
WCHAR **argv;

if (InitHeapMemory() == NULL) {
DbgPrint("%s:%d InitHeapMemory failed\n",__FILE__,__LINE__);
}

print(L"ImagePathName:");
NtDisplayString(&pPEB->ProcessParameters->ImagePathName);
print(L"\n");

print(L"CommandLine:");
NtDisplayString(&pPEB->ProcessParameters->CommandLine);
print(L"\n\n");

pArgs = (WCHAR *) malloc(pPEB->ProcessParameters->CommandLine.Length + 2);
if (pArgs != NULL) {
memset(pArgs, 0, pPEB->ProcessParameters->CommandLine.Length + 2);
memcpy(pArgs,
pPEB->ProcessParameters->CommandLine.Buffer,
pPEB->ProcessParameters->CommandLine.Length);
}

rc = parseArgs(pArgs,
  pPEB->ProcessParameters->CommandLine.
Length / 2, &argc, &argv);

if (rc != 0) {
print(L"Parsing Args failed.\n");
}

rc = UserMain(argc, argv);
if (argv) {
free(argv);
}
if (pArgs) {
free(pArgs);
}
DeinitHeapMemory();
NtTerminateProcess( NtCurrentProcess(), rc );
}

把編譯出來的程式還是放進 c:\windows\system32 下,

並使用以下指令修改 registry,

reg add "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager" /v BootExecute /t REG_MULTI_SZ /d "autocheck autochk *\0native arg1 arg2 arg3" /f

上面的 CommandLine ,

第一個參數依舊是所要執行的檔案名稱是 native ,

之後的參數依序是:arg1 arg2 arg3 。

在執行之後就可以看到,你就可以看到如圖片所顯示的結果。