#include "FixRelocation.h"



/*
重定位表  修复
*/
BOOLEAN
    FixBaseRelocTable (
    PVOID NewImageBase,
    DWORD ExistImageBase
    )
{
    LONGLONG Diff;
    ULONG TotalCountBytes = 0;
    ULONG_PTR VA;
    ULONGLONG OriginalImageBase;
    ULONG SizeOfBlock;
    PUCHAR FixupVA;
    USHORT Offset;
    PUSHORT NextOffset = NULL;
    PIMAGE_NT_HEADERS NtHeaders;
    PIMAGE_BASE_RELOCATION NextBlock;


    NtHeaders = RtlImageNtHeader( NewImageBase );
    if (NtHeaders == NULL) 
    {
        return FALSE;
    }

    switch (NtHeaders->OptionalHeader.Magic) {

    case IMAGE_NT_OPTIONAL_HDR32_MAGIC:

        OriginalImageBase =
            ((PIMAGE_NT_HEADERS32)NtHeaders)->OptionalHeader.ImageBase;
        break;

    case IMAGE_NT_OPTIONAL_HDR64_MAGIC:

        OriginalImageBase =
            ((PIMAGE_NT_HEADERS64)NtHeaders)->OptionalHeader.ImageBase;
        break;

    default:
        return FALSE;
    }

    //
    // Locate the relocation section.
    //

    NextBlock = (PIMAGE_BASE_RELOCATION)RtlImageDirectoryEntryToData(
        NewImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &TotalCountBytes);

    //
    // It is possible for a file to have no relocations, but the relocations
    // must not have been stripped.
    //

    if (!NextBlock || !TotalCountBytes) 
    {

        if (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) 
        {
            DbgPrint("Image can't be relocated, no fixup information.\n");
            return FALSE;

        }
        else 
        {
            return TRUE;
        }

    }

    //
    // If the image has a relocation table, then apply the specified fixup
    // information to the image.
    //
    Diff = (ULONG_PTR)ExistImageBase - OriginalImageBase;
    while (TotalCountBytes)
    {
        SizeOfBlock = NextBlock->SizeOfBlock;
        TotalCountBytes -= SizeOfBlock;
        SizeOfBlock -= sizeof(IMAGE_BASE_RELOCATION);
        SizeOfBlock /= sizeof(USHORT);
        NextOffset = (PUSHORT)((PCHAR)NextBlock + sizeof(IMAGE_BASE_RELOCATION));

        VA = (ULONG_PTR)NewImageBase + NextBlock->VirtualAddress;

        if ( !(NextBlock = LdrProcessRelocationBlockLongLong( VA,
            SizeOfBlock,
            NextOffset,
            Diff)) ) 
        {

            DbgPrint("%s: Unknown base relocation type\n");
            return FALSE;

        }
    }

    return TRUE;
}



/*修复重定位表*/
PIMAGE_BASE_RELOCATION
    LdrProcessRelocationBlockLongLong(
    IN ULONG_PTR VA,
    IN ULONG SizeOfBlock,
    IN PUSHORT NextOffset,
    IN LONGLONG Diff
    )
{
    PUCHAR FixupVA;
    USHORT Offset;
    LONG Temp;
    ULONG Temp32;
    ULONGLONG Value64;
    LONGLONG Temp64;



    while (SizeOfBlock--) {

        Offset = *NextOffset & (USHORT)0xfff;
        FixupVA = (PUCHAR)(VA + Offset);

        //
        // Apply the fixups.
        //

        switch ((*NextOffset) >> 12) {

        case IMAGE_REL_BASED_HIGHLOW :
            //
            // HighLow - (32-bits) relocate the high and low half
            //      of an address.
            //
            *(LONG UNALIGNED *)FixupVA += (ULONG) Diff;
            break;

        case IMAGE_REL_BASED_HIGH :
            //
            // High - (16-bits) relocate the high half of an address.
            //
            Temp = *(PUSHORT)FixupVA << 16;
            Temp += (ULONG) Diff;
            *(PUSHORT)FixupVA = (USHORT)(Temp >> 16);
            break;

        case IMAGE_REL_BASED_HIGHADJ :
            //
            // Adjust high - (16-bits) relocate the high half of an
            //      address and adjust for sign extension of low half.
            //

            //
            // If the address has already been relocated then don't
            // process it again now or information will be lost.
            //
            if (Offset & LDRP_RELOCATION_FINAL) {
                ++NextOffset;
                --SizeOfBlock;
                break;
            }

            Temp = *(PUSHORT)FixupVA << 16;
            ++NextOffset;
            --SizeOfBlock;
            Temp += (LONG)(*(PSHORT)NextOffset);
            Temp += (ULONG) Diff;
            Temp += 0x8000;
            *(PUSHORT)FixupVA = (USHORT)(Temp >> 16);

            break;

        case IMAGE_REL_BASED_LOW :
            //
            // Low - (16-bit) relocate the low half of an address.
            //
            Temp = *(PSHORT)FixupVA;
            Temp += (ULONG) Diff;
            *(PUSHORT)FixupVA = (USHORT)Temp;
            break;

        case IMAGE_REL_BASED_IA64_IMM64:

            //
            // Align it to bundle address before fixing up the
            // 64-bit immediate value of the movl instruction.
            //

            FixupVA = (PUCHAR)((ULONG_PTR)FixupVA & ~(15));
            Value64 = (ULONGLONG)0;

            //
            // Extract the lower 32 bits of IMM64 from bundle
            //


            EXT_IMM64(Value64,
                (PULONG)FixupVA + EMARCH_ENC_I17_IMM7B_INST_WORD_X,
                EMARCH_ENC_I17_IMM7B_SIZE_X,
                EMARCH_ENC_I17_IMM7B_INST_WORD_POS_X,
                EMARCH_ENC_I17_IMM7B_VAL_POS_X);
            EXT_IMM64(Value64,
                (PULONG)FixupVA + EMARCH_ENC_I17_IMM9D_INST_WORD_X,
                EMARCH_ENC_I17_IMM9D_SIZE_X,
                EMARCH_ENC_I17_IMM9D_INST_WORD_POS_X,
                EMARCH_ENC_I17_IMM9D_VAL_POS_X);
            EXT_IMM64(Value64,
                (PULONG)FixupVA + EMARCH_ENC_I17_IMM5C_INST_WORD_X,
                EMARCH_ENC_I17_IMM5C_SIZE_X,
                EMARCH_ENC_I17_IMM5C_INST_WORD_POS_X,
                EMARCH_ENC_I17_IMM5C_VAL_POS_X);
            EXT_IMM64(Value64,
                (PULONG)FixupVA + EMARCH_ENC_I17_IC_INST_WORD_X,
                EMARCH_ENC_I17_IC_SIZE_X,
                EMARCH_ENC_I17_IC_INST_WORD_POS_X,
                EMARCH_ENC_I17_IC_VAL_POS_X);
            EXT_IMM64(Value64,
                (PULONG)FixupVA + EMARCH_ENC_I17_IMM41a_INST_WORD_X,
                EMARCH_ENC_I17_IMM41a_SIZE_X,
                EMARCH_ENC_I17_IMM41a_INST_WORD_POS_X,
                EMARCH_ENC_I17_IMM41a_VAL_POS_X);

            EXT_IMM64(Value64,
                ((PULONG)FixupVA + EMARCH_ENC_I17_IMM41b_INST_WORD_X),
                EMARCH_ENC_I17_IMM41b_SIZE_X,
                EMARCH_ENC_I17_IMM41b_INST_WORD_POS_X,
                EMARCH_ENC_I17_IMM41b_VAL_POS_X);
            EXT_IMM64(Value64,
                ((PULONG)FixupVA + EMARCH_ENC_I17_IMM41c_INST_WORD_X),
                EMARCH_ENC_I17_IMM41c_SIZE_X,
                EMARCH_ENC_I17_IMM41c_INST_WORD_POS_X,
                EMARCH_ENC_I17_IMM41c_VAL_POS_X);
            EXT_IMM64(Value64,
                ((PULONG)FixupVA + EMARCH_ENC_I17_SIGN_INST_WORD_X),
                EMARCH_ENC_I17_SIGN_SIZE_X,
                EMARCH_ENC_I17_SIGN_INST_WORD_POS_X,
                EMARCH_ENC_I17_SIGN_VAL_POS_X);
            //
            // Update 64-bit address
            //

            Value64+=Diff;

            //
            // Insert IMM64 into bundle
            //

            INS_IMM64(Value64,
                ((PULONG)FixupVA + EMARCH_ENC_I17_IMM7B_INST_WORD_X),
                EMARCH_ENC_I17_IMM7B_SIZE_X,
                EMARCH_ENC_I17_IMM7B_INST_WORD_POS_X,
                EMARCH_ENC_I17_IMM7B_VAL_POS_X);
            INS_IMM64(Value64,
                ((PULONG)FixupVA + EMARCH_ENC_I17_IMM9D_INST_WORD_X),
                EMARCH_ENC_I17_IMM9D_SIZE_X,
                EMARCH_ENC_I17_IMM9D_INST_WORD_POS_X,
                EMARCH_ENC_I17_IMM9D_VAL_POS_X);
            INS_IMM64(Value64,
                ((PULONG)FixupVA + EMARCH_ENC_I17_IMM5C_INST_WORD_X),
                EMARCH_ENC_I17_IMM5C_SIZE_X,
                EMARCH_ENC_I17_IMM5C_INST_WORD_POS_X,
                EMARCH_ENC_I17_IMM5C_VAL_POS_X);
            INS_IMM64(Value64,
                ((PULONG)FixupVA + EMARCH_ENC_I17_IC_INST_WORD_X),
                EMARCH_ENC_I17_IC_SIZE_X,
                EMARCH_ENC_I17_IC_INST_WORD_POS_X,
                EMARCH_ENC_I17_IC_VAL_POS_X);
            INS_IMM64(Value64,
                ((PULONG)FixupVA + EMARCH_ENC_I17_IMM41a_INST_WORD_X),
                EMARCH_ENC_I17_IMM41a_SIZE_X,
                EMARCH_ENC_I17_IMM41a_INST_WORD_POS_X,
                EMARCH_ENC_I17_IMM41a_VAL_POS_X);
            INS_IMM64(Value64,
                ((PULONG)FixupVA + EMARCH_ENC_I17_IMM41b_INST_WORD_X),
                EMARCH_ENC_I17_IMM41b_SIZE_X,
                EMARCH_ENC_I17_IMM41b_INST_WORD_POS_X,
                EMARCH_ENC_I17_IMM41b_VAL_POS_X);
            INS_IMM64(Value64,
                ((PULONG)FixupVA + EMARCH_ENC_I17_IMM41c_INST_WORD_X),
                EMARCH_ENC_I17_IMM41c_SIZE_X,
                EMARCH_ENC_I17_IMM41c_INST_WORD_POS_X,
                EMARCH_ENC_I17_IMM41c_VAL_POS_X);
            INS_IMM64(Value64,
                ((PULONG)FixupVA + EMARCH_ENC_I17_SIGN_INST_WORD_X),
                EMARCH_ENC_I17_SIGN_SIZE_X,
                EMARCH_ENC_I17_SIGN_INST_WORD_POS_X,
                EMARCH_ENC_I17_SIGN_VAL_POS_X);
            break;

        case IMAGE_REL_BASED_DIR64:

            *(ULONGLONG UNALIGNED *)FixupVA += Diff;

            break;

        case IMAGE_REL_BASED_MIPS_JMPADDR :
            //
            // JumpAddress - (32-bits) relocate a MIPS jump address.
            //
            Temp = (*(PULONG)FixupVA & 0x3ffffff) << 2;
            Temp += (ULONG) Diff;
            *(PULONG)FixupVA = (*(PULONG)FixupVA & ~0x3ffffff) |
                ((Temp >> 2) & 0x3ffffff);

            break;

        case IMAGE_REL_BASED_ABSOLUTE :
            //
            // Absolute - no fixup required.
            //
            break;

        case IMAGE_REL_BASED_SECTION :
            //
            // Section Relative reloc.  Ignore for now.
            //
            break;

        case IMAGE_REL_BASED_REL32 :
            //
            // Relative intrasection. Ignore for now.
            //
            break;

        default :
            //
            // Illegal - illegal relocation type.
            //

            return (PIMAGE_BASE_RELOCATION)NULL;
        }
        ++NextOffset;
    }
    return (PIMAGE_BASE_RELOCATION)NextOffset;
}


/*
获得NtHeader
*/
NTSTATUS
    NTAPI
    RtlImageNtHeaderEx(
    ULONG Flags,
    PVOID Base,
    ULONG64 Size,
    OUT PIMAGE_NT_HEADERS * OutHeaders
    )

{
    PIMAGE_NT_HEADERS NtHeaders = 0;
    ULONG e_lfanew = 0;
    BOOLEAN RangeCheck = 0;
    NTSTATUS Status = 0;
    const ULONG ValidFlags = 
        RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK;

    if (OutHeaders != NULL) {
        *OutHeaders = NULL;
    }
    if (OutHeaders == NULL) {
        Status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }
    if ((Flags & ~ValidFlags) != 0) {
        Status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }
    if (Base == NULL || Base == (PVOID)(LONG_PTR)-1) {
        Status = STATUS_INVALID_PARAMETER;
        goto Exit;
    }

    RangeCheck = ((Flags & RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK) == 0);
    if (RangeCheck) {
        if (Size < sizeof(IMAGE_DOS_HEADER)) {
            Status = STATUS_INVALID_IMAGE_FORMAT;
            goto Exit;
        }
    }

    //
    // Exception handling is not available in the boot loader, and exceptions
    // were not historically caught here in kernel mode. Drivers are considered
    // trusted, so we can't get an exception here due to a bad file, but we
    // could take an inpage error.
    //
#define EXIT goto Exit
    if (((PIMAGE_DOS_HEADER)Base)->e_magic != IMAGE_DOS_SIGNATURE) {
        Status = STATUS_INVALID_IMAGE_FORMAT;
        EXIT;
    }
    e_lfanew = ((PIMAGE_DOS_HEADER)Base)->e_lfanew;
    if (RangeCheck) {
        if (e_lfanew >= Size
#define SIZEOF_PE_SIGNATURE 4
            || e_lfanew >= (MAXULONG - SIZEOF_PE_SIGNATURE - sizeof(IMAGE_FILE_HEADER))
            || (e_lfanew + SIZEOF_PE_SIGNATURE + sizeof(IMAGE_FILE_HEADER)) >= Size
            ) {
                Status = STATUS_INVALID_IMAGE_FORMAT;
                EXIT;
        }
    }

    NtHeaders = (PIMAGE_NT_HEADERS)((PCHAR)Base + e_lfanew);

    //
    // In kernelmode, do not cross from usermode address to kernelmode address.
    //
    if (Base < MM_HIGHEST_USER_ADDRESS) {
        if ((PVOID)NtHeaders >= MM_HIGHEST_USER_ADDRESS) {
            Status = STATUS_INVALID_IMAGE_FORMAT;
            EXIT;
        }
        //
        // Note that this check is slightly overeager since IMAGE_NT_HEADERS has
        // a builtin array of data_directories that may be larger than the image
        // actually has. A better check would be to add FileHeader.SizeOfOptionalHeader,
        // after ensuring that the FileHeader does not cross the u/k boundary.
        //
        if ((PVOID)((PCHAR)NtHeaders + sizeof (IMAGE_NT_HEADERS)) >= MM_HIGHEST_USER_ADDRESS) {
            Status = STATUS_INVALID_IMAGE_FORMAT;
            EXIT;
        }
    }

    if (NtHeaders->Signature != IMAGE_NT_SIGNATURE) {
        Status = STATUS_INVALID_IMAGE_FORMAT;
        EXIT;
    }
    Status = STATUS_SUCCESS;

Exit:
    if (NT_SUCCESS(Status)) {
        *OutHeaders = NtHeaders;
    }
    return Status;
}


PIMAGE_NT_HEADERS
    NTAPI
    RtlImageNtHeader(
    PVOID Base
    )
{
    PIMAGE_NT_HEADERS NtHeaders = NULL;
    (VOID)RtlImageNtHeaderEx(RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK, Base, 0, &NtHeaders);
    return NtHeaders;
}


