二进制入门学习笔记-23.shellcode编写

1.Windows shellcode编写原则

  • 1.字符串使用:字符串使用的时候不要使用双引号字符串,否则变量会放在常量区。字符串最后最好放一个0x00
  • 2.加载模块:加载模块从PEB获取当前进程空间kernel32.dll基址,然后遍历kernel32.dll的导出表获取LoadLibraryAGetProcAddress函数地址。
  • 3.项目属性中进行如下配置:
    • C/C++ --> 常规 --> SDL检查:否
    • C/C++ --> 优化 --> 优化 :最大优化(优化大小)
    • C/C++ --> 代码生成 --> 安全检查 :禁用安全检查(/GS-)
    • 链接器 --> 清单文件 --> 生成清单:否
    • 链接器 --> 调试 --> 生成调试信息:否
    • 链接器 --> 高级 --> 入口点 :自己重新定义个入口点函数名称

2.编译后生成代码的顺序

注意一定要让shellcode的入口点在提取到的shellcode最前面。

  • 1.同文件中:同文件中,多个函数按照实现顺序排序
  • 2.不同文件中:不同文件中,按照如下图所示,项目配置文件中的顺序进行排序

3.代码样例

以下代码中实现了一段执行messageBox的shellcode。代码如下:

#include <windows.h>
#include <winternl.h>

typedef struct detail_LDR_DATA_TABLE_ENTRY{
   LIST_ENTRY InLoadOrderLinks;
   LIST_ENTRY InMemoryOrderLinks;
   LIST_ENTRY InInitializationOrderLinks;
   PVOID DllBase;
   PVOID EntryPoint;
   DWORD SizeOfImage;
   UNICODE_STRING FullDllName;
   UNICODE_STRING BaseDllName;

}detail_LDR_DATA_TABLE_ENTRY,*Pdetail_LDR_DATA_TABLE_ENTRY;

typedef HMODULE (WINAPI *pLoadLibraryA)(_In_ LPCSTR lpLibFileName);
typedef WINBASEAPI FARPROC (WINAPI *pGetProcAddress)(_In_ HMODULE hModule,_In_ LPCSTR lpProcName);
typedef int (WINAPI* pMessageBoxA)(_In_opt_ HWND hWnd,_In_opt_ LPCSTR lpText,_In_opt_ LPCSTR lpCaption,_In_ UINT uType);
void* getFuncAddrFromKernel32(char* targetFuncName);

int test() {
    //从kernel32中获取loadLibrary和GetProcAddress
    char loadLibraryAName[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',0x00 };
    void* loadLibraryFuncAddr = getFuncAddrFromKernel32(loadLibraryAName);

    char getProcAddrName[] = { 'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0x00 };;
    void* GetProcAddressFuncAddr = getFuncAddrFromKernel32(getProcAddrName);

    //调用LoadLibraryA加载想用的dll
    pLoadLibraryA myLoadLibrary = (pLoadLibraryA)loadLibraryFuncAddr;
    char dllName[] = { 'u','s','e','r','3','2','\.','d','l','l',0x00 };
    HMODULE user32Handler = myLoadLibrary(dllName);

    //调用GetProcAddress获取方法地址
    char funcName[] = { 'M','e','s','s','a','g','e','B','o','x','A',0x00 };
    pGetProcAddress myGetProcAddress = (pGetProcAddress)GetProcAddressFuncAddr;

    //调用目标方法
    pMessageBoxA myMessageBoxA = pMessageBoxA(myGetProcAddress(user32Handler, funcName));
    char title[] = { 't','i','t','l','e',0x00 };
    char msg[] = { 'm','e','s','s','a','g','e',0x00 };
    myMessageBoxA(NULL, msg, title, NULL);

    return 0;
}

void* getFuncAddrFromKernel32(char* targetFuncName) {
    //获取PEB指针
    PPEB pebPtr = (PPEB)__readfsdword(0x0C * sizeof(PVOID));
    //获取PEB结构块的PEB_LDR_DATA
    PPEB_LDR_DATA pldr = pebPtr->Ldr;
    //链表头节点、尾节点;
    PLIST_ENTRY pListEntryNow = NULL;
    PLIST_ENTRY pListEntryEnd = NULL;
    pListEntryNow = pldr->InMemoryOrderModuleList.Flink;
    pListEntryEnd = pldr->InMemoryOrderModuleList.Flink;
    Pdetail_LDR_DATA_TABLE_ENTRY pNowLdrDataEntry = NULL;
    PIMAGE_DOS_HEADER kernel32Base = NULL;//用于保存kernel32的基址
    //循环遍历以加载模块
    do
    {
        pNowLdrDataEntry = (Pdetail_LDR_DATA_TABLE_ENTRY)CONTAINING_RECORD(pListEntryNow, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
        wchar_t name[] = { 'K','E','R','N','E','L','3','2',0x00 };
        DWORD res = 0;
        wchar_t* BaseDllName = NULL;
        BaseDllName = (wchar_t*)pNowLdrDataEntry->BaseDllName.Buffer;

        //对比dll名称
        wchar_t* psrc = name;
        wchar_t* pdst = BaseDllName;
        res = 1;
        while (*psrc) {

            if (*psrc != *pdst) {
                res = 0;
                break;
            }
            else {
                psrc++;
                pdst++;
            }
        }

        if (res != 0) {
            kernel32Base = (PIMAGE_DOS_HEADER)pNowLdrDataEntry->DllBase;
            break;
        }
        pListEntryNow = pNowLdrDataEntry->InMemoryOrderLinks.Flink;
    } while (1);
    //获取kernerl32 NT头
    PIMAGE_NT_HEADERS kernel32IMAGE_NT_HEADERS = NULL;
    kernel32IMAGE_NT_HEADERS = PIMAGE_NT_HEADERS(((char*)kernel32Base) + kernel32Base->e_lfanew);
    //获取导出表RVA
    IMAGE_DATA_DIRECTORY pExportImgDataDir = kernel32IMAGE_NT_HEADERS->OptionalHeader.DataDirectory[0];
    //获取导出表
    PIMAGE_EXPORT_DIRECTORY pimgExportDir = (PIMAGE_EXPORT_DIRECTORY)((char*)kernel32Base + pExportImgDataDir.VirtualAddress);
    int targetFuncIndexAddressOfNames = NULL;

    //遍历导出名称表,获取序号
    for (int i = 0; i < pimgExportDir->NumberOfNames; i++) {
        PDWORD  nameAddr = (PDWORD)((DWORD)kernel32Base + pimgExportDir->AddressOfNames);
        char* currentName = (char*)kernel32Base + nameAddr[i];
        ////printf("正在对比:%s\n", currentName);
        char* psrc = targetFuncName;
        char* pdst = currentName;
        DWORD res = 1;
        while (*psrc) {
            if (*psrc != *pdst) {
                res = 0;
                break;
            }
            else {
                psrc++;
                pdst++;
            }
        }
        if (res != 0) {
            targetFuncIndexAddressOfNames = i;
            break;
        }
    }
    //根据函数序号表查函数序号表
    int targetFuncIndexInAddressOfNameOrdinals = NULL;
    if (targetFuncIndexAddressOfNames != NULL) {
        PWORD AddressOfNameOrdinals = (PWORD)((DWORD)kernel32Base + pimgExportDir->AddressOfNameOrdinals);
        targetFuncIndexInAddressOfNameOrdinals = AddressOfNameOrdinals[targetFuncIndexAddressOfNames];
    }

    PVOID targetFuncAddr = NULL;
    //根据序号查函数地址表获取函数地址
    if (targetFuncIndexInAddressOfNameOrdinals) {
        PDWORD AddressOfFunctions = (PDWORD)((DWORD)kernel32Base + pimgExportDir->AddressOfFunctions);
        targetFuncAddr = (PVOID)((DWORD)kernel32Base + AddressOfFunctions[targetFuncIndexInAddressOfNameOrdinals]);
    }
    return targetFuncAddr;
}

编译后的代码中,直接使用winhex提取text段即可。

然后复制到加载器中测试即可。