【2021.01.15】注入ShellCode

什么是ShellCode?

不依赖环境,在任何地方都能执行的机器码(硬编码)。

ShellCode的编写原则

  1. 不能有全局变量。
  2. 不能使用常量字符串、
  3. 不能使用系统调用。
  4. 不能嵌套调用其他函数。

ShellCode的问题

由于第 3 点,所以不能直接使用函数,因为直接使用函数在编译时会产生导入表。而将代码复制到其他进程中,其他进程没有需要的导入表,所以会出问题。

那么该怎么解决这个问题呢?

可以不依赖任何导入表得到进程的TEB,其次,通过TEB找到PEB,再找到 3 个链表。

对链表进行遍历,找到需要的模块,如Kernel32.dll、User32.dll等。

找到模块了还不够,因为还需要找到函数在哪里。通过PE相关的知识(知道导出表的结构)遍历模块文件,找到需要的函数地址。

代码

#include <iostream>
#include <Windows.h>

//定义函数指针
typedef int (WINAPI* PMESSAGEBOX)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
typedef FARPROC(WINAPI* PGETPROCADDRESS)(HMODULE hModule, LPCSTR lpProcName);
typedef HMODULE(WINAPI* PLOADLIBRARY)(LPCTSTR lpFileName);

typedef struct _UNICODE_STRING {
    USHORT  Length;
    USHORT  MaximumLength;
    PWSTR   Buffer;
} UNICODE_STRING;

typedef struct _PEB_LDR_DATA {
    DWORD       Length;
    BYTE     Initialized;
    PVOID       SsHandle;
    LIST_ENTRY  InLoadOrderModuleList;
    LIST_ENTRY  InMemoryOrderModuleList;
    LIST_ENTRY  InInitializationOrderModuleList;
} PEB_LDR_DATA;

typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY      InLoadOrderLinks;                   //代表按加载顺序构成的模块链表
    LIST_ENTRY      InMemoryOrderLinks;                 //代表按内存顺序构成的模块链表
    LIST_ENTRY      InInitializationOrderLinks;         //代表按初始化顺序构成的模块链表
    PVOID           DllBase;                            //该模块的基地址
    PVOID           EntryPoint;                         //该模块的入口
    ULONG           SizeOfImage;                        //该模块的映像大小
    UNICODE_STRING  FullDllName;                        //包含路径的模块名称
    UNICODE_STRING  BaseDllName;                        //不包含路径的模块名称
    ULONG           Flags;
    SHORT           LoadCount;                          //该模块的引用计数
    SHORT           TlsIndex;
    HANDLE          SectionHandle;
    ULONG           CheckSum;
    ULONG           TimeDataStamp;

} LDR_DATA_TABLE_ENTRY;

void ShellCode()
{
    LDR_DATA_TABLE_ENTRY* pPld = NULL, * pBeg = NULL;
    PGETPROCADDRESS pGetProcAddress = NULL;
    PMESSAGEBOX pMessageBox = NULL;
    PLOADLIBRARY pLoadLibrary = NULL;
    WORD* pFirst = NULL, * pLast = NULL;
    DWORD ret = 0, i = 0;
    DWORD dwKernelBase = 0;

    //定义自身要使用的DLL、函数
    char szKernel32[] = { 'K',0,'E',0,'R',0,'N',0,'E',0,'L',0,'3',0,'2',0,'.',0,'D',0,'L',0,'L',0,0,0 };
    char szUser32[] = { 'u','s','e','r','3','2','.','d','l','l',0 };

    char szGetProcAddress[] = { 'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0 };
    char szLoadLibrary[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',0 };
    char szMessageBox[] = { 'M','e','s','s','a','g','e','B','o','x','A',0 };

    //获取链表 TEB->PEB->_PEB_LDR_DATA->_LDR_DATA_TABLE_ENTRY
    __asm
    {
        mov eax,fs:[0x30]   //PEB
        mov eax,[eax+0x0c]  //PEB->Ldr
        add eax,0x0c        //_PEB_LDR_DATA->InLoadOrderModuleList
        mov pBeg,eax
        mov eax,[eax]
        mov pPld,eax
    }

    //遍历寻找 Kernel32.dll
    while (pPld!=pBeg)
    {
        pLast = (WORD*)pPld->BaseDllName.Buffer;
        pFirst = (WORD*)szKernel32;
        while (*pFirst && *pFirst == *pLast)
            pFirst++, pLast++;
        if (*pFirst == *pLast)
        {
            dwKernelBase = (DWORD)pPld->DllBase;
            break;
        }

        pPld = (LDR_DATA_TABLE_ENTRY*)pPld->InLoadOrderLinks.Flink;
    }

    //遍历 Kernel32.dll 的导出表 找到 GetProcAddress 函数的地址
    IMAGE_DOS_HEADER* pIDH = (IMAGE_DOS_HEADER*)dwKernelBase;
    IMAGE_NT_HEADERS* pINGS = (IMAGE_NT_HEADERS*)((DWORD)dwKernelBase + pIDH->e_lfanew);
    IMAGE_EXPORT_DIRECTORY* pIED = (IMAGE_EXPORT_DIRECTORY*)((DWORD)dwKernelBase + pINGS->OptionalHeader.DataDirectory[0].VirtualAddress);

    DWORD* pAddOfFun_Raw = (DWORD*)((DWORD)dwKernelBase + pIED->AddressOfFunctions);
    WORD* pAddOfOrd_Raw = (WORD*)((DWORD)dwKernelBase + pIED->AddressOfNameOrdinals);
    DWORD* pAddOfNames_Raw = (DWORD*)((DWORD)dwKernelBase + pIED->AddressOfNames);
    DWORD dwCnt = 0;

    char* pFinded = NULL, * pSrc = szGetProcAddress;
    for (; dwCnt < pIED->NumberOfNames; dwCnt++)
    {
        pFinded = (char*)((DWORD)dwKernelBase + pAddOfNames_Raw[dwCnt]);
        while (*pFinded && *pFinded == *pSrc)
            pFinded++, pSrc++;
        if (*pFinded == *pSrc)
        {
            pGetProcAddress = (PGETPROCADDRESS)((DWORD)dwKernelBase + pAddOfFun_Raw[pAddOfOrd_Raw[dwCnt]]);
            break;
        }

        pSrc = szGetProcAddress;
    }

    //有了 GetProcAddress 就可以得到任何的API函数了
    pLoadLibrary = (PLOADLIBRARY)pGetProcAddress((HMODULE)dwKernelBase, szLoadLibrary);
    pMessageBox = (PMESSAGEBOX)pGetProcAddress(pLoadLibrary((LPCTSTR)szUser32), szMessageBox);

    //使用函数
    char szTitle[] = { 'T','e','s','t',0 };
    char szContent[] = { 'S','h','e','l','l','C','o','d','e','T','e','s','t',0 };
    pMessageBox(NULL, (LPCTSTR)szContent, (LPCTSTR)szTitle, 0);
}

int main()
{
    std::cout << "按任意键继续..." << std::endl;

    std::cin.get();

    ShellCode();

    return 0;
}

效果

猜你喜欢

转载自blog.csdn.net/qq_18120361/article/details/112671324