修改GetCommandLine()的命令行参数

时间:2014-07-15 09:42:43

标签: c++ windows winapi

我正在尝试修改可执行文件的命令行参数,以便GetCommandLine()将返回我设置的字符串。由于我想在任何人之前修改命令行值,我已经通过/ ENTRY开关将我的入口点更改为testme()函数,并且还设置了/ NODEFAULTLIB选项以排除CRT。使用以下代码,为什么我可以通过CommandLine更改字符串缓冲区指针但不能分配全新的缓冲区?

代码:

#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;
}

2 个答案:

答案 0 :(得分:4)

不幸的是GetCommandLineW没有从PEB返回命令行。在BaseDllInitialize例程中,复制由PEB命令行结构组成,从那时起GetCommandLineW使用此副本。您需要在内存中找到此副本才能对其进行修改,这似乎非常困难且危险/不可靠。

您可以查看像Detours这样的API挂钩,但更简单的解决方案可能就是首先使用您想要的命令行启动您的可执行文件。如果命令行是正确的,你可以测试它何时启动,如果没有,它可以使用所需的命令行生成另一个自身副本。

答案 1 :(得分:2)

经过一些试验和错误后,我想出了以下内容。我写了一个C可执行文件,只与kernel32.lib链接,但没有链接CRT。在exe中,我EAT修补了GetCommandLineX中的kernel32.dll个功能。然后我加载另一个我的DLL(test.dll),它需要GetCommandLineX方法作为其功能的一部分。由于kernel32被修补,因此加载器使用修补的函数指针填充test.dll的导入表。最后,test.dll中的方法调用了我的GetCommandLineX版本,我可以轻松地更改它们的实现。