远程线程注入详解.ppt_第1页
远程线程注入详解.ppt_第2页
远程线程注入详解.ppt_第3页
远程线程注入详解.ppt_第4页
远程线程注入详解.ppt_第5页
已阅读5页,还剩27页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、远程线程注入详解,哈尔滨工程大学,前言,远程线程技术指的是通过在其他进程中创建新线程的方法进入该进程的内存地址空间,从而获得对该进程的控制权的方法。 在进程中可以通过CreateThread函数创建线程,被创建的新线程与主线程共享地址空间以及其他的资源。同样,通过CreateRemoteThread函数可以在其他进程内创建新线程,新创建的的远程线程可以共享远程进程的地址空间。 所以通过在远程进程中创建新线程的方法,就可以进入到远程进程的内存地址空间,也就拥有了和那个远程进程相当的权限,可以在远程进程中执行代码,从而达到远程进程控制、进程隐藏的目的。,常用的注入手段,远程线程注入相信对Windo

2、ws底层编程和系统安全熟悉的人并不陌生,其主要核心在于一个Windows API函数CreateRemoteThread,通过它可以在另外一个进程中注入一个线程并执行。在提供便利的同时,正是因为如此,使得系统内部出现了安全隐患。常用的注入手段有两种:一种是远程的dll的注入,另一种是远程代码的注入。 顾名思义,远程线程注入就是在非本地进程中创建一个新的线程。相比而言,本地创建线程的方法很简单,系统API函数CreateThread可以在本地创建一个新的线程,其函数声明如下:,CreateThread参数,HANDLE WINAPI CreateThread( LPSECURITY_ATTRIB

3、UTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, PDWORD lpThreadId ); 这里最关心的两个参数是lpStartAddress和lpParameter,它们分别代表线程函数的入口和参数,其他参数一般设置为0即可。由于参数的类型是LPVOID,因此传入的参数数据需要用户自己定义,而入口函数地址类型必须是LPTHREAD_START_ROUTINE类型。LPTHREAD_START

4、_ROUTINE类型定义为: typedefDWORD(WINAPI*PTHREAD_START_ROUTINE)(LPVOIDlpThreadParameter);typedefPTHREAD_START_ROUTINELPTHREAD_START_ROUTINE;,CreateRemoteThread,HANDLE WINAPI CreateRemoteThread( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAdd

5、ress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); 可见该函数就是比CreateThread多了一个参数用于传递远程进程的打开句柄,而我们知道打开一个进程需要函数OpenProcess,其函数声明为:,OpenProcess,HANDLEWINAPIOpenProcess(DWORDdwDesiredAccess,BOOLbInheritHandle,DWORDdwProcessId); 第一个参数表示打开进程所要的访问权限,一般使用PROCESS_ALL_ACCESS来获得所有权限,第二个参数表示进程的

6、继承属性,这里设置为false,最关键的参数是第三个参数进程的ID。因此在此之前必须获得进程名字和PID的对应关系,TlHelp32.h库内提供的函数CreateToolhelp32Snapshot、Process32First、Process32Next提供了对当前进程的遍历访问,使用这里有段公用代码可以使用: #include ,获取进程名称的进程ID号,DWORD getPid(LPTSTR name) HANDLE hProcSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);/获取进程快照句柄 assert(hProcSnap!=I

7、NVALID_HANDLE_VALUE); PROCESSENTRY32 pe32; pe32.dwSize=sizeof(PROCESSENTRY32); BOOL flag=Process32First(hProcSnap,本地程序的权限,因此,按照以上的方式,使用getpid获取指定名称进程pid,传入OpenProcess打开进程获取进程句柄。但是你会发现这时候进程是无法打开的,或者说进程不能以完全访问的权限打开,因此必须提高本地程序的权限,这是远程注入线程引发的第一个问题,这里也有一段通用代码。/提升进程权限,本地程序的权限,int EnableDebugPrivilege(cons

8、t LPTSTR name) HANDLE token; TOKEN_PRIVILEGES tp; /打开进程令牌环 if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,入口地址的问题,通过调用EnableDebugPrivilege(SE_DEBUG_NAME)提高本地程序权限后就可以打开系统进程了。然后传入进程句柄到CreateRemoteThread注入远程进程,但是遗憾的是远程线程无法运行,这里就引发了第二个问题。CreateRemoteThread和CreateThread并不仅仅

9、是多了一个进程句柄参数那么简单,其中更大的区别是它们的函数入口和参数的区别。CreateThread是创建本地线程,函数入口地址和参数都在本地进程,这很好理解,但是CreateRemoteThread创建的是其他进程的线程,它的入口地址和参数就该在其他进程中。如果强行把本地地址和参数传入,虽然编译上能通过,但是运行时侯被注入的进程会查找和本地进程相同值的地址和参数地址,当然结果可想而知,这就像拿着一号公寓201的钥匙去开二号公寓201的门一样。,在远端进程开辟新的空间和写入代码,既然这样,那么如何告诉远程线程需要执行的代码和地址呢?继续上边那个例子,假设在一号公寓201房间内可以使用高功率电器

10、,但是一号公寓检查严格,一旦有此情况立马被禁止。而二号公寓戒备很松,所以有人想办法在二号公寓新准备一个空的房间专门使用高功率电器,这样即回避了检查,也达到了目的。这里一号公寓相当于本地进程,二号公寓相当于系统进程,使用高功率电器相当于黑客的行为,准备新的房间相当于开辟新的存储空间,禁止使用高功率电器相当于杀软的查杀。那么这里就需要关心如何在二号公寓新建一个房间,这里系统有两个API函数VirtualAllocEx和WriteProcessMemory,顾名思义,前者在远程进程中申请一段内存用于存储数据或者代码准备房间,后者在申请的空间内写入数据或者代码准备高功率电器。参看一下他们的声明就一目了

11、然,在远端进程中申请空间,LPVOID WINAPI VirtualAllocEx( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect ); VirtualAllocEx指定了进程和申请内存块的大小以及内存块的访问权限,并且返回申请后的内存首地址这个地址是远程进程中的地址,在本地进程没有任何意义。一般函数调用形式如下: char*procAddr=(char*)VirtualAllocEx(hProc,NULL,1024,MEM_COMMIT,PAGE_READWR

12、ITE); 这样就在进程hProc中申请到了一个1024字节大小的可读可写的内存块。,写入数据和代码,BOOL WINAPI WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T * lpNumberOfBytesWritten ); 这个函数和memcpy功能和形式都很类似,本质上就是缓冲区的复制,将数据lpBuffernSize的数据复制到hProcess:lpBaseAddressnSize中去。 这样CreateRemoteThread的参数就很

13、好设置了,线程入口函数地址找不到申请一段空间放上代码,返回代码首地址;参数地址找不到申请一段空间放上数据,返回数据首地址;这样房间,电器,原料都已齐全了,使用CreateRemoteThread启动电器就可以加工了!这种思维很合乎逻辑,但是实现起来较为复杂,这是稍后介绍的代码注入方式。不过在这之前我们需要看一种更简单的dll注入方式,说起dll我们需要声明两点关键的内容:,远程线程DLL注入,首先,我们需要知道Win32程序在运行时都会加载一个名为kernel32.dll的文件,而且Windows默认的是同一个系统中dll的文件加载位置是固定的。我们又知道dll里有一系列按序排列的输出函数,因

14、此这些函数在任何进程的地址空间中的位置是固定的!例如本地进程中MessageBox函数的地址和其他任何进程的MessageBox的地址是一样的。 其次,我们需要知道动态加载dll文件需要系统API LoadLibraryA或者LoadLibraryW,由于使用MBCS字符集,这里我们只关心LoadLibraryA,而这个函数正是kernel32.dll的导出函数!因此我们就能在本地进程获得了LoadLibraryA的地址,然后告诉远程进程这就是远程线程入口地址,那么远程线程就会自动的执行LoadLibraryA这个函数。这就像我们已经知道二号公寓和一号公寓一样,在201房间都可以使用高功率电器

15、,那何必还要重新造一个新的房间放电器呢。,远程线程DLL注入,高功率电器可以搞定,但是即使煮饭也总要有米和水的。函数可以伪造代替,但是参数是不能伪造代替的。因此用前边的方法,我们申请一个新的房间专门存放粮食,待用到的时候取便是。我们知道LoadLibraryA的参数就是要加载的dll的路径,为了保险起见,我们把要注入的dll的路径字符串注入到远程进程空间中,这样返回的地址就是LoadLibraryA的参数字符串的地址,将这两个地址分别作为入口和参数传入CreateRemoteThread就可以使得远程进程加载我们自己的dll了。 说到这里,或许有人疑问这么折腾了半天,举了这么多例子,仅仅加载了

16、一个自定义dll进去,并没有做任何“想做”的事情。其实,这里已经能做基本上任何事情了。因此dll是我们自己写的,那么做什么事情就有我们自己来定,可能有人最疑惑的莫过于如何在加载dll以后立即执行我们真正想执行的代码。这里就需要看一下一个简单DLL工程。,DLL的编写,BOOLAPIENTRYDllMain(HANDLEhModule,DWORDul_reason_for_call,LPVOIDlpReserved)switch(ul_reason_for_call)caseDLL_PROCESS_ATTACH:/加载时候/dosomethingbreak;default:break;retur

17、nTRUE; 看到这个函数相信很多人一目了然了,在switch-case语句的case DLL_PROCESS_ATTACHE条件下就是执行用户自定义代码的地方,它执行的时机就是在DLL被任何一个进程加载的时候,这也就解决了第三个用户代码启动的问题,至于写什么有你自己决定。,远程线程代码注入,既然使用LoadLibraryA加载DLL执行启动代码并不能达到很好的效果,那么我们就想办法直接写代码直接让远程线程执行。 这里主要关心的就是代码的问题,因为线程函数参数传递方式和dll路径的方法大同小异,代码的注入却和数据的注入有着很多不同。 首先,这是第四个问题,注入代码如何书写。通过类比Create

18、Thread的函数入口,我们自然能想到,使用和CreateThread同样形式的函数定义即可,即形为LPSECURITY_ATTRIBUTES的函数定义。但是这里最关键的不是函数的定义形式,而是函数内部代码的限制。由于这段代码,或者叫注入函数,是要“拷贝”到其他进程空间去的,因此这个函数不能使用任何全局变量、不能使用堆空间、不能调用本地定义的函数、不能调用一些库函数等等。经测试,最保险的方式是:函数使用栈空间的局部变量是没有问题的,因为汇编代码将局部变量翻译为相对地址;函数使用系统的API是没有问题的,最可靠的是使用kernel32.dll内的函数,万一使用其他dll库的函数需要使用kerne

19、l32.dll导出函数LoadLibraryA加载对应的dll后,再使用kernel32.dll的导出函数GetProcAddress获取函数地址,比如MessagBox函数。虽然限制很多,但是足可以写出功能很强大的代码,因为Windows的API可以自由的使用!,注入代码如何定位,其次,即第五个问题,注入代码如何定位。定位包含两层含义:代码的起始位置和代码的长度。有人说这个简单,起始位置就是函数名的值,长度虽然不好确定,就给一个比较大的值就可以了。这个思路是没有问题的,但是实际上这么做并不一定成功!问题不在代码长度上,而是出现在代码的起始位置。,基本原理,A P I 函数 其中Virtual

20、AllocEx和CreateRemoteThread两个API函数只能NT内核下用。实现的基本过程如下: (1)通过OpenProcess函数打开进程PID为ProcessID的远程进程: hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, ProcessId); (2)通过VirtualAllocEx函数在刚打开的进程中申请IMageSize个字节的内存: InjectPoint = (LPBYTE)VirtualAllocEx(hProcess, 0, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE

21、) ; (3)通过WriteProcessMemory函数将NewModule开始的IMageSize个字节的代码写入已经申请的内存中: WriteProcessMemory(hProcess, InjectPoint, NewModule, ImageSize, NULL) ; (4)通过CreateRemoteThread函数启动刚写入的代码: CreateRemoteThread(hProcess, NULL, 0, RemoteEntryPoint, Param, 0, NULL);,重定位和函数导入的问题,远线程DLL注入主要通过使用API CreateRemoteThread创建远

22、程线程来实现,CreateRemoteThread与创建线程用的CreateThread非常相似,除了第一个参数hProcess之外,其他参数都是一样的。hProcess用于指定要在哪个进程中创建远程线程,也就是需要将DLL注入的那个进程。 为了通过将远程线程与DLL加载关联起来,我们可以把LoadLibraryW作为线程函数,把DLL的路径作为线程的参数。,重定位和函数导入的问题,对于DLL的路径,因为用户态进程空间是互相独立的,所以我们需要通过VirtualAllocEx在目标进程中分配一块足够大小的内存空间,用于存放DLL的路径字符串,可以通过WriteProcessMemory这个AP

23、I来实现跨进程写数据。那么又如何获取目标进程空间中LoadLibraryW的地址呢?对于kernel32.dll这样的DLL,操作系统会保证他们在任何进程中加载的基地址都是一样的,这样以来,kernel32.dll中的导出函数的地址也会是固定的,所以我们可以直接通过GetModuleHandle和GetProcAddress这两个API来获取LoadLibraryW的地址。 由于创建远程线程需要进行跨进程内存空间分配、跨进程内存数据读写等操作,所以是需要一定的权限的。我们必须将发起进程提升到一定的权限,否则操作会失败。通常将进程提升到DEBUG权限即可。,线程注入实现原理,BOOL DllIn

24、ject(DWORD dwPid, TCHAR szDllPath) BOOL bRet = FALSE; HANDLE hRemoteProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwPid); HMODULE hKrl32 = GetModuleHandle(TEXT(kernel32.dll); RemoteThreadProc remoteThreadProc = (RemoteThreadProc)GetProcAddress(hKrl32,

25、 LoadLibraryW); DWORD cbSize = (lstrlen(szDllPath) + 1) * sizeof(szDllPath0); TCHAR *pszRemoteParam = (TCHAR *)VirtualAllocEx(hRemoteProcess, 0, cbSize, MEM_COMMIT, PAGE_READWRITE);,线程注入实现,BOOL bWriteMem = WriteProcessMemory(hRemoteProcess, (PVOID)pszRemoteParam, (PVOID)szDllPath, cbSize, NULL); HAN

26、DLE hThread = CreateRemoteThread(hRemoteProcess, NULL, 0, remoteThreadProc, (LPVOID)pszRemoteParam, 0, NULL); WaitForSingleObject(hThread, INFINITE); / 判断注入是否成功 DWORD dwExitCode = 0; bRet = GetExitCodeThread(hThread, ,远程注入线程的卸载,如何卸载远程DLL呢?同样可以使用这个方法来调用GetModuleHandle和FreeLibrary。 / = 获得需要创建REMOTETHR

27、EAD的进程句柄 = HWND hWnd = FindWindow(notepad, NULL); / 以NOTEPAD为例 DWORD dwProcessId; :GetWindowThreadProcessId(hWnd, ,提升进程权限,BOOL EnablePriv() HANDLE hToken; If(OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,枚举当前线程,CreateToolhelp32Snapshot/Process32First/Process32Next API枚举系统进程.在很多情况下需要对系统的进程进行操作,方法有很多种但最常用的是: CreateToolhelp32Snapshot/Process32First/Process32Next 一系列API使现结束进程使用 TerminateProcess结束进程。,枚举当前线程,int ProcessList() PROCESSENTRY32 pe32; pe32.dwSize

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论