首页
归档
友情链接
关于
Search
1
在wsl2中安装archlinux
80 阅读
2
nvim番外之将配置的插件管理器更新为lazy
58 阅读
3
2018总结与2019规划
54 阅读
4
PDF标准详解(五)——图形状态
33 阅读
5
为 MariaDB 配置远程访问权限
30 阅读
心灵鸡汤
软件与环境配置
博客搭建
从0开始配置vim
Vim 从嫌弃到依赖
archlinux
Emacs
MySQL
Git与Github
AndroidStudio
cmake
读书笔记
菜谱
编程
PDF 标准
从0自制解释器
qt
C/C++语言
Windows 编程
Python
Java
算法与数据结构
PE结构
登录
Search
标签搜索
c++
c
学习笔记
windows
文本操作术
编辑器
NeoVim
Vim
win32
VimScript
Java
emacs
linux
文本编辑器
elisp
反汇编
OLEDB
数据库编程
数据结构
内核编程
Masimaro
累计撰写
308
篇文章
累计收到
27
条评论
首页
栏目
心灵鸡汤
软件与环境配置
博客搭建
从0开始配置vim
Vim 从嫌弃到依赖
archlinux
Emacs
MySQL
Git与Github
AndroidStudio
cmake
读书笔记
菜谱
编程
PDF 标准
从0自制解释器
qt
C/C++语言
Windows 编程
Python
Java
算法与数据结构
PE结构
页面
归档
友情链接
关于
搜索到
1
篇与
的结果
2018-09-09
Windows下的代码注入
木马和病毒的好坏很大程度上取决于它的隐蔽性,木马和病毒本质上也是在执行程序代码,如果采用独立进程的方式需要考虑隐藏进程否则很容易被发现,在编写这类程序的时候可以考虑将代码注入到其他进程中,借用其他进程的环境和资源来执行代码。远程注入技术不仅被木马和病毒广泛使用,防病毒软件和软件调试中也有很大的用途,最近也简单的研究过这些东西,在这里将它发布出来。想要将代码注入到其他进程并能成功执行需要解决两个问题:第一个问题是如何让远程进程执行注入的代码。原始进程有它自己的执行逻辑,想要破坏原来的执行流程,使EIP寄存器跳转到注入的代码位置基本是不可能的第二个问题是每个进程中地址空间是独立的,比如在调用某个句柄时,即使是同一个内核对象,在不同进程中对应的句柄也是不同的,这就需要进行地址转化。要进行远程代码注入的要点和难点主要就是这两个问题,下面给出两种不同的注入方式来说明如何解决这两个问题DLL注入DLL注入很好的解决了第二个问题,DLL被加载到目标进程之后,它里面的代码中的地址就会自动被转化为对应进程中的地址,这个特性是由于DLL加载的过程决定的,它会自己使用它所在进程中的资源和地址空间,所以只要DLL中不存在硬编码的地址,基本不用担心里面会出现函数或者句柄需要进行地址转化的问题。那么第一个问题改怎么解决呢?要执行用户代码,在Windows中最常见的就是使用回调的方式,Windows采用的是事件驱动的方式,只要发生了某些事件就会调用回调,在众多使用回调的场景中,线程的回调是最简单的,它不会干扰到目标进程的正常执行,也就不用考虑最后还原EIP的问题,因此DLL注入采用的最常见的就是创建一个远程线程,让线程加载DLL代码。DLL注入中一般的思路是:使用CreateRemoteThread来在目标进程中创建一个远程的线程,这个线程主要是加载DLL到目标进程中,由于DLL在入口函数(DLLMain)中会处理进程加载Dll的事件,所以将注入代码写到这个事件中,这样就能执行注入的代码了。那么如何在远程进程中执行DLL的加载操作呢?我们知道加载DLL主要使用的是函数LoadLibrary,仔细分析线程的回调函数和LoadLibrary函数的原型,会发现,它们同样都是传入一个参数,而CreateRemoteThread函数正好需要一个函数的地址作为回调,并且传入一个参数作为回调函数的参数。这样就有思路了,我们让LoadLibrary作为线程的回调函数,将对应dll的文件名和路径作为参数传入,这样就可以在对应进程中加载dll了,进一步也就可以执行dllmain中的对应代码了。还有一个很重要的问题,我们知道不同进程中,地址空间是隔离的,那么我在注入的进程中传入LoadLibrary函数的地址,这算是一个硬编码的地址,它在目标进程中是否是一样的呢?答案是,二者的地址是一样的,这是由于kernel32.dll在32位程序中加载的基地址是一样的,而LoadLibrary在kernel32.dll中的偏移是一定的(只要不同的进程加载的是同一份kernel32.dll)那么不同进程中的LoadLibrary函数的地址是一样的。其实不光是LoadLibrary函数,只要是kernel32.dll中导出的函数,在不同进程中的地址都是一样的。注意这里只是32位,如果想要使用32位程序往64位目标程序中注入,可能需要考虑地址转换的问题,只要知道kernel32.dll在64位中的偏移,就可以计算出对应函数的地址了。LoabLibrary函数传入的代表路径的字符串的首地址在不同进程中同样是不同的,而且也没办法利用偏移来计算,这个时候解决的办法就是在远程进程中申请一块虚拟地址空间,并将目标字符串写入对应的地址中,然后将对应的首地址作为参数传入即可。最后总结一下DLL注入的步骤:获取LoadLibrary函数的地址调用VirtualAllocEx 函数在远程进程中申请一段虚拟内存调用WriteProcessMemory 函数将参数写入对应的虚拟内存调用CreateRemoteThread 函数创建远程线程,线程的回调函数为LoadLibrary,参数为对应的字符串的地址按照这个思路可以编写如下的代码:typedef HMODULE(WINAPI *pfnLoadLibrary)(LPCWSTR); if (!DebugPrivilege()) //提权代码,在Windows Vista 及以上的版本需要将进程的权限提升,否则打开进程会失败 { return FALSE; } //打开目标进程 HANDLE hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid); //dwPid是对应的进程ID if (NULL == hRemoteProcess) { AfxMessageBox(_T("OpenProcess Error")); } //查找LoadLibrary函数地址 pfnLoadLibrary lpLoadLibrary = (pfnLoadLibrary)GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "LoadLibraryW"); //在远程进程中申请一块内存用于保存对应线程的参数 PVOID pBuffer = VirtualAllocEx(hRemoteProcess, NULL, MAX_PATH, MEM_COMMIT, PAGE_READWRITE); //在对应内存位置处写入参数值 DWORD dwWritten = 0; WriteProcessMemory(hRemoteProcess, pBuffer, m_csDLLName.GetString(), (m_csDLLName.GetLength() + 1) * sizeof(TCHAR), &dwWritten); //创建远程线程并传入对应参数 HANDLE hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpLoadLibrary, pBuffer, 0, NULL); WaitForSingleObject(hRemoteThread, INFINITE); VirtualFreeEx(hRemoteProcess, pBuffer, 0, MEM_RELEASE); CloseHandle(hRemoteThread); CloseHandle(hRemoteProcess);卸载远程DLL上面进行了代码的注入,作为一个文明的程序,自然得考虑卸载dll,毕竟现在提倡环保,谁使用,谁治理。这里既然注入了,自然得考虑卸载。卸载的思路与注入的类似,只是函数变为了FreeLibrary,传入的参数变成了对应的dll的句柄了。如何获取这个模块的句柄呢?我们可以枚举进程中的模块,根据模块的名称来找到对应的模块并获取它的句柄。枚举的方式一般是使用toolhelp32中对应的函数,下面是卸载的例子代码HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, m_dwPid); if (INVALID_HANDLE_VALUE == hSnapshot) { AfxMessageBox(_T("CreateToolhelp32Snapshot Error")); return; } MODULEENTRY32 me = {0}; me.dwSize = sizeof(MODULEENTRY32); BOOL bRet = Module32First(hSnapshot, &me); while (bRet) { CString csModuleFile = _tcsupr(me.szExePath); if (csModuleFile == _tcsupr((LPTSTR)m_csDLLName.GetString()) != -1) { break; } ZeroMemory(&me, sizeof(me)); me.dwSize = sizeof(PROCESSENTRY32); bRet = Module32Next(hSnapshot, &me); } CloseHandle(hSnapshot); typedef BOOL (*pfnFreeLibrary)(HMODULE); pfnFreeLibrary FreeLibrary = (pfnFreeLibrary)GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "FreeLibrary"); HANDLE hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_dwPid); if (hRemoteProcess == NULL) { AfxMessageBox(_T("OpenProcess Error")); return; } HANDLE hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, (LPTHREAD_START_ROUTINE)FreeLibrary, me.modBaseAddr, 0, NULL); WaitForSingleObject(hRemoteThread, INFINITE); CloseHandle(hRemoteThread); CloseHandle(hRemoteProcess);无DLL的注入注入不一定需要使用DLL,虽然使用DLL比较简单一点,无DLL注入在解决上述两个问题的第一个思路是一样的,也是使用CreateRemoteThread来创建一个远程线程来执行目标代码。无dll的注入主要麻烦是在进行地址转化上,在调用API的时候,如果无法保证对应的dll的基地址不变的话,就得在目标进程中自行调用LoadLibrary来动态获取函数地址,并调用。在动态获取API函数的地址的时候,主要使用的函数是LoadLibrary、GetModuleHandle、GetProcAddress这三个函数,而线程的回调函数只能传入一个参数,所以我们需要将对应的需要传入的参数组成一个结构体,并将结构体对应的数据写入到目标进程的内存中,特别要注意的是,里面不要使用指针或者句柄这种与地址有关的东西。例如我们想在目标进程中注入一段代码,让它弹出一个对话框,以便测试是否注入成功。这种情况除了要传入上述三个函数的地址外,还需要MesageBox,而MessageBox是在user32.dll中,user32.dll在每个进程中的基地址并不相同,因此在注入的代码中需要动态加载,因此可以定义下面一个结构typedef struct REMOTE_DATA { DWORD dwLoadLibrary; DWORD dwGetProcAddress; DWORD dwGetModuleHandle; DWORD dwGetModuelFileName; //辅助函数 char szUser32dll[MAX_PATH]; //存储user32dll的路径,以便调用LoadLibrary加载 char szMessageBox[128]; //存储字符串MessageBoxA 这个字符串,以便使用GetProcAddress加载MesageBox函数 char szMessage[512]; //弹出对话框上显示的字符 }不使用DLL注入与使用DLL注入的另一个区别是,不使用DLL注入的时候需要自己加载目标代码到对应的进程中,这个操作可以借由WriteProcessMemory 将函数代码写到对应的虚拟内存中。最后注入的代码主要如下:DWORD WINAPI RemoteThreadProc(LPVOID lpParam) { LPREMOTE_DATA lpData = (LPREMOTE_DATA)lpParam; typedef HMODULE (WINAPI *pfnLoadLibrary)(LPCSTR); typedef FARPROC (WINAPI *pfnGetProcAddress)(HMODULE, LPCSTR); typedef HMODULE (*pfnGetModuleHandle)(LPCSTR); typedef DWORD (WINAPI *pfnGetModuleFileName)( HMODULE,LPSTR, DWORD); pfnGetModuleHandle MyGetModuleHandle = (pfnGetModuleHandle)lpData->dwGetModuleHandle; pfnGetModuleFileName MyGetModuleFileName = (pfnGetModuleFileName)lpData->dwGetModuleFileName; pfnGetProcAddress MyGetProcAddress = (pfnGetProcAddress)lpData->dwGetProcAddress; pfnLoadLibrary MyLoadLibrary = (pfnLoadLibrary)lpData->dwGetProcAddress; typedef int (WINAPI *pfnMessageBox)(HWND, LPCSTR, LPCSTR, UINT); //加载User32.dll HMODULE hUser32Dll = MyLoadLibrary(lpData->szUerDll); //加载MessageBox函数 pfnMessageBox MyMessageBox = (pfnMessageBox)MyGetProcAddress(hUser32Dll, lpData->szMessageBox); char szTitlte[MAX_PATH] = ""; MyGetModuleFileName(NULL, szTitlte, MAX_PATH); MyMessageBox(NULL, lpData->szMessage, szTitlte, MB_OK); return 0; } m_dwPid = GetPid(); //获取目标进程ID DebugPrivilege(); //进程提权 HANDLE hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_dwPid); if (NULL == hRemoteProcess) { AfxMessageBox(_T("OpenProcess Error")); return; } LPREMOTE_DATA lpData = new REMOTE_DATA; ZeroMemory(lpData, sizeof(REMOTE_DATA)); //获取对应函数的地址 lpData->dwGetModuleFileName = (DWORD)GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "GetModuleFileNameA"); lpData->dwGetModuleHandle = (DWORD)GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "GetModuleHandleA"); lpData->dwGetProcAddress = (DWORD)GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "GetProcAddress"); lpData->dwLoadLibrary = (DWORD)GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "LoadLibraryA"); // 拷贝对应的字符串 StringCchCopyA(lpData->szMessage, MAX_STRING_LENGTH, "Inject Success!!!"); StringCchCopyA(lpData->szUerDll, MAX_PATH, "user32.dll"); StringCchCopyA(lpData->szMessageBox, MAX_PROC_NAME_LENGTH, "MessageBoxA"); //在远程空间中申请对应的内存,写入参数和函数的代码 LPVOID lpRemoteBuf = VirtualAllocEx(hRemoteProcess, NULL, sizeof(REMOTE_DATA), MEM_COMMIT, PAGE_READWRITE); // 存储data结构的数据 LPVOID lpRemoteProc = VirtualAllocEx(hRemoteProcess, NULL, 0x4000, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 存储函数的代码 DWORD dwWrittenSize = 0; WriteProcessMemory(hRemoteProcess, lpRemoteProc, &RemoteThreadProc, 0x4000, &dwWrittenSize); WriteProcessMemory(hRemoteProcess, lpRemoteBuf, lpData, sizeof(REMOTE_DATA), &dwWrittenSize); HANDLE hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpRemoteProc, lpRemoteBuf, 0, NULL); WaitForSingleObject(hRemoteThread, INFINITE); VirtualFreeEx(hRemoteProcess, lpRemoteBuf, 0, MEM_RELEASE); VirtualFreeEx(hRemoteProcess, lpRemoteProc, 0, MEM_RELEASE); CloseHandle(hRemoteThread); CloseHandle(hRemoteProcess); delete[] lpData;
2018年09月09日
6 阅读
0 评论
0 点赞