修改GetCommandLine()的命令行参数

Modifying command line arguments for GetCommandLine()

本文关键字:命令行 参数 GetCommandLine 修改      更新时间:2023-10-16

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

代码:

#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'';
  pbi.PebBaseAddress->ProcessParameters->CommandLine.Length = 6;
  // This does not work  
  UNICODE_STRING cmdLine;
  wchar_t wszNewCmdLine[] = L"x y";
  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'');
  //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;
}

不幸的是,GetCommandLineW没有从PEB返回命令行。在BaseDllInitialize例程中,生成PEB命令行结构的副本,从那时起,该副本被GetCommandLineW使用。为了修改它,你需要在内存中找到这个副本,这看起来很困难,也很危险/不可靠。

您可以查看类似Detours的API钩子,但更简单的解决方案可能是首先使用所需的命令行启动可执行文件。您可以在它启动时测试命令行是否正确,如果不正确,则使用所需的命令行生成另一个副本。

经过一些尝试和错误后,我得出了以下结论。我写了一个C可执行文件,它只链接kernel32.lib而不链接CRT。在exe中,我在kernel32.dll中对GetCommandLineX功能进行EAT补丁。然后我加载我的另一个dll (test.dll),它需要GetCommandLineX方法作为其功能的一部分。由于kernel32被打了补丁,所以loader用打了补丁的函数指针填充了test.dll的导入表。最后,test.dll中的方法调用我的GetCommandLineX版本,我可以很容易地改变它们的实现。