一线红队带你从O到无限进步
本文章所涉及所有内容均是大佬上课说讲的内容,其中做了一下基础的补充,高深的技术后面会慢慢在文章中出现
一线在职红队在线性感分享技术,从c语言开始基础带你慢慢进入终端对抗(免杀但是不止免杀)的大门。
今天分享大佬对R3层杀软对抗看法,以及环境搭建。
我们先看看大佬对当下杀软的看法以及对抗的细节概述。
流量层面 - cdn
行为层面 - 静态行为- 静态查杀
动态查杀方式 - iat表 字符串(危险exe危险的字符串的hash值被标记了) 签名(5k~200k不等) 熵值 白+黑 exe
行为查杀方式 - 启发式查杀:loader加载shellcode 使用函数的调用链
行为查杀 进程注入 注册表 添加用户 添加计划任务(不适用与启发式) bof - 走api形式(不用起cmd或者powershell)
内存查杀 卡巴斯基(做的最好) 诺顿 360 火绒(一般只是扫rwx区)
静态(主要目标:赛门铁克)沙箱牛逼
静态 eset (流量监测严格)+ bitdefender
杀软特点(不会扫自己的签名进程)
编程规范
google规范
linux命名:变量命名:buff_len
函数命名:read_buffer()
windows命名:变量命名:buffLen
函数命名:ReadBuffer
int i
char str = "1";//utf - 8
WCHAR wStr = L"1";//utf - 16
BOOL bRet = FALSE //TRUE
char* pStr = NULL;
WCHAR* pWstr = NULL;
W函数适用于亚太地区文字 A函数适用于英美
这里写出编程规范是大佬一直强调编程的规范重要性。
下面给出的就是编程环境搭建的步骤以及截图:
首先我们需要一个干净的win10虚拟机环境(需要英文版),不要windowsdefender这里杀软存在。
接下来是c语言一些基础讲解
数据类型
1.整型
作用:整型变量表示整数类型的数据。
语法:
int a = 10;
C++中共有4种表示整型的类型,区别在于所占内存空间和可表示的取值范围不同。
数据类型 |
占用空间 |
short(短整型) |
2字节 |
int(整型) |
4字节 |
long(长整形) |
4字节 |
long long(长长整形) |
8字节 |
2.浮点型(实型)
作用:浮点型变量表示小数类型的数据。
语法:
float f1 = 3.1415926535f;
double d1 = 3.1415926535;
浮点型变量分为两种,区别在于表示的有效数字范围不同。
(1)单精度float:4字节
(2)双精度double:8字节
数据类型 |
占用空间 |
float |
4字节 |
double |
8字节 |
3.字符型
C和C++语言中,字符型变量只占用1个字节。
字符型变量是将对应的ASCII编码存放至内存,而不是字符本身。
作用:字符型变量可表示单个字符。
语法:
char ch = 'a';
4.字符串型
作用:表示一串字符
语法:
//C风格字符串
char cStr[] = "hello c str";
cout << cStr << endl;
//C++风格字符串
string cppStr = "hello cpp str";
cout << cppStr << endl;
(1)C风格字符串: char 变量名[] = "字符串值";
(2)C++风格字符串: string 变量名 = "字符串值";
5.布尔类型 bool
作用:布尔数据类型表示真或假的值。
语法:
bool flag = true;
cout << flag << endl; // 1(真)
flag = false;
cout << flag << endl; // 0(假)
bool类型占1个字节大小,且只有两个值:
(1)true: 真(本质是1)
(2)false:假(本质是0)
5.转义字符
作用:表示一些特殊的无法直接显示的ASCII字符。
常用的转义字符有:n \ t
转义字符 |
含义 |
ASCII**码值(十进制)** |
a |
警报 |
007 |
b |
退格(BS) ,将当前位置移到前一列 |
008 |
f |
换页(FF),将当前位置移到下页开头 |
012 |
n |
换行(LF) ,将当前位置移到下一行开头 |
010 |
r |
回车(CR) ,将当前位置移到本行开头 |
013 |
t |
水平制表(HT) (跳到下一个TAB位置) |
009 |
运算符
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 语言内置了丰富的运算符,并提供了以下类型的运算符:
·算术运算符
·关系运算符
·逻辑运算符
·位运算符
·赋值运算符
·杂项运算符
算术运算符
运算符 |
描述 |
实例 |
+ |
把两个操作数相加 |
A + B 将得到 30 |
- |
从第一个操作数中减去第二个操作数 |
A - B 将得到 -10 |
* |
把两个操作数相乘 |
A * B 将得到 200 |
/ |
分子除以分母 |
B / A 将得到 2 |
% |
取模运算符,整除后的余数 |
B % A 将得到 0 |
++ |
自增运算符,整数值增加 1 |
A++ 将得到 11 |
-- |
自减运算符,整数值减少 1 |
A-- 将得到 9 |
关系运算符
int A = 10;
int B = 20;
运算符 |
描述 |
实例 |
== |
检查两个操作数的值是否相等,如果相等则条件为真。 |
(A == B) 为假。 |
!= |
检查两个操作数的值是否相等,如果不相等则条件为真。 |
(A != B) 为真。 |
> |
检查左操作数的值是否大于右操作数的值,如果是则条件为真。 |
(A > B) 为假。 |
< |
检查左操作数的值是否小于右操作数的值,如果是则条件为真。 |
(A < B) 为真。 |
>= |
检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 |
(A >= B) 为假。 |
<= |
检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 |
(A <= B) 为真。 |
逻辑运算符
int A = 1;
int B = 0;
运算符 |
描述 |
实例 |
&& |
称为逻辑与运算符。如果两个操作数都非零,则条件为真。 |
(A && B) 为假。 |
|| |
称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 |
(A || B) 为真。 |
! |
称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。 |
!(A && B) 为真。 |
位运算符
&、 | 和 ^ 的真值表如下所示:
p |
q |
p & q |
p | q |
p ^ q |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
0 |
1 |
0 |
0 |
1 |
1 |
赋值运算符
运算符 |
描述 |
实例 |
= |
简单的赋值运算符,把右边操作数的值赋给左边操作数 |
C = A + B 将把 A + B 的值赋给 C |
+= |
加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 |
C += A 相当于 C = C + A |
-= |
减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 |
C -= A 相当于 C = C - A |
*= |
乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 |
C *= A 相当于 C = C * A |
/= |
除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 |
C /= A 相当于 C = C / A |
%= |
求模且赋值运算符,求两个操作数的模赋值给左边操作数 |
C %= A 相当于 C = C % A |
<<= |
左移且赋值运算符 |
C <<= 2 等同于 C = C << 2 |
>>= |
右移且赋值运算符 |
C >>= 2 等同于 C = C >> 2 |
&= |
按位与且赋值运算符 |
C &= 2 等同于 C = C & 2 |
^= |
按位异或且赋值运算符 |
C ^= 2 等同于 C = C ^ 2 |
|= |
按位或且赋值运算符 |
C |= 2 等同于 C = C | 2 |
杂项运算符 ↦ sizeof & 三元
列出 C 语言支持的其他一些重要的运算符
运算符 |
描述 |
实例 |
sizeof() |
返回变量的大小。 |
sizeof(a) 将返回 4,其中 a 是整数。 |
& |
返回变量的地址。 |
&a; 将给出变量的实际地址。 |
* |
指向一个变量。 |
*a; 将指向一个变量。 |
? : |
条件表达式 |
如果条件为真 ? 则值为 X : 否则值为 Y |
C语言运算符优先级
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
1 |
*[]* |
数组下标 |
数组名[常量表达式] |
左到右 |
-- |
*()* |
圆括号 |
(表达式)/函数名(形参表) |
-- |
|
|
*.* |
成员选择(对象) |
对象.成员名 |
-- |
|
|
*->* |
成员选择(指针) |
对象指针->成员名 |
-- |
|
|
|
|
|
|
|
|
2 |
*-* |
负号运算符 |
-表达式 |
*右到左* |
单目运算符 |
*~* |
按位取反运算符 |
~表达式 |
|
|
|
*++* |
自增运算符 |
++变量名/变量名++ |
|
|
|
*--* |
自减运算符 |
--变量名/变量名-- |
|
|
|
*** |
取值运算符 |
*指针变量 |
|
|
|
*&* |
取地址运算符 |
&变量名 |
|
|
|
*!* |
逻辑非运算符 |
!表达式 |
|
|
|
*(**类型**)* |
强制类型转换 |
(数据类型)表达式 |
-- |
|
|
*sizeof* |
长度运算符 |
sizeof(表达式) |
-- |
|
|
|
|
|
|
|
|
3 |
*/* |
除 |
表达式/表达式 |
左到右 |
双目运算符 |
*** |
乘 |
表达式*表达式 |
|
|
|
*%* |
余数(取模) |
整型表达式%整型表达式 |
|
|
|
4 |
*+* |
加 |
表达式+表达式 |
左到右 |
双目运算符 |
*-* |
减 |
表达式-表达式 |
|
|
|
5 |
*<<* |
左移 |
变量<<表达式 |
左到右 |
双目运算符 |
*>>* |
右移 |
变量>>表达式 |
|
|
|
|
|
|
|
|
|
6 |
*>* |
大于 |
表达式>表达式 |
左到右 |
双目运算符 |
*>=* |
大于等于 |
表达式>=表达式 |
|
|
|
*<* |
小于 |
表达式<表达式 |
|
|
|
*<=* |
小于等于 |
表达式<=表达式 |
|
|
|
7 |
*==* |
等于 |
表达式==表达式 |
左到右 |
双目运算符 |
*!**=* |
不等于 |
表达式!= 表达式 |
|
|
|
|
|
|
|
|
|
8 |
*&* |
按位与 |
表达式&表达式 |
左到右 |
双目运算符 |
9 |
*^* |
按位异或 |
表达式^表达式 |
左到右 |
双目运算符 |
10 |
*|* |
按位或 |
表达式|表达式 |
左到右 |
双目运算符 |
11 |
*&&* |
逻辑与 |
表达式&&表达式 |
左到右 |
双目运算符 |
12 |
*||* |
逻辑或 |
表达式||表达式 |
左到右 |
双目运算符 |
|
|
|
|
|
|
13 |
*?:* |
条件运算符 |
表达式1?表达式2: 表达式3 |
*右到左* |
三目运算符 |
|
|
|
|
|
|
14 |
*=* |
赋值运算符 |
变量=表达式 |
*右到左* |
-- |
*/=* |
除后赋值 |
变量/=表达式 |
-- |
|
|
*=** |
乘后赋值 |
变量*=表达式 |
-- |
|
|
*%=* |
取模后赋值 |
变量%=表达式 |
-- |
|
|
*+=* |
加后赋值 |
变量+=表达式 |
-- |
|
|
*-=* |
减后赋值 |
变量-=表达式 |
-- |
|
|
*<<=* |
左移后赋值 |
变量<<=表达式 |
-- |
|
|
*>>=* |
右移后赋值 |
变量>>=表达式 |
-- |
|
|
*&=* |
按位与后赋值 |
变量&=表达式 |
-- |
|
|
*^=* |
按位异或后赋值 |
变量^=表达式 |
-- |
|
|
*|=* |
按位或后赋值 |
变量|=表达式 |
-- |
|
|
|
|
|
|
|
|
15 |
*,* |
逗号运算符 |
表达式,表达式,… |
左到右 |
-- |
同一优先级的运算符,运算次序由结合方向所决定
简单记就是:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符
课程知识点讲解
1.const 变量
const int d = 10“(const代表整个作用域中的d都是定值10)
解释:const 变量
const 变量指的是,此变量的值是只读的,不应该被改变。
如果我们在程序中试图修改 const 变量的值,在编译的时候,编译器将给出错误提示。
正因为 const 变量的值在给定以后不能改变,所以 const 变量必须被初始化。
2.函数入口点
函数入口点默认是main函数,程序运行时是从main函数开始运行,没有特殊需求的情况不要修改
如果需要修改就需要再编辑器中修改,vs和vc位置不一样这里我们演示vs的修改位置
先打开项目的属性页面
然后再点开链接器界面,选择所以选项的选项卡,然后翻找一下就可以看到入口点配置,在这里就可以修改你的入口点函数
3.格式说明符
通常来说使用格式说明符的输出函数是printf(),输入函数是scanf_s(),这两个函数的用法不在过多描述,接下来看一下格式说明符
·%d 或 %i: 输出十进制整数。
·%u: 输出无符号十进制整数。
·%f: 输出浮点数。
·%s: 输出字符串。
·%c: 输出单个字符。
·%x 或 %X: 输出十六进制数。
·%o: 输出八进制数。
·%p: 输出指针地址。
上面就是格式化东西,下面用代码演示一下用法
printf("number:%d",a);
这里的%d就是格式说明符表示已十进制整形输出,a则是输出的内容
例如:int a =10;
printf("number:%d",a);
那么输出结果就是number:10。
如果要打印多个数据就如下操作
int a = 10;
int b = 11;
int c = 12;
printf("number1:%d,number2:%d,number3:%d",a,b,c);
那么结果就是number1:10,number2:11,number3:12
其他格式说明符均是这样操作。
计算大小的函数
1.sizeof
sizeof是一个单目运算符,不是函数。
sizeof返回一个对象在内存中所占的存储空间,单位是字节byte。
printf("short int=%lldn", sizeof(short));
printf("int=%lldn", sizeof(int));
printf("long int=%lldn", sizeof(long));
printf("long long int=%lldn", sizeof(long long));
printf("float=%lldn", sizeof(float));
printf("double=%lldn", sizeof(double));
printf("char=%lld", sizeof(char));
输出效果:
short int=2
int=4
long int=4
long long int=8
float=4
double=8
char=1
注意: sizeof()//sizeof包含结尾的 []这种情况一般用sizeof sizeof计算的是在内存种占用的大小(算指针在内存的大小只能用sizeof)
2.strlen
strlen函数是C语言中的一个字符串函数,用于计算一个字符串的长度。 它的原型如下:
size_t strlen(const char *str);
其中,参数str是一个指向以 字符结尾的字符串的指针,函数返回值是一个无符号整型(size_t),表示字符串的长度。
char str[] = "Hello World";
int len = strlen(str);
printf("Length of string: %dn", len);
输出结果:
Length of string: 11
申请内存的函数
常用的内存申请函数有4个:malloc() calloc() HeapAlloc() VitrualAlloc()
1.malloc
分配内存块。
语法:
void *malloc(
size_t size
);
参数:
size
要分配的字节数。
返回值:
malloc 会返回指向已分配空间的 void 指针,如果可用内存不足,则返回 NULL。
例子:
double * ptd = (double * ) malloc (30 * sizeof(double));
2.calloc
使用初始化为 0 的元素分配内存中的数组。
语法:
void *calloc(
size_t number,
size_t size
);
参数:
number
元素数量。
size
每个元素的长度(以字节为单位)。
返回值:
calloc 返回指向已分配空间的指针。 返回值指向的存储空间与任何类型的对象的存储适当对齐。 若要获取指向类型而非 void 的指针,请在返回值中使用类型转换。
例子:
int* p = (int*)calloc(10,sizeof(int));
3.HeapAlloc
从堆中分配内存块。 分配的内存不可移动。
语法:
DECLSPEC_ALLOCATOR LPVOID HeapAlloc(
[in] HANDLE hHeap,
[in] DWORD dwFlags,
[in] SIZE_T dwBytes
);
参数:
[in] hHeap
要从中分配内存的堆的句柄。 此句柄由 HeapCreate 或 GetProcessHeap 函数返回。
[in] dwFlags
堆分配选项。 指定这些值中的任何一个都将替代使用 HeapCreate 创建堆时指定的相应值。 此参数可使用以下一个或多个值。
[in] dwBytes
要分配的字节数。
如果 hHeap 参数指定的堆是“不可增长的”堆, 则 dwBytes 必须小于 0x7FFF8。 可以通过使用非零值调用 HeapCreate 函数来创建不可增长的堆。
返回值:
如果函数成功,则返回值是指向已分配内存块的指针。
如果函数失败并且您尚未指定 HEAP_GENERATE_EXCEPTIONS,则返回值为 NULL。
例子:
char* buffer3 = NULL;
buffer3 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(calculate));
注意:HeapAlloc没有设置内存权限的设置。
4.VitrualAlloc
保留、提交或更改调用进程的虚拟地址空间中页面区域的状态。 此函数分配的内存会自动初始化为零。
语法:
LPVOID VirtualAlloc(
[in, optional] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flAllocationType,
[in] DWORD flProtect
);
参数:
[in, optional] lpAddress
要分配的区域的起始地址。 如果保留内存,则指定的地址向下舍入到分配粒度中最近的倍数。 如果内存已保留并正在提交,地址将向下舍入到下一页边界。 若要确定页面的大小和主机计算机上的分配粒度,请使用 GetSystemInfo 函数。 如果此参数 NULL,系统将确定分配区域的位置。
[in] dwSize
区域的大小(以字节为单位)。 如果 lpAddress 参数 NULL,则此值向上舍入到下一页边界。 否则,分配的页面将包含范围中包含一个或多个字节的所有页面,从 lpAddress 到 lpAddress+dwSize。 这意味着跨页边界的 2 字节范围会导致这两个页面都包括在分配的区域。
[in] flAllocationType
内存分配的类型。此参数必须包含以下值之一。
具体参数前往微软官网查看
https://learn.microsoft.com/zh-cn/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
[in] flProtect
要分配的页面区域的内存保护。 如果要提交页面,则可以指定内存保护常量之一。
如果 lpAddress 指定 enclave 中的地址,flProtect 不能是以下值之一:
PAGE_NOACCESS
PAGE_GUARD
PAGE_NOCACHE
PAGE_WRITECOMBINE
为 enclave 分配动态内存时,flProtect 参数必须 PAGE_READWRITE 或 PAGE_EXECUTE_READWRITE。
返回值:
如果函数成功,则返回值是页面分配区域的基址。
如果函数失败,则返回值 NULL。
例子:
char* buffer4 = NULL;
buffer4 = VirtualAlloc(NULL, sizeof(calculate)//需要申请的大小, MEM_COMMIT//此处理解为固定搭配一般不修改, PAGE_EXECUTE_READWRITE);//这里我们申请的权限是可读可写可执行 EXECUTE:执行权限 READ:读权限 WRITE:写权限
总结:前三者没有申请的内存没有执行权限,VitrualAlloc有,每个函数都有适用场景
字符串拷贝以及内存拷贝的函数
1.strcpy()(字符串拷贝常用函数)
使用头文件:#include
定义:char *strcpy(char *dest, const char *src);
参数:
destinin:目标字符数组;
source:源字符数组;
函数说明:strcpy()会将参数src 字符串拷贝至参数dest 所指的地址。 用于对字符串进行复制,识别到字符串的结束符号‘ ’自动停止
返回值:返回参数dest 的字符串起始地址。
注意:
参数 dest 的内存空间要足够大,否则拷贝可能会造成缓冲溢出。
strcpy() 在复制结束后会添加结束符 ,这点和strncpy()不同
2.memcpy()(内存拷贝常用函数)
使用头文件:C语言:#include
定义:void memcpy(void *dest, const void *src, size_t n);
参数:
destinin:目标地址;
source:源地址;
n:复制的字节长度。
函数说明:memcpy()复制 src 所指的内存数据的 n 个字节到 dest所指的内存地址上。也就是从源地址复制n 个字节到目标地址
第一个和第二个指针都是void型且第二个指针不能被修改,第三个参数是需要拷贝的内存长度按字节记。
返回值:返回指向 dest 的指针。返回的指针类型是void。
注意:
memcpy()并不限制被复制的数据类型,只是逐字节地进行复制,任何数据类型都可以进行复制,例如字符数组、整型、结构体、类等
memcpy() 会完整的复制 num个字节,不会遇到‘ ’而结束,这点与 strcpy() 不同
通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
条件判断函数
1.if
语法:
if (表达式)
{
语句
}
例子:
#include
void main()
{
int a = 10;
int b = 20;
if(a>b)
{
printf("max = %dn",a);
}
if(a <b)< span> </b)<>
{
printf("max = %dn",b);
}
}
如果满足第一个if的表达式那么就打印a的值,如果满足第二个if的表达式那么就打印b的值
上面的结果是max = 20;
2.if_else
默认在if和else语句中都只控制一条语句
语法:
if(表达式)
{
语句
}
else
{
语句
}
例子:
#include
int main()
{
int age = 0;
scanf("%d", &age);
if (age >= 18)
{
printf("已经成年了n");
printf("该上大学了n");
}
else
{
printf("未成年n");
printf("还没上大学n");
}
return 0;
}
if esle的本质就是如果没有满足if的表达式那么就直接执行else里面的语句
按照上面的代码逻辑来看,如果我们输入的age值满足if的表达式就打印已经成年了,没有就直接执行else里面的语句
所以如果你输入的值大于等于18就打印已经成年了,没有就是打印未成年。
3.switch
switch语句是一个多分支选择语句,并且可以支持嵌套
语法:
switch(表达式)
{
case 常量1:语句1
case 常量2:语句2
default:语句n
break;
}
例子:
#define _CRT_SECURE_NO_WARNINGS 1
#include
int main()
{
int day = 0; //定义一个整型变量day,并对其赋值为0
printf("请输入一个数字n"); //让用户输入一个数字
scanf("%d", &day); //接收用户输入的数字
switch (day)
{
case 1:printf("星期一n"); //如果day的值为1,则输出星期一
case 2:printf("星期二n"); //以下同上
case 3:printf("星期三n");
case 4:printf("星期四n");
case 5:printf("星期五n");
case 6:printf("星期六n");
case 7:printf("星期天n");
default:
break;
}
return 0;
我们先观看代码逻辑根据我们输入的值会产生不同分支选择
这里我们接收的变量是day,day会作为switch的表达式,当day = 1那么就会满足第一个分支,就打印星期一以此类推
如果上面的七个分支都没满足就会走到default这个分支。和if else类似。
循环函数
1.while循环语句:
语法:
while(表达式)
{
循环语句;
}
例子:
#include
int main()
{
int n = 1;
while (n <= 10)//如果n<=10就继续执行循环的内容
{
printf("%d ", n);
n++;//调整循环的部分
}
return 0;
}
代码的执行结果就是打印从1到10,当n自增到11时就会退出循环,因为已经不满表达式条件了
n++等同于n = n + 1
2.for循环语句()
语法:
for(表达式1; 表达式2; 表达式3)
{
循环语句;
}
例子:
int i = 0; //初始化变量
for (i = 1; i <= 10; i++)//第一个分号之前的为表达式1以此类推分别为表达式2和表达式3
{
printf("%d ", i); //需要循环的语句
}
这个循环的逻辑是,先给用于进行判断的变量进行赋值,然后根据表达式2的判断语句进行判断,如果i<=10那么就执行循环语句,反之则退出循环,表达式3就是用于给i变量自增,以满足循环能够结束,否则就会无限循环
这段代码的执行结果就是 从1打印到10。
3.do...while循环
do while循环与while循环的语法基本差不多,但是do while至少会先执行一次循环语句。
语法:
do
{
循环语句;
}while(表达式);
例子:
int i = 1;
do
{
printf("%d",a)
i++;
}while(i <= 10);
这段代码的逻辑就是,先执行一次打印,然后在进行判断,i是否<=10如果满足表达式就继续执行循环语句,打印2到10。
所以do while和while的区别就是最少会先执行一次循环语句,在判读是否满足表达式。
常规shellcode执行步骤(为核心基础步骤)
1.计算shellcode内存大小(因为我们需要去申请一块内存存放我们的shellcode,所以需要获得shellcode的大小)
2.申请shellcode内存(根据大小申请内存,这里我们需要申请可读可写可执行的内存)
3.将shellcode写入内存中(写入shellcode)
4.执行shellcode内容(shellcode放入内存以后我们将它执行起来)
原文始发于微信公众号(泾弦安全):免杀对抗从0开始(一)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论