自学内容网 自学内容网

第13节 第二种shellcode编写实战(2)

我最近在做一个关于shellcode入门和开发的专题课👩🏻‍💻,主要面向对网络安全技术感兴趣的小伙伴。这是视频版内容对应的文字版材料,内容里面的每一个环境我都亲自测试实操过的记录,有需要的小伙伴可以参考🫡

我的个人主页:https://imbyter.com

一、C语言方式编写shellcode

第二种shellcode编写实战(1)的基础上,新增加一个CAPI类,将所有用到的函数都在这个类中做动态调用的处理,这样使得整个shellcode功能结构更加清晰。

1. 新建类CAPI(即api.h和api.cpp两个文件):

api.h:

#pragma once

#include <windows.h>
#include <Winternl.h>

class CAPI
{
private:
    HMODULE GetKernel32BaseAddress();
    FARPROC _GetPorcAddress();

public:
    void InitFunctions();

public:
    typedef HANDLE(WINAPI* FN_CreateFileA)(
        _In_ LPCSTR lpFileName,
        _In_ DWORD dwDesiredAccess,
        _In_ DWORD dwShareMode,
        _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
        _In_ DWORD dwCreationDisposition,
        _In_ DWORD dwFlagsAndAttributes,
        _In_opt_ HANDLE hTemplateFile);
    typedef int (WINAPI* FN_MessageBoxA)(
        __in_opt HWND hWnd,
        __in_opt LPCSTR lpText,
        __in_opt LPCSTR lpCaption,
        __in UINT uType);
    typedef HMODULE(WINAPI* FN_LoadLibraryA)(
        __in LPCSTR lpLibFileName);

public:
    FN_CreateFileA CreateFileA;
    FN_MessageBoxA MessageBoxA;
    FN_LoadLibraryA LoadLibraryA;
};

api.cpp:

#include "api.h"

// 获取kernel32基址
HMODULE CAPI::GetKernel32BaseAddress()
{
    HMODULE hKernel32 = NULL;

    // 用户保存模块名
    WCHAR wszModuleName[MAX_PATH];

#ifdef _WIN64    // 64位PEB偏移为0x60
    PPEB lpPeb = (PPEB)__readgsqword(0x60);
#else            // 32位PEB偏移为0x30
    PPEB lpPeb = (PPEB)__readfsdword(0x30);
#endif

    PLIST_ENTRY pListHead = &lpPeb->Ldr->InMemoryOrderModuleList;
    PLIST_ENTRY pListData = pListHead->Flink;

    // 遍历所有模块
    while (pListData != pListHead)
    {
        PLDR_DATA_TABLE_ENTRY pLDRData = CONTAINING_RECORD(pListData, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

        DWORD dwLen = pLDRData->FullDllName.Length / 2;
        if (dwLen > 12)    // 12 是"kernel32.dll"的长度,获取到的完整路径肯定要比模块名长
        {
            // 从获取到的模块完整路径中提取模块名
            for (size_t i = 0; i < 12; i++)
            {
                wszModuleName[11 - i] = pLDRData->FullDllName.Buffer[dwLen - 1 - i];
            }

            // 最终要获取的目标模块名("kernel32.dll"),逐个字节比较,包含大小写。
            if ((wszModuleName[0] == 'k' || wszModuleName[0] == 'K') &&
                (wszModuleName[1] == 'e' || wszModuleName[1] == 'E') &&
                (wszModuleName[2] == 'r' || wszModuleName[2] == 'R') &&
                (wszModuleName[3] == 'n' || wszModuleName[3] == 'N') &&
                (wszModuleName[4] == 'e' || wszModuleName[4] == 'E') &&
                (wszModuleName[5] == 'l' || wszModuleName[5] == 'L') &&
                (wszModuleName[6] == '3') &&
                (wszModuleName[7] == '2') &&
                (wszModuleName[8] == '.') &&
                (wszModuleName[9] == 'd' || wszModuleName[9] == 'D') &&
                (wszModuleName[10] == 'l' || wszModuleName[10] == 'L') &&
                (wszModuleName[11] == 'l' || wszModuleName[11] == 'L'))
            {
                hKernel32 = (HMODULE)pLDRData->DllBase;
                break;
            }
        }
        pListData = pListData->Flink;
    }
    return hKernel32;
}

// 获取GetPorcAddress函数地址
FARPROC CAPI::_GetPorcAddress()
{
    // 保存最终结果
    FARPROC pGetPorcAddress = NULL;

    // kernel32基址
    HMODULE hKernel32 = GetKernel32BaseAddress();
    if (!hKernel32)
    {
        return NULL;
    }

    PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)hKernel32;
    PIMAGE_NT_HEADERS lpNTHeader = (PIMAGE_NT_HEADERS)((unsigned char*)hKernel32 + lpDosHeader->e_lfanew);

    // 模块有效性验证
    if (!lpNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)
    {
        return NULL;
    }
    if (!lpNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)
    {
        return NULL;
    }

    // 通过导出表中的导出函数名,定位"GetProcAddress"的位置
    PIMAGE_EXPORT_DIRECTORY lpExports = (PIMAGE_EXPORT_DIRECTORY)((unsigned char*)hKernel32 + lpNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    PDWORD lpdwFunName = (PDWORD)((unsigned char*)hKernel32 + lpExports->AddressOfNames);
    PWORD lpdwOrd = (PWORD)((unsigned char*)hKernel32 + lpExports->AddressOfNameOrdinals);
    PDWORD lpdwFunAddr = (PDWORD)((unsigned char*)hKernel32 + lpExports->AddressOfFunctions);

    for (DWORD dwLoop = 0; dwLoop <= lpExports->NumberOfNames - 1; dwLoop++)
    {
        char* pFunName = (char*)(lpdwFunName[dwLoop] + (unsigned char*)hKernel32);
        // 比较函数名
        if (
            pFunName[0] == 'G' &&
            pFunName[1] == 'e' &&
            pFunName[2] == 't' &&
            pFunName[3] == 'P' &&
            pFunName[4] == 'r' &&
            pFunName[5] == 'o' &&
            pFunName[6] == 'c' &&
            pFunName[7] == 'A' &&
            pFunName[8] == 'd' &&
            pFunName[9] == 'd' &&
            pFunName[10] == 'r' &&
            pFunName[11] == 'e' &&
            pFunName[12] == 's' &&
            pFunName[13] == 's'
            )
        {
            pGetPorcAddress = (FARPROC)(lpdwFunAddr[lpdwOrd[dwLoop]] + (unsigned char*)hKernel32);
            break;
        }
    }
    return pGetPorcAddress;
}

// 初始化所有用到的函数
void CAPI::InitFunctions()
{
    // 获取GetPorcAddress函数地址
    typedef FARPROC(WINAPI* FN_GetProcAddress)(__in HMODULE hModule, __in LPCSTR lpProcName);
    FN_GetProcAddress fn_GetProcAddress = (FN_GetProcAddress)_GetPorcAddress();
    if (fn_GetProcAddress)
    {
        // 获取LoadLibraryA函数地址
        char szLoadLibraryA[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',0 };
        LoadLibraryA = (FN_LoadLibraryA)fn_GetProcAddress(GetKernel32BaseAddress(), szLoadLibraryA);
        if (LoadLibraryA)
        {
            // 获取MessageBoxA函数地址
            char szUser32[] = { 'U','s','e','r','3','2','.','d','l','l',0 };
            char szMessageBoxA[] = { 'M','e','s','s','a','g','e','B','o','x','A',0 };
            MessageBoxA = (FN_MessageBoxA)fn_GetProcAddress(LoadLibraryA(szUser32), szMessageBoxA);
            
            // 获取CreateFileA函数地址
            char szCreateFileA[] = { 'C','r','e','a','t','e','F','i','l','e','A',0 };
            CreateFileA = (FN_CreateFileA)fn_GetProcAddress(GetKernel32BaseAddress(), szCreateFileA);
        }
    }
}

2. 在CAPI中,使用InitFunctions函数来初始化所有shellcode中用到的函数,在shellcode执行功能处,进行如下调用即可(a.start.cpp):

#include "a.start.h"
#include "shellcode.h"
#include "api.h"

void ShellCodeStart()
{
    CAPI api;
    // 初始化所有用到的函数
    api.InitFunctions();

    CDoShellcode shellcode;

    // 创建文件
    shellcode.DoCreateFile(&api);

    // 弹框提示
    shellcode.DoMessageBox(&api);

    // 其他功能...
}

3. 在类CDoShellcode中,将所有函数功能执行的参数都传递一个CAPI的指针变量,那么所有功能都可以使用CAPI中的函数。比如CDoShellcode中的DoCreateFile方法:

// 功能:创建文件 D:\1.txt
int CDoShellcode::DoCreateFile(CAPI* api)
{
    // 执行动态CreateFileA,创建文件
    char szFilePath[] = { 'D',':','\\','1','.','t','x','t',0 };
    api->CreateFileA(szFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);

    return 0;
}

如此一来,对类CDoShellcode而言,我们只关注功能的实现,不必再顾及函数动态调用的问题。所有用到的动态函数的实现都可以共享CAPI中的实现。

项目结构:

两个类:

  • CDoShellcode(shellcode.h和shellcode.cpp):shellcode执行的各类功能;
  • CAPI(api.h和api.cpp):所有shellcode使用到的动态函数。

    如果有任何问题,可以在我们的知识社群中提问和沟通交流:

    图片​​

    一个人走得再快,不如一群人走得更远!🤜🤛



原文地址:https://blog.csdn.net/Qyears/article/details/138754520

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!