1. 前言
2. 原理
3. 可利用的点
4. 实践开始
5. 总结
01
前言
为了避免杀软检测到代码中的shellcode,可以对shellcode进行加密,执行时输入key,使用本地分离、远程加载等方式。后面几种很容易实现,效果也很好,但是操作麻烦,而且除了远程加载外都不能使用在钓鱼中;如果只用第一种方式,这种方法的好处是可以避免使用独立的程序,并且可以在不暴露key的情况下解密shellcode。不过,由于加密算法和密钥都是硬编码的,想靠加密算法如des、ase、rc4等免杀很难,它们可能会存在被杀软检测并逆推还原出原shellcode等问题。为了提高静态免杀能力,可以在运行代码时动态生成key,而不是在代码中硬编码它。
02
原理
动态生成key的原理是利用外部运行环境点作为加密算法的key。注意,使用这些外部运行环境点作为加密算法的key并不能完全保证杀软无法逆推出原始shellcode。一些先进的杀软可能会监视这些环境点,并尝试通过分析它们的变化来检测潜在的恶意行为。为了确保最大的安全性,最好配合更复杂的加密算法来加密shellcode。
03
可利用的点
叫chatgpt列出了一些可利用的点:
这些外部运行环境点在不同的系统配置中可能会有所不同,因此在选择一个用作key的环境点时,需要进行一些测试以确保它在特定环境中是稳定的,并且不能被杀软检测到。
04
实践开始
以下仅在同一shellcode加载器及同一加密算法下实践。
#include <windows.h>
#include <iostream>
using namespace std;
int main()
{
HMODULE hModule = GetModuleHandleA("kernel32.dll");
if (hModule == NULL)
{
cout << "GetModuleHandle failed." << endl;
return -1;
}
char szPath[MAX_PATH];
DWORD dwSize = GetModuleFileNameA(hModule, szPath, MAX_PATH);
if (dwSize == 0)
{
cout << "GetModuleFileName failed." << endl;
return -1;
}
cout << "The path of kernel32.dll is: " << szPath << endl;
return 0;
}
过360火绒静态加动态,defender静态,卡巴直接不过。
#include <windows.h>
#include <iostream>
using namespace std;
int main()
{
HKEY hKey = NULL;
DWORD dwType = REG_SZ;
char szValue[MAX_PATH] = { 0 };
DWORD dwSize = sizeof(szValue);
LONG lRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey);
if (lRet != ERROR_SUCCESS)
{
cout << "RegOpenKeyEx failed." << endl;
return -1;
}
lRet = RegQueryValueExA(hKey, "ProductName", NULL, &dwType, (LPBYTE)szValue, &dwSize);
if (lRet != ERROR_SUCCESS)
{
cout << "RegQueryValueEx failed." << endl;
RegCloseKey(hKey);
return -1;
}
cout << "The value of ProductName is: " << szValue << endl;
RegCloseKey(hKey);
return 0;
}
过360火绒静态加动态,defender静态,卡巴直接不过。
#include <windows.h>
#include <iphlpapi.h>
#include <iostream>
#pragma comment(lib, "iphlpapi.lib")
using namespace std;
int main()
{
PIP_ADAPTER_INFO pAdapterInfo = NULL;
ULONG ulSize = 0;
DWORD dwRet = GetAdaptersInfo(pAdapterInfo, &ulSize);
if (dwRet == ERROR_BUFFER_OVERFLOW)
{
pAdapterInfo = (PIP_ADAPTER_INFO)malloc(ulSize);
dwRet = GetAdaptersInfo(pAdapterInfo, &ulSize);
}
if (dwRet == ERROR_SUCCESS)
{
PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
while (pAdapter)
{
cout << "Adapter name: " << pAdapter->AdapterName << endl;
cout << "MAC address: ";
for (int i = 0; i < pAdapter->AddressLength; i++)
{
printf("%02X", pAdapter->Address[i]);
if (i < pAdapter->AddressLength - 1)
{
printf("-");
}
}
cout << endl;
pAdapter = pAdapter->Next;
}
}
else
{
cout << "GetAdaptersInfo failed." << endl;
}
if (pAdapterInfo)
{
free(pAdapterInfo);
}
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <time.h>
#include <iostream>
using namespace std;
int main()
{
time_t now = time(NULL);
cout << "Current time: " << now / 1000000000 << endl;
return 0;
}
过360火绒静态加动态,defender静态,卡巴直接不过。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
char* username = getenv("USERNAME");
if (username != NULL)
cout << "USERNAME is: " << username << endl;
else
cout << "USERNAME is not defined" << endl;
return 0;
}
过360火绒静态加动态,defender静态,卡巴静态加动态。
代码太长,这里不放了。
defender静态,卡巴一直卡死在99。
defender静态,卡巴不过。
defender静态,卡巴不过。
实践过程中,我逐渐意识到问题,可能并不是卡巴查杀很厉害,而是编译器的问题。在测试过程中,卡巴查杀经常卡在1%或99%,等很长时间,重复试了几次,最后卡巴直接删除了exe:
卡巴可能并没有检测出shellcode,而是基于一种风险指数,虽然没有检测出shellcode,但风险指数偏高,于是直接列为恶意软件直接删除了。这种风险指数与编译器有关。vs默认的编译器更容易被杀软认为恶意软件,vs默认的编译器几乎被所有杀软标记重点了。后面切换vs的intel C++编译器(需要自行安装),重新对前面的几个环境点进行了测试,切换intel C++编译器后均过卡巴静态加动态且扫描很快,没有出现像vs默认的编译器一直卡住的情况:
然后上传了VT测试,静态全过:
不使用动态生成key,明文密钥,使用intel c++编译,VT:
05
总结
使用动态生成key静态免杀效果良好。在使用vs默认编译器的情况下,上面的多个环境点测试均过360、火绒,defender过静态,卡巴除了利用环境变量外其它均不过;在切换vs的intel c++编译器后360、火绒、卡巴均过,defender过静态。使用intel c++编译免杀效果良好,但是安装麻烦,占内存
锦鲤安全
一个安全技术学习与工具分享平台
点分享
点收藏
点点赞
点在看