zoukankan      html  css  js  c++  java
  • 【windows核心编程】远程线程DLL注入

    15.1 DLL注入

    目前公开的DLL注入技巧共有以下几种:

    • 1、注入表注入
    • 2、ComRes注入
    • 3、APC注入
    • 4、消息钩子注入
    • 5、远线程注入
    • 6、依赖可信进程注入
    • 7、劫持进程创建注入
    • 8、输入法注入

    远程线程注入的方式在于使用一个Windows API函数CreateRemoteThread,通过它可以在另外一个进程中注入一个线程并执行。

    实验环境

    操作系统:win10_64位   
    被注入程序:系统自带notepad_32位 
    

    工具整理

    Procexp  :  查看进程模块是否被注入成功的工具
    

    编程思路

    • 1 在目标进程中申请一个空间
    • 2 把dll的路径写入到对方的进程空间中
    • 3 创建一个远程线程,让目标进程调用LoadLibrary
    • 4 释放申请的虚拟内存空间

    使用的API:

    FindWindow

    查找处理窗口的类名和窗口名称匹配指定的字符串

    WINUSERAPI
    HWND
    WINAPI
    FindWindow(
        //参数指向类名
        _In_opt_ LPCSTR lpClassName,
        
        //指向窗口名
        _In_opt_ LPCSTR lpWindowName);
    WINUSERAPI
    
    • lpClassName

    指向一个以NULL字符结尾的、用来指定类名的字符串或一个可以确定类名字符串的原子。如果这个参数是一个原子,那么它必须是一个在调用此函数前已经通过GlobalAddAtom函数创建好的全局原子。这个原子(一个16bit的值),必须被放置在lpClassName的低位字节中,lpClassName的高位字节置零。
    如果该参数为null时,将会寻找任何与lpWindowName参数匹配的窗口。

    • lpWindowName

    指向一个以NULL字符结尾的、用来指定窗口名(即窗口标题)的字符串。如果此参数为NULL,则匹配所有窗口名。

    返回值

    如果函数执行成功,则返回值是拥有指定窗口类名或窗口名的窗口的句柄。

    如果函数执行失败,则返回值为 NULL。可以通过调用GetLastError函数获得更加详细的错误信息。

    GetWindowThreadProcessId

    找出某个进程的PID值。

    WINUSERAPI
    DWORD
    WINAPI
    GetWindowThreadProcessId(
         //窗口的句柄
        _In_ HWND hWnd,
        
         //进程号的存放地址(变量地址) 
        _Out_opt_ LPDWORD lpdwProcessId);
    

    返回值

    返回线程号,注意,lpdwProcessId 是存放进程号的变量。

    DWORD dwPID, dwTID;
    dwTID = GetWindowThreadProcessId( hWnd, &dwPID );
    

    VirtualAllocEx

    可以在其他进程分配或者预定一块虚拟内存

    WINBASEAPI
    _Ret_maybenull_ _Post_writable_byte_size_(dwSize)
    LPVOID
    WINAPI
    VirtualAllocEx(
        //申请内存所在的进程句柄。
        _In_ HANDLE hProcess,
        
        //保留页面的内存地址;一般用NULL自动分配 。
        _In_opt_ LPVOID lpAddress, 
        
        //欲分配的内存大小,字节单位;
        //注意实际分配的内存大小是页内存大小的整数倍
        _In_ SIZE_T dwSize,
        
    
        //指定内存分配的方式,预定还是要提交
        _In_ DWORD flAllocationType,
        
        
        //指定应用程序读写的权限,内存的保护属性
        _In_ DWORD flProtect
        );
    

    flAllocationType 可取下列值:

    • MEM_COMMIT:为特定的页面区域分配内存中或磁盘的页面文件中的物理存储

    • MEM_PHYSICAL :分配物理内存(仅用于地址窗口扩展内存)

    • MEM_RESERVE:保留进程的虚拟地址空间,而不分配任何物理存储。保留页面可通过继续调用VirtualAlloc()而被占用

    • MEM_RESET :指明在内存中由参数lpAddress和dwSize指定的数据无效

    • MEM_TOP_DOWN:在尽可能高的地址上分配内存(Windows 98忽略此标志)

    • MEM_WRITE_WATCH:必须与MEM_RESERVE一起指定,使系统跟踪那些被写入分配区域的页面(仅针对Windows 98)

    flProtect可取下列值:

    • PAGE_READONLY: 该区域为只读。如果应用程序试图访问区域中的页的时候,将会被拒绝访

    • PAGE_READWRITE 区域可被应用程序读写

    • PAGE_EXECUTE: 区域包含可被系统执行的代码。试图读写该区域的操作将被拒绝。

    • PAGE_EXECUTE_READ :区域包含可执行代码,应用程序可以读该区域。

    • PAGE_EXECUTE_READWRITE: 区域包含可执行代码,应用程序可以读写该区域。

    • PAGE_GUARD: 区域第一次被访问时进入一个STATUS_GUARD_PAGE异常,这个标志要和其他保护标志合并使用,表明区域被第一次访问的权限

    • PAGE_NOACCESS: 任何访问该区域的操作将被拒绝

    • PAGE_NOCACHE: RAM中的页映射到该区域时将不会被微处理器缓存(cached)

    注:PAGE_GUARD和PAGE_NOCHACHE标志可以和其他标志合并使用以进一步指定页的特征。PAGE_GUARD标志指定了一个防护页(guard page),即当一个页被提交时会因第一次被访问而产生一个one-shot异常,接着取得指定的访问权限。PAGE_NOCACHE防止当它映射到虚拟页的时候被微处理器缓存。这个标志方便设备驱动使用直接内存访问方式(DMA)来共享内存块。

    返回值:

    执行成功就返回分配内存的首地址,不成功就是NULL。

    WriteProcessMemory

    此函数能写入某一进程的内存区域。

     WINBASEAPI
    _Success_(return != FALSE)
    BOOL
    WINAPI
    WriteProcessMemory(
        //由OpenProcess返回的进程句柄。
        _In_ HANDLE hProcess,
        
        //要写的内存首地址
        _In_ LPVOID lpBaseAddress,
        
        //指向要写的数据的指针。
        _In_reads_bytes_(nSize) LPCVOID lpBuffer,
        
        //要写入的字节数。
        _In_ SIZE_T nSize,
        
        //可选参数,指向变量的指针接收的字节数转移到指定的过程
        _Out_opt_ SIZE_T * lpNumberOfBytesWritten
        );
     
    

    返回值

    非零值代表成功。
    可用GetLastError获取更多的错误详细信息。

    CreateRemoteThread

    可以创建一个在其它进程地址空间中运行的线程。

    WINBASEAPI
    _Ret_maybenull_
    HANDLE
    WINAPI
    CreateRemoteThread(
        //线程所属进程的进程句柄.
        _In_ HANDLE hProcess,
        
        //一个指向 SECURITY_ATTRIBUTES 结构的指针, 该结指定了线程的安全属性.
        _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
        
        //线程初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小.
        _In_ SIZE_T dwStackSize,
        
        //在远程进程的地址空间中,该线程的线程函数的起始地址.
        _In_ LPTHREAD_START_ROUTINE lpStartAddress,
        
        //传给线程函数的参数.
        _In_opt_ LPVOID lpParameter,
        
        线程的创建标志.
        _In_ DWORD dwCreationFlags,
        
        //指向所创建线程ID的指针,如果创建失败,该参数为NULL.
        _Out_opt_ LPDWORD lpThreadId
        );
    

    返回值

    如果调用成功,返回新线程句柄.
    如果失败,返回NULL.

    WaitForSingleObject

    Windows API函数。当等待仍在挂起状态时,句柄被关闭。

    WINBASEAPI
    DWORD
    WINAPI
    WaitForSingleObject(
        _In_ HANDLE hHandle,
        _In_ DWORD dwMilliseconds
        );
    
    • hHandle

    对象句柄。可以指定一系列的对象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。

    • dwMilliseconds

    定时时间间隔,单位为milliseconds(毫秒).如果指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。如果dwMilliseconds为0,对象没有被触发信号,函数不会进入一个等待状态,它总是立即返回。如果dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回。

    返回值

    执行成功,返回值指示出引发函数返回的事件.

    VirtualFreeEx

    VirtualFreeEx即为目标进程的句柄,可在其它进程中释放申请的虚拟内存空间。

    WINBASEAPI
    BOOL
    WINAPI
    VirtualFreeEx(
        //目标进程的句柄。该句柄必须拥有 PROCESS_VM_OPERATION 权限。
        _In_ HANDLE hProcess,
        
        //指向要释放的虚拟内存空间首地址的指针。
        _Pre_notnull_ _When_(dwFreeType == MEM_DECOMMIT, _Post_invalid_) _When_(dwFreeType == MEM_RELEASE, _Post_ptr_invalid_) LPVOID lpAddress,
        
        //虚拟内存空间的字节数。
        _In_ SIZE_T dwSize,
        
        //释放类型
        _In_ DWORD dwFreeType
        );
    
    • dwFreeType

    释放类型,取值见下表:

      • MEM_DECOMMIT

    表示释放后内存空间不可用,但是内存页还存在

      • MEM_RELEASE

    表示内存被释放,内存页完全回收

    远程线程注入源码:

    
    #include "stdafx.h"
    #include <Windows.h>
    #define path _T("E:\09核心编程\_1304~1\win原理下1\Debug\test.dll")
    bool Inject(DWORD dwId,WCHAR* szPath)
    {
    	//1 在目标进程中申请一个空间
    	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwId);
    	LPVOID pRemoteAddress = VirtualAllocEx(
    		hProcess,
    		NULL,
    		1,
    		MEM_COMMIT,
    		PAGE_READWRITE
    		);
    	//2 把dll的路径写入到对方的进程空间中
    	DWORD dwWriteSize = 0;
    	//写一段数据到指定进程所开辟的内存空间
    	WriteProcessMemory(hProcess, pRemoteAddress, szPath, wcslen(szPath) * 2 + 2, &dwWriteSize);
    
    	//3 创建一个远程线程,让目标进程调用LoadLibrary
    	HANDLE hThread = CreateRemoteThread(
    		hProcess,
    		NULL,
    		0,
    		(LPTHREAD_START_ROUTINE)LoadLibrary,
    		pRemoteAddress,
    		NULL,
    		NULL
    		);
    	WaitForSingleObject(hThread, -1);
    	//4 释放申请的虚拟内存空间
    	VirtualFreeEx(hProcess, pRemoteAddress, 1, MEM_DECOMMIT);
    	return 0;
    }
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	DWORD dwId = 0;
    	printf("请输入一个ID:");
    	HWND hCalc = FindWindow(NULL, L"FileCleaner2.0");
    	DWORD dwPid = 0;
    	DWORD dwRub = GetWindowThreadProcessId(hCalc, &dwPid);
    
        //选择自己输出PID或者自动获取
    	//	scanf_s("%d", &dwId);
    	Inject(dwPid, path);
    	return 0;
    }
    

    DLL源码

    #include "stdafx.h"
    
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
    					 )
    {
    	switch (ul_reason_for_call)
    	{
    	case DLL_PROCESS_ATTACH:
    	{		
    		MessageBox(NULL, L"呵呵", L"成功了,ye", NULL);
    
    	}
    		break;
    	case DLL_THREAD_ATTACH:
    	case DLL_THREAD_DETACH:
    	case DLL_PROCESS_DETACH:
    		break;
    	}
    	return TRUE;
    }
    
    

    注入成果效果图:

    image

    注入后的DLL查看:

    image

  • 相关阅读:
    所谓的日常 #8
    所谓的日常 #7
    所谓的日常 #6
    所谓的日常 #5
    所谓的日常 #4
    所谓的日常 #3
    DFS序+线段树 hihoCoder 1381 Little Y's Tree(树的连通块的直径和)
    Codeforces Round #366 (Div. 2)
    2016 Multi-University Training Contests
    DP套DP HDOJ 4899 Hero meet devil(国王的子民的DNA)
  • 原文地址:https://www.cnblogs.com/17bdw/p/6527998.html
Copyright © 2011-2022 走看看