獲取,修改進程命令行


在XDebug的源碼學習中看到的命令行操作,將主要源碼摘錄出來做了個獲取和更改進程命令行的Demo.

0x01  獲取命令行

     這里獲取命令行的方式並不是通過調用GetCommandLine函數。

   而是借由ntdll中的NtQueryInformationProcess函數查詢ProcessBasicInformation,得到ProcessBasicInformation中的PebBaseAddress字段。得到進程PEB地址后,再通過PEB地址的獲取ProcessParameter字段地址,

     最后通過ProcessParameter字段獲取到ProcessParameter的 CommandLine  字段,得到了命令行的存儲地址。

主要源碼:

void* GetPEBLocation(HANDLE hProcess)
{
	ULONG RequiredLen = 0;
	void* PebAddress = 0;
	PROCESS_BASIC_INFORMATION myProcessBasicInformation[5] = { 0 };

	if (NtQueryInformationProcess(hProcess, ProcessBasicInformation, myProcessBasicInformation, sizeof(PROCESS_BASIC_INFORMATION), &RequiredLen) == STATUS_SUCCESS)
	{
		PebAddress = (void*)myProcessBasicInformation->PebBaseAddress;
	}
	else
	{
		if (NtQueryInformationProcess(hProcess, ProcessBasicInformation, myProcessBasicInformation, RequiredLen, &RequiredLen) == STATUS_SUCCESS)
		{
			PebAddress = (void*)myProcessBasicInformation->PebBaseAddress;
		}
	}

	return PebAddress;
}


BOOL Getcommandlineaddr(duint *CommandLineAddressdr)
{
	duint PEBAddress;
	duint pprocess_parameters;
	duint ProcessParametersAddress;
	duint ReturnLength;
	PEBAddress = (duint)GetPEBLocation(__ProcessHandle);
	ProcessParametersAddress = (duint) & (((PPEB)PEBAddress)->ProcessParameters);



	ReadProcessMemory(__ProcessHandle, reinterpret_cast<LPVOID>(ProcessParametersAddress), &pprocess_parameters,
		sizeof(pprocess_parameters), reinterpret_cast<SIZE_T*>(&ReturnLength));

	*CommandLineAddressdr = (duint) & (((RTL_USER_PROCESS_PARAMETERS*)pprocess_parameters)->CommandLine);

	return TRUE;
}

  

 

0x02  修改命令行

    修改命令行的關鍵就是操作內存。

    XDebug首先獲取GetCommandLineA或GetCommandLineW函數的地址。

    

if (!valfromstring("kernelBase:GetCommandLineA", &getcommandline))
	{
		if (!valfromstring("kernel32:GetCommandLineA", &getcommandline))
		{
			
			return FALSE;
		}
	}


bool valfromstring(const char* string, duint* value, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly, bool allowassign)
{
	duint result;
	if (!Calculate(string, result, false /*valuesignedcalc()*/, allowassign, silent, baseonly, value_size, isvar, hexonly))
		return false;
	*value = result;
	return true;
}

bool Calculate(const char* string, duint & value, bool signedcalc, bool allowassign, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly)
{
	
	return DoEvaluate(string, value, silent, baseonly, value_size, isvar, hexonly);
}
bool DoEvaluate(const char* string, duint & result, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly)
{
	return valfromstring_noexpr(string, (duint*)&result, silent, baseonly, value_size, isvar, hexonly);
}
bool valfromstring_noexpr(const char* string, duint* value, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly)
{
	if (!value || !string || !*string)
		return false;

	if (valapifromstring(string, value, value_size, true, silent, hexonly)) //then come APIs
		return true;
	return false;
}
bool valapifromstring(const char* name, duint* value, int* value_size, bool printall, bool silent, bool* hexonly)
{
	//explicit API handling
	const char* apiname = strchr(name, ':'); //the ':' character cannot be in a path: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#naming_conventions
	bool noexports = false;
	if (!apiname) //not found
	{
		apiname = strstr(name, "..") ? strchr(name, '.') : strrchr(name, '.'); //kernel32.GetProcAddress support
		if (!apiname) //not found
		{
			apiname = strchr(name, '?'); //the '?' character cannot be in a path either
			noexports = true;
		}
	}
	if (apiname)
	{
		char modname[MAX_MODULE_SIZE] = "";
		strncpy_s(modname, name, _TRUNCATE);
		modname[apiname - name] = 0;
		apiname++;
		if (!strlen(apiname))
			return false;
		duint modbase = 0;//ModBaseFromName(modname);從MAP表里查基地址
		char szModPath[MAX_PATH] = "kernel32.dll";
		if (!1/*ModPathFromAddr(modbase, szModPath, _countof(szModPath))*/)//查表得到完整路徑
		{
			//if (!silent)
				//dprintf(QT_TRANSLATE_NOOP("DBG", "Could not get filename of module %p\n"), modbase);
		}
		else
		{
			HMODULE mod = LoadLibraryExW(Utf8ToUtf16(szModPath).c_str(), 0, DONT_RESOLVE_DLL_REFERENCES);
			if (!mod)
			{
				//if (!silent)
				//	dprintf(QT_TRANSLATE_NOOP("DBG", "Unable to load library %s\n"), szModPath);
			}
			else
			{
				duint addr = noexports ? 0 : SafeGetProcAddress(mod, apiname);
				//if (addr) //found exported function
				//	addr = modbase + (addr - (duint)mod); //correct for loaded base
				//else //not found
				{
				}
				FreeLibrary(mod);
				if (addr) //found!
				{
					if (value_size)
						*value_size = sizeof(duint);
					if (hexonly)
						*hexonly = true;
					*value = addr;
					return true;
				}
			}
		}
		return false;
	}
	return true;
}

  獲取到這個所謂的“函數地址后並沒有結束,這里需要設計到重定向表的重定向問題,在這個地址的基礎上還需要兩次跳轉才能得到真正的功能函數地址。這個地址上的第一次跳轉,是跳向重定向表,然后重定向表再給出地址,跳向庫中真正的功能函數地址。

  對應的源碼操作(圖中getcommandline即為前文valfromstring函數獲取的GetCommandLineA或GetCommandLineW函數的地址。)

 

 

調試反匯編查看獲取到的getcommandline地址:

 

越過FF 25兩字節,獲取到跳轉地址76b311fch:

ReadProcessMemory(__ProcessHandle, reinterpret_cast<LPVOID>(getcommandline + 2), data, 100, reinterpret_cast<SIZE_T*>(&ReturnLength));

  

 

 

內存窗口查看76b311fch:處內容,得到重定向表給出的功能函數地址:0x7406e210

 

 

 

    ReadProcessMemory(__ProcessHandle, reinterpret_cast<LPVOID>(*(int*)data), data, 100, reinterpret_cast<SIZE_T*>(&ReturnLength));

  

 

反匯編窗口中查看0x7406e210中的內容:

 

 

現在就可以通過字符串匹配來得到命令行的存儲地址:

 

/*

    750FE9CA | A1 CC DB 1A 75           | mov eax,dword ptr ds:[751ADBCC]         |

    750FE9CF | C3                       | ret                                     |

    */

    if (data[0] != 0xA1 || data[5] != 0xC3)

    {

         return FALSE;

    }

    command_line_stored = *((duint*)& data[1]);

  

找到地址后最后一步寫入地址:

WriteProcessMemory(__ProcessHandle, reinterpret_cast<LPVOID>(command_line_stored), &new_command_line, sizeof(new_command_line), reinterpret_cast<SIZE_T*>(&ReturnLength));

    //update the pointer in the debuggee

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM