zoukankan      html  css  js  c++  java
  • 获取,修改进程命令行

    在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
    "), 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
    "), 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
    

      

  • 相关阅读:
    AMF序列化技巧
    为什么用ByteArray序列化的对象如此小?
    解决Asp.net中翻页问题的自定义用户控件
    新建对象:反射会调用构造函数,clone不会调用构造函数
    Java 的传值小例子
    JDK中设计模式
    tryfinally中的返回值
    c++类中的常量(注意)
    创建有个性的对话框之MFC篇(转)
    用VC在IE浏览器的工具条上添加命令按钮(转 可以借鉴)
  • 原文地址:https://www.cnblogs.com/lsh123/p/8320562.html
Copyright © 2011-2022 走看看