Reputation: 193
I am trying to modify command line arguments of my executable so that GetCommandLine() will return the string I set. Since I want to modify the command line value before anyone, I have changed my entry point to testme() function via /ENTRY switch and also set /NODEFAULTLIB option in order to exclude CRT. Using the following code why can I change the string buffer pointer by CommandLine but cannot allocate a completely new buffer?
The code:
#include <Windows.h>
#include <winternl.h>
typedef NTSTATUS (WINAPI *PFN_NtQueryInformationProcess)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
IN PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength );
int testme()
{
// Get PEB block address
PROCESS_BASIC_INFORMATION pbi;
ULONG result;
PFN_NtQueryInformationProcess pfnQueryProcess =
(PFN_NtQueryInformationProcess) GetProcAddress(LoadLibrary("ntdll"),
"NtQueryInformationProcess");
pfnQueryProcess(GetCurrentProcessId(),
ProcessBasicInformation, &pbi, sizeof(pbi), &result);
// Modify ProcessParameters->CommandLine
// This works
pbi.PebBaseAddress->ProcessParameters->CommandLine.Buffer[0] = L'a';
pbi.PebBaseAddress->ProcessParameters->CommandLine.Buffer[1] = L' ';
pbi.PebBaseAddress->ProcessParameters->CommandLine.Buffer[2] = L'b';
pbi.PebBaseAddress->ProcessParameters->CommandLine.Buffer[3] = L'\0';
pbi.PebBaseAddress->ProcessParameters->CommandLine.Length = 6;
// This does not work
UNICODE_STRING cmdLine;
wchar_t wszNewCmdLine[] = L"x y\0";
cmdLine.Buffer = (wchar_t*)GlobalAlloc(GMEM_FIXED, sizeof(wchar_t)*pbi.PebBaseAddress->ProcessParameters->CommandLine.MaximumLength);
cmdLine.MaximumLength = pbi.PebBaseAddress->ProcessParameters->CommandLine.MaximumLength;
cmdLine.Length = sizeof(wszNewCmdLine) - sizeof(L'\0');
//Copy buffer
for(int i=0; i<cmdLine.Length; ++i)
cmdLine.Buffer[i] = wszNewCmdLine[i];
pbi.PebBaseAddress->ProcessParameters->CommandLine.Buffer = cmdLine.Buffer;
pbi.PebBaseAddress->ProcessParameters->CommandLine.Length = cmdLine.Length;
pbi.PebBaseAddress->ProcessParameters->CommandLine.MaximumLength = cmdLine.MaximumLength;
// Now testing, pCmdLine returned is "a b", not "x y".
wchar_t *pCmdLine = GetCommandLine();
return 0;
}
Upvotes: 2
Views: 3334
Reputation: 193
After a bit of trial and error I come up with following. I wrote a C executable that only links with kernel32.lib
and does not link CRT
. In the exe
, I do EAT
patching of GetCommandLineX
functions in kernel32.dll
. Then I load another of my dll (test.dll) which needs GetCommandLineX
method as part of its functions. Since kernel32
is patched, loader fills the import table of the test.dll with the patched function pointers. In the end, methods in the test.dll calls my version of GetCommandLineX, which I can easily change their implementation.
Upvotes: 2
Reputation: 37192
Unfortunately GetCommandLineW
doesn't return the command line from the PEB. In the BaseDllInitialize
routine a copy is made of the PEB command line structure, and from then on this copy is used by GetCommandLineW
. You would need to locate this copy in memory in order to modify it, which seems quite difficult and also dangerous/unreliable.
You could look at an API hook like Detours, but an easier solution might just be to launch your executable with your desired command line in the first place. You could test when it starts up if the command line is right and if not have it spawn another copy of itself with the desired command line.
Upvotes: 4