심플스 - 프로그램과 책 이야기로 가득한 곳, (Simples.Kr)

  


   심플스 배너



커널레벨에서 IAT Hook

Windows Research 조회 수 4348 추천 수 0 2011.06.11 10:13:10
 

오래전에 작업하고 더 이상 쓰지 않고 놔뒹굴고 있어서 이곳에 정리합니다.

32Bit, 64Bit 둘 다 정상적으로 동작합니다.


물론 64Bit에서는 패치가드가 보호하는 모듈은 후킹을 시도하지 말아야겠죠.


후킹하려는 대상 모듈이 scsiport.sys이고 이 모듈이 ntoskrnl!IoCreateSymbolicLink 함수를 호출하는 것을

모두 후킹하고 싶다면 다음과 같이 코드를 작성합니다.


#define SCSIPORT_SYS_FILEPATH L"\\SystemRoot\\System32\\drivers\\scsiport.sys"
#define SCSIPORT_SYS_FILENAME "ScsiPort.sys"


PVOID pScsiPortImageAddress;

ULONG uIoCreateSymbolicLinkRVA;
ULONG uImageSize;


// 전체 파일경로로부터 특정 모듈에 대한 IAT 함수(IoCreateSymbolicLink) RVA를 가져옵니다.
uIoCreateSymbolicLinkRVA = IATFunctionRVAFromFile(SCSIPORT_SYS_FILEPATH, "ntoskrnl", "IoCreateSymbolicLink");
if (uIoCreateSymbolicLinkRVA == 0)
{
    KdPrint(("HookIoCreateSymbolicLinkHookForScsiPort : IATFunctionRVAFromFile fail.\n"));

    return ;
}

// 후킹하는 모듈에 메모리 주소를 가져옵니다.
pScsiPortImageAddress = GetImageAddress(SCSIPORT_SYS_FILENAME, &uImageSize);
if (pScsiPortImageAddress == NULL)
{
    KdPrint(("HookIoCreateSymbolicLinkHookForScsiPort : GetImageAddress fail.\n"));

    return ;
}


// RVA가 이미지 크기보다 크다면 잘 못 된 값을 가져온 것입니다.
if (uIoCreateSymbolicLinkRVA >= uImageSize)
{
    KdPrint(("HookIoCreateSymbolicLinkHookForScsiPort : uIoCreateSymbolicLinkRVA >= uImageSize\n"));

    return ;
}


// IAT 후킹을 하며, Scsiport.sys에서 IoCreateSymbolicLink 함수를 호출 시 DummyIoCreateSymbolicLink함수가 호출되게 됩니다.
ExchangeProtectedIATPointer((PVOID)((ULONG_PTR)pScsiPortImageAddress + (ULONG_PTR)uIoCreateSymbolicLinkRVA), DummyIoCreateSymbolicLink);


NTSTATUS
  DummyIoCreateSymbolicLink(
    IN PUNICODE_STRING  SymbolicLinkName,
    IN PUNICODE_STRING  DeviceName
    )
{
    NTSTATUS ntStatus;

    KdPrint(("hi DummyIoCreateSymbolicLink :)\n"));
    ntStatus = MyIoCreateUnprotectedSymbolicLink(SymbolicLinkName, DeviceName);
    if (NT_SUCCESS(ntStatus))
    {
        KdPrint(("Successed.\n"));
    }

    return ntStatus;
}


/////////////////////////////////////////////////////////////////////////////////////////

// 여기서부터 불러다 쓰면 되는 함수가 정의되어 있습니다.

/////////////////////////////////////////////////////////////////////////////////////////


ULONG IATFunctionRVAFromFile(WCHAR *FileName, UCHAR *ModuleName, UCHAR *FunctionName)
{
    NTSTATUS ntStatus;

    HANDLE FileHandle;

    IMAGE_DOS_HEADER DosHeader;
    //
    // 참고 !! : NtImage.h 헤더를 Include하였고 내부적으로 64비트인지 32비트인지에 따라서
    // IMAGE_NT_HEADERS는 IMAGE_NT_HEADERS32, IMAGE_NT_HEADERS64로 나누어집니다.
    //
    IMAGE_NT_HEADERS NTHeader;

    IMAGE_SECTION_HEADER ImageSectionHeader;

    IMAGE_IMPORT_DESCRIPTOR ImageImportDescriptor;

    PUCHAR pImageImportByNameBuffer;

    ULONG ImageImportByNameBufferLength;
    ULONG FunctionNameLength;

    PUCHAR pModuleNameBuffer;
    ULONG ModuleNameLength;

    ULONG ImportTableRVA;
    ULONG ImportNameTableRVA;

    // ImportNameTable 각 배열의 크기는 32비트 PE Format에 경우 32비트이며 64비트에 경우 64비트입니다.
    ULONG_PTR ImportNameTable;

    LARGE_INTEGER Offset;
    LARGE_INTEGER SectionHeaderOffset;
    LARGE_INTEGER ImportDescriptorOffset;
    LARGE_INTEGER ImportNameTableOffset;

    int i;
    int j;
    int iCount;

    if (FileName == NULL)
    {
        return 0;
    }
    if (ModuleName == NULL)
    {
        return 0;
    }
    if (FunctionName == NULL)
    {
        return 0;
    }

    FunctionNameLength = strlen(FunctionName);
    if (FunctionNameLength == 0)
    {
        return 0;
    }
    ModuleNameLength = strlen(ModuleName);
    if (ModuleNameLength == 0)
    {
        return 0;
    }

    ntStatus = IoOpenNormalFile(FileName, &FileHandle);
    if (NT_SUCCESS(ntStatus) == FALSE)
    {
        return 0;
    }
    if (FileHandle == NULL)
    {
        return 0;
    }

    ImageImportByNameBufferLength = FunctionNameLength + sizeof(USHORT);

    pImageImportByNameBuffer = ExAllocatePoolWithTag(PagedPool, ImageImportByNameBufferLength, POOL_TAG);
    if (pImageImportByNameBuffer == NULL)
    {
        ZwClose(FileHandle);
        return 0;
    }

    pModuleNameBuffer  = ExAllocatePoolWithTag(PagedPool, ModuleNameLength, POOL_TAG);
    if (pModuleNameBuffer == NULL)
    {
        ExFreePoolWithTag(pImageImportByNameBuffer, 0);
        ZwClose(FileHandle);

        return 0;
    }

    __try
    {
        Offset.QuadPart = 0x00;
        if (IoReadNormalFile(FileHandle, &DosHeader, &Offset, sizeof(DosHeader)) == FALSE)
        {
            return 0;
        }

        // 올바른 시그내쳐를 갖고 있는지 확인합니다.
        if (DosHeader.e_magic != 0x5A4D)
        {
            KdPrint(("Invalid dos_header.\n"));

            return 0;
        }

        // NTHeader 파일 오프셋을 구합니다.
        Offset.QuadPart = DosHeader.e_lfanew;
        if (IoReadNormalFile(FileHandle, &NTHeader, &Offset, sizeof(NTHeader)) == FALSE)
        {
            return 0;
        }

        // 올바른 시그내쳐를 갖고 있는지 확인합니다.
        if (NTHeader.Signature != 0x00004550)
        {
            KdPrint(("Invalid nt_header\n"));

            return 0;
        }

        if (NTHeader.FileHeader.NumberOfSections == 0)
        {
            // 섹션의 개수가 0입니다.
            KdPrint(("Invalid NumberOfSections\n"));

            return 0;
        }

        // Import Table에 RVA 주소를 저장합니다.
        ImportTableRVA = NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;

        SectionHeaderOffset.QuadPart = Offset.QuadPart + sizeof(NTHeader);
        for (i = 0; i < (int)NTHeader.FileHeader.NumberOfSections; i++)
        {
            if (IoReadNormalFile(FileHandle, &ImageSectionHeader, &SectionHeaderOffset, sizeof(ImageSectionHeader)) == FALSE)
            {
                return 0;
            }

            if ((NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress >= ImageSectionHeader.VirtualAddress)
                && (NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress < (ImageSectionHeader.VirtualAddress + ImageSectionHeader.Misc.VirtualSize)))
            {
                // 임포트 테이블에 해당하는 섹션을 찾았습니다.

                // IMAGE_IMPORT_DESCRIPTOR 구조체가 있는 파일 오프셋을 구합니다.
                ImportDescriptorOffset.QuadPart = NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress - ImageSectionHeader.VirtualAddress + ImageSectionHeader.PointerToRawData;
               
                for (j = 0; j < 10; j++)
                {
                    if (IoReadNormalFile(FileHandle, &ImageImportDescriptor, &ImportDescriptorOffset, sizeof(ImageImportDescriptor)) == FALSE)
                    {
                        return 0;
                    }
                    if (ImageImportDescriptor.OriginalFirstThunk == 0x00000000)
                    {
                        // NULL 임포트 디스크립터라면 임포트 테이블을 모두 순회한 것입니다.

                        return 0;
                    }

                    // 라이브러리 이름 RVA 주소에 대한 파일 오프셋을 구합니다.
                    Offset.QuadPart = ImageImportDescriptor.Name - ImageSectionHeader.VirtualAddress + ImageSectionHeader.PointerToRawData;
                    if (IoReadNormalFile(FileHandle, pModuleNameBuffer, &Offset, ModuleNameLength) == FALSE)
                    {
                        return 0;
                    }

                    if (memcmp(pModuleNameBuffer, ModuleName, ModuleNameLength) == 0)
                    {
                        // ntoskrnl 이 맞다면 원하는 임포트 디스크립터 테이블을 찾은 것입니다. ( RVA 주소에 대한 파일 오프셋을 구합니다. )
                        ImportNameTableOffset.QuadPart = ImageImportDescriptor.OriginalFirstThunk - ImageSectionHeader.VirtualAddress + ImageSectionHeader.PointerToRawData;
                        iCount = 0;
                        while (TRUE)
                        {
                            if (IoReadNormalFile(FileHandle, &ImportNameTable, &ImportNameTableOffset, sizeof(ImportNameTable)) == FALSE)
                            {
                                break;
                            }
                            if (ImportNameTable == 0)
                            {
                                // 값이 0 이라면 모두 순회한 것입니다.
                                break;
                            }
                            // IMAGE_IMPORT_BY_NAME 오프셋을 구합니다. ( RVA 주소에 대한 파일 오프셋을 구합니다.
                            Offset.QuadPart = ImportNameTable - ImageSectionHeader.VirtualAddress + ImageSectionHeader.PointerToRawData;
                            if (IoReadNormalFile(FileHandle, pImageImportByNameBuffer, &Offset, ImageImportByNameBufferLength) == FALSE)
                            {
                                break;
                            }
                            if (memcmp(pImageImportByNameBuffer + sizeof(USHORT), FunctionName, FunctionNameLength) == 0)
                            {
                                // 찾았습니다.
                                return (ImageImportDescriptor.FirstThunk + (iCount *  sizeof(PVOID))/* + NTHeader.OptionalHeader.ImageBase*/);
                            }

                            ImportNameTableOffset.QuadPart += sizeof(ImportNameTable);
                            iCount++;
                        }

                        // 위에 while 문으로터 검색에 실패하였습니다.
                        return 0;
                    }

                    // 다음 임포트 디스크립터를 검색합니다.
                    ImportDescriptorOffset.QuadPart += sizeof(ImageImportDescriptor);
                }

                // 위에서 루프를 돌았지만 검색에 실패하였습니다.
                return 0;
            }

            // 다음 섹션을 검사합니다.
            SectionHeaderOffset.QuadPart += sizeof(ImageSectionHeader);
        }
    }
    __finally
    {
        if (pModuleNameBuffer != NULL)
        {
            ExFreePoolWithTag(pModuleNameBuffer, POOL_TAG);
        }
        if (pImageImportByNameBuffer != NULL)
        {
            ExFreePoolWithTag(pImageImportByNameBuffer, POOL_TAG);
        }
        ZwClose(FileHandle);
    }

    return 0;
}


PVOID GetImageAddress(CHAR *ImageFileName, ULONG *pImageSize)
{
    NTSTATUS Status;
    PRTL_PROCESS_MODULES ModuleInfo;
    PRTL_PROCESS_MODULE_INFORMATION ModuleEntry;
    ULONG ReturnedLength;
    ULONG i;

    PVOID pImageAddress;

    //
    // Figure out how much size we need
    //
    Status = ZwQuerySystemInformation(SystemModuleInformation,
                                      NULL,
                                      0,
                                      &ReturnedLength);
    if (Status != STATUS_INFO_LENGTH_MISMATCH) return NULL;

    //
    // Allocate a buffer large enough
    //
    //ModuleInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, ReturnedLength);
    ModuleInfo = (PRTL_PROCESS_MODULES)ExAllocatePoolWithTag(NonPagedPool, ReturnedLength, POOL_TAG);
    if (!ModuleInfo) return NULL;

    //
    // Now query the data again
    //
    Status = ZwQuerySystemInformation(SystemModuleInformation,
                                      ModuleInfo,
                                      ReturnedLength,
                                      &ReturnedLength);
    if (!NT_SUCCESS(Status))
    {
        ExFreePoolWithTag(ModuleInfo, POOL_TAG);

        return NULL;
    }

    //
    // Loop all the drivers
    //
    for (i = 0; i < ModuleInfo->NumberOfModules; i++)
    {
        //
        // Get the current entry and check if the pointer is within it
        //
        ModuleEntry = &ModuleInfo->Modules[i];
        if (_stricmp(ModuleEntry->FullPathName + ModuleEntry->OffsetToFileName, ImageFileName) == 0)
        {
            // 주소를 미리 얻은 후에 ExFreePoolWithTag 함수를 호출해야 합니다.
            // ( 그렇지 않으면 블루 스크린이 발생할 수 있습니다. )
            pImageAddress = ModuleEntry->ImageBase;
            if (pImageSize != NULL)
            {
                *pImageSize = ModuleEntry->ImageSize;
            }

            ExFreePoolWithTag(ModuleInfo, POOL_TAG);

            return (pImageAddress);
        }
    }
    ExFreePoolWithTag(ModuleInfo, POOL_TAG);

    return NULL;
}


void ExchangeProtectedIATPointer(PVOID IATPointerAddress, PVOID NewAddress)
{
    PMDL pMdl;
    PUCHAR pBuffer;

    pMdl = IoAllocateMdl(IATPointerAddress, sizeof(PVOID), FALSE, FALSE, NULL);
    if (pMdl == NULL)
    {
        return ;
    }
    MmBuildMdlForNonPagedPool(pMdl);
   
    pBuffer = MmMapLockedPagesSpecifyCache(pMdl, KernelMode, MmNonCached, NULL, FALSE, NormalPagePriority);
    if (pBuffer == NULL)
    {
        IoFreeMdl(pMdl);

        return ;
    }
    //
    // InterlockedExchangePointer(&pBuffer, NewAddress);  <= 이렇게 하면 안되고
    // InterlockedExchangePointer(pBuffer, NewAddress); <= 이렇게 해야 합니다.
    // 포인터의 값을 바꾸는것이 아니라 포인터가 가르키는 실제 값을 바꾸는 것이기 때문입니다.
    // 꼭 Interlocked 함수를 사용하지 않고 바로 대입해도 상관이 없습니다.
    // 그러나 한 번에 대입되는 방식이 아닌 Byte단위나 Word 단위 등에 방식은 동기화 문제를 발생시킬 수 있습니다.
    //
    InterlockedExchangePointer((PVOID *)pBuffer, NewAddress);

    MmUnmapLockedPages(pBuffer, pMdl);
    IoFreeMdl(pMdl);
}

List of Articles
번호 제목 글쓴이 날짜 조회 수
162 Windows Research SCSI Miniport - IOCTL_SCSI_PASS_THROUGH_DIRECT ( ... [레벨:6]lain 2011-06-11 3650
» Windows Research 커널레벨에서 IAT Hook [레벨:6]lain 2011-06-11 4348
160 Windows Research DEVICE_OBJECT에 대한 보안 디스크립터 변경 [레벨:6]lain 2011-06-11 3167
159 Windows Research IE Cache 경로 변경하기 [레벨:6]lain 2011-06-09 3292
158 Windows Research 32Bit 윈도우즈에서 실제 물리메모리크기 얻어오기 [레벨:6]lain 2011-06-09 3432
157 Windows Research VMWare 탐지 기법 우회 - 2 [2] [레벨:6]lain 2011-04-04 8851
156 Linux Tip 우분투에 IRC서버 설치후 닉네임 길이와 동접자 조절. [1] [레벨:0]오랑캐꽃 2010-11-26 5830
155 Linux Tip VirtualBox에 우분투 설치 후에 내부 네트워크 접속 설정하기 file [레벨:8]esniper 2010-09-10 12223
154 Linux Tip VirtualBox에서 오른쪽 CTRL키 사용하기(VitualBox 호스트키) file [2] [레벨:8]esniper 2010-09-10 6057
153 Linux Tip 우분투 설치 후 putty에서 한글 안 깨지도록 설정하기 file [레벨:8]esniper 2010-09-10 6366
152 Linux Tip 우분투 ssh or mysql 서버 접속지연이 있는 경우 해결책 [레벨:8]esniper 2010-09-10 6244
151 문서자료 고품질의 무료 아이콘들 [3] [레벨:8]esniper 2010-09-08 7439
150 Windows Research Sysinternals - Filemon source code file [5] [레벨:6]lain 2010-09-04 5818
149 Windows Research Sysinternals - Regmon source code file [1] [레벨:6]lain 2010-09-04 5492
148 Windows Research PostQueuedCompletionStatus 함수에 실패 여부도 고려해야 할까... [레벨:6]lain 2010-09-04 4678
147 Windows Research SetEvent 함수는 언제 실패할까? [레벨:6]lain 2010-09-04 5491
146 Windows Research 커널모드 드라이버에서 사용하는 시간관련 매크로 [레벨:6]lain 2010-09-04 4443
145 Windows Research 화면캡쳐방지는 어떻게 구현될까? [3] [레벨:6]lain 2010-09-04 8658
144 Windows Research IRP Completion Hook file [레벨:6]lain 2010-09-04 3992
143 Windows Research IRP Hook [레벨:6]lain 2010-09-04 4195

  • 이용약관
  • 개인정보취급방침
  • 사이트맵