首页
社区
课程
招聘
[原创]学习笔记之钩住驱动程序导入表
发表于: 2008-4-1 10:19 15195

[原创]学习笔记之钩住驱动程序导入表

2008-4-1 10:19
15195

这篇文章算是我的学习笔记吧,贴之与菜鸟们一起学习.因为偶也是菜鸟,很多概念性的,技术性的讲得不好还请矫正,好了,开始:
  当我们的程序调用一个API时候,比如CreateWindowEx,在编译的时候生成代码
call        dword ptr
[__imp__CreateWindowExA@48],这个API的地址就在__imp__CreateWindowExA@48中,在程序加载运行的时候,__imp__CreateWindowExA@48代表CreateWindowEx的内存地址,于是如果程序运行时候,我们把__imp__CreateWindowExA@48改成自己函数的地址,那当程序调用CreateWindowEx,实际上是跳到了我们的函数里,这就是IAT HOOK的基本思想,
  这篇文章是钩住驱动程序的IAT,不过这之前,先看一下用户态于IAT有关的一些信息,这有利于菜鸟理解接下来的驱动代码,(我也是菜鸟)因为驱动程序和一般的EXE程序一样,都是PE格式
  下面的这段用户代码基本是遍历遍历本程序的导入函数,然后打出来

#include <windows.h>
#include <stdio.h>
#include <imagehlp.h>
#pragma comment(lib,"imagehlp.lib")

int main()
{
  
        char *dll_name[30];
    char* function_name[30];
        HMODULE hInstance;
    int outloop_index=0;
   
    PIMAGE_THUNK_DATA thunk;
        hInstance=GetModuleHandle(NULL);
    IMAGE_DOS_HEADER *dosheader= (IMAGE_DOS_HEADER *)hInstance;
    IMAGE_NT_HEADERS *ntheader= (IMAGE_NT_HEADERS *)((DWORD)hInstance + dosheader->e_lfanew);
    IMAGE_DATA_DIRECTORY *pSymbolTable= &ntheader->OptionalHeader.DataDirectory[1];
    IMAGE_IMPORT_DESCRIPTOR *pImportDesc =(IMAGE_IMPORT_DESCRIPTOR *)((DWORD)hInstance + pSymbolTable->VirtualAddress);
   
        printf("the module is load at:%d\n",hInstance);
            printf("the pointer to ImportDesc is: %d\n",pImportDesc);
   
   while(pImportDesc ->FirstThunk)
{
       dll_name[outloop_index]=(char*)((PBYTE)hInstance+pImportDesc->Name);
           printf("------%s-------\n",dll_name[outloop_index]);
           outloop_index++;

    thunk=( PIMAGE_THUNK_DATA)((PBYTE)hInstance+ pImportDesc->OriginalFirstThunk);

    int x=0;
    while(thunk->u1.Function)
    {
        function_name[x]=(char*)((PBYTE)hInstance +
                ( DWORD)thunk->u1.AddressOfData+2);
                printf("%s\n",function_name[x]);
        
                x++;
                thunk++;
    }

   pImportDesc++;
}

   return 0 ;
}
好了,这里的hInstance是由hInstance=GetModuleHandle(NULL)获得,hInstance表示当前程序装载的基址,这个基地址很重要,没有它就不能正确定位调用函数在内存中的地址,
在驱动程序,这里我们假使要HOOK C:\WINDOWS\SYSTEM32\test_sysnap.sys,test_sysnap.sys
这个程序很简单,就是用DPC定时输出一个字符串:
VOID Myroutine(IN PKDPC Dpc,IN PVOID DeferredContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2)

{
    HANDLE id=PsGetCurrentProcessId();

    DbgPrint("the Current Process Id is:%x \n",id);

}
嗯,那test_sysnap.sys就一定导入了PsGetCurrentProcessId,我们就是要IAT HOOK这个函数,这样以后系统调用Myroutine执行到PsGetCurrentProcessId,都会转到我们自己的程序里运行

在讲用户程序的时候,我们说过hInstance很重要,那驱动程序的IAT HOOK,我们也需要知道test_sysnap.sys被加载在驱动程序里的什么地址,这里我们用ZwQuerySystemInformation这个函数就可以获得,前提,这个函数学没有被HOOK,这里我们就是这么假使滴,好了,先运行test_sysnap.sys,代码在后面在最后-----------------------------------------------------------------------------------------------------------------------
再看下面的驱动程序
#include "ntddk.h"
#include "hookiat.h"
#pragma comment(lib,"ntdll.lib")
       
PVOID GetDriverBaseAdress(char* driverName)
{
        ULONG size,index;
        PULONG buf;
        PSYSTEM_MODULE_INFORMATION module;
        PVOID driverAddress=0;

        ZwQuerySystemInformation(SystemModuleInformation,&size, 0, &size);

        if(NULL==(buf = (PULONG)ExAllocatePool(PagedPool, size)))
        {
                DbgPrint("failed alloc memory failed  \n");
                return 0;
        }
     
        status=ZwQuerySystemInformation(SystemModuleInformation,buf, size , 0);
        if(!NT_SUCCESS( status ))
        {
       DbgPrint("failed  query\n");
           return 0;
        }

        module = (PSYSTEM_MODULE_INFORMATION)(( PULONG )buf + 1);
       
        for (index = 0; index < *buf; index++)
        if (_stricmp(module[index].ImageName + module[index].ModuleNameOffset, driverName) == 0)  
        {
                driverAddress = module[index].Base;
        DbgPrint("Module found at:%x\n",driverAddress);
        }
        ExFreePool(buf);
        return driverAddress;
}

VOID Unload(PDRIVER_OBJECT  DriverObject)
{   
    DbgPrint("Unload Called \r\n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING str)
{
       
        PVOID base = NULL;
        base = GetDriverBaseAdress("test_sysnap.sys.sys");
        DriverObject->DriverUnload = Unload;
        return STATUS_SUCCESS;
}

编译一下,确实真实 DbgPrint了test_sysnap.sys加载的地址,/////好了,我们的目标是找到call        dword ptr
[__imp__PsGetCurrentProcessId@XX],修改之
首先我们要定位IAT,找出PsGetCurrentProcessId在其中的RAV////,最后目的就是得到(DWORD*)( (BYTE*)base + Thunk ) + RVA,修改之,这里base已经得到了,接下来就是Thunk和RVA,
首先我们用ZwOpenFile,ZwCreateSection,ZwMapViewOfSection几个函数来映射test_sysnap.sys,其实跟方法就是跟用户程序使用CreateFile,CreateFileMapping,MapViewOfFile一样

PVOID CreateMapFileAndReturnBaseAddress(PUNICODE_STRING pDriverName)
{
        HANDLE  hFile;
//HANDLE  hSection看需要在别的地方ZWclose(hSection),若不用.那定义成局部变量就可以了,或者做为数参数传递
    char *pszModName;
    PVOID MapFileBaseAddress = NULL;
        SIZE_T size=0;
    IO_STATUS_BLOCK stataus;
    OBJECT_ATTRIBUTES oa ;
   
        InitializeObjectAttributes(
    &oa,
        pDriverName,
    OBJ_CASE_INSENSITIVE,
    0,
    0
    );

ZwOpenFile(&hFile,
                   FILE_EXECUTE | SYNCHRONIZE,
                   &oa,
                   &stataus,
                   FILE_SHARE_READ,
                   FILE_SYNCHRONOUS_IO_NONALERT);
oa.ObjectName = 0;

ZwCreateSection(&hSection,
                                SECTION_ALL_ACCESS,
                                &oa,
                                0,
                                PAGE_EXECUTE,
                                SEC_IMAGE,
                                hFile);
ZwMapViewOfSection(hSection,
                                   PsGetCurrentProcessId(),
                                   &MapFileBaseAddress,
                                   0,
                                   1024,
                                   0,
                                   &size,
                                   ViewShare,
                                   MEM_TOP_DOWN,
                                   PAGE_READWRITE);
ZwClose(hFile);
DbgPrint("baseadress:%x\n",MapFileBaseAddress);
return MapFileBaseAddress;
       
}

把这个函数加上去, DriverEntry添加:
UNICODE_STRING driverName;
RtlInitUnicodeString(&driverName, L"\\Device\\HarddiskVolume1\\Windows\\System32\\drivers\\test_sysnap.sys");
    BaseAddress= CreateMapFileAndReturnBaseAddress(&driverName);
DbgPrint("MapFile Return Address:%x",BaseAddress);
编译一下,运行后没问题,GOOD,继续,到这里我们已经获得了MapFileBaseAddress,接着就是用它来获得ImageImportDescriptor

DWORD GetImageImportDescriptorPointer(IN OUT HANDLE* hMod, IN OUT IMAGE_IMPORT_DESCRIPTOR** pImportDesc)
        {
   
        IMAGE_DOS_HEADER * dosheader;
        IMAGE_OPTIONAL_HEADER * optheader;
    PVOID BaseAddress = NULL;
    UNICODE_STRING driverName;
   
        RtlInitUnicodeString(&driverName, L"\\Device\\HarddiskVolume1\\Windows\\System32\\drivers\\test_sysnap.sys");
    BaseAddress=  CreateMapFileAndReturnBaseAddress(&driverName);
    *hMod = BaseAddress;
   
        dosheader= (IMAGE_DOS_HEADER *)BaseAddress;
    optheader =(IMAGE_OPTIONAL_HEADER *) ((BYTE*)BaseAddress+dosheader->e_lfanew+24);
    *pImportDesc = (IMAGE_IMPORT_DESCRIPTOR *)((BYTE*)dosheader+ optheader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
    if( NULL == (*pImportDesc)) return 0;
        else
                return 1;
    DbgPrint("DataEntryAddress:%x\n",pImportDesc);
       
        }
照样把该函数学添加上去,自己加点测试的,编译一下,运行没什么问题,这样我们就得到了
MapFileBaseAddress和ImageImportDescriptor,接着就是用他们来找 Thunk和RVA

DWORD GetFunctionThunkAndRav(IN char* lpFunctionName, IN char* lpFunctionLibrary, OUT DWORD* pThunk, OUT DWORD* pRVA)
{
       
        HANDLE   hMod;
        IMAGE_IMPORT_DESCRIPTOR * pImportDesc;
        IMAGE_THUNK_DATA* thunk;
        char *pszModName;
        DWORD firstThunkList;
    DWORD ret;
        BOOLEAN isOrdinal;
    BOOLEAN foundIt;
       
        int x=0;
    SIZE_T size=0;
   
        ret=GetImageImportDescriptorPointer(&hMod,&pImportDesc);
        if(ret==0)
        {
       DbgPrint("GetImageImportDescriptorPointer return NULL");
                return 0;
        }
           
        //遍历IMPORT DIRECTORY TABLE,找到ntoskrnl.exe对应的IMAGE_IMPORT_DESCRIPTOR
        while (pImportDesc->FirstThunk)
        {               
                pszModName = (PSTR) ((PBYTE) hMod + pImportDesc->Name);
                if (_stricmp(pszModName, lpFunctionLibrary) == 0 )
                {
                        foundIt = TRUE;
            DbgPrint("name:%s\n",pszModName);
                        break;
                }
                pImportDesc++;
        }

        if(foundIt==FALSE)
        {
                return 0;
        }

       
       
        //得到ntoskrnl.exe的IMAGE_IMPORT_DESCRIPTOR,就可以其IAT,IAT中就可以找到导出函数了
       
        thunk = (IMAGE_THUNK_DATA*)( (BYTE*)hMod + pImportDesc->OriginalFirstThunk);
    firstThunkList = (DWORD)((PBYTE)hMod + pImportDesc->FirstThunk);
    foundIt = FALSE;
while(thunk->u1.Function)
        {
               
                isOrdinal = 0;
                //IMAGE_THUNK_DATA其实就是一个DWORD,它要么是Ordinal,要么是AddressOfData
                if(thunk->u1.Function >= 0x01000000) isOrdinal = TRUE;
                if(!isOrdinal) // 以名字到处而不是序号
                {
                        //IMAGE_IMPORT_BY_NAME
                        char* functionName = (char*)( (BYTE*)hMod + (DWORD)thunk->u1.AddressOfData + 2 );
                        if (_stricmp(functionName, lpFunctionName) == 0 )
                        {
                                *pThunk = pImportDesc->FirstThunk;
                                *pRVA = x;
                                DbgPrint("%x",( DWORD *)((PBYTE)hMod +pImportDesc->FirstThunk)+x);
                            ZwClose(hSection);
                                return 1;
                        }
                }
                if(isOrdinal)
                {
                        ZwClose(hSection);
                        return (DWORD) NULL;
                }

                x++;
                thunk++;
                firstThunkList++;
        }
       

if(foundIt==FALSE)
        {
                ZwClose(hSection);
                return 0;
        }
        ZwClose(hSection);
        return 0;

}
添加这个函数,稍微测试下,没什么问题,好的,这里我们已经得到(DWORD*)( (BYTE*)base + Thunk ) + RVA,接着就是最后的工作,修改之
g_FunctionInMemory = (DWORD*)( (BYTE*)base + Thunk ) + RVA;
.
.       
_asm
        {
                CLI                                       
                MOV        EAX, CR0               
                AND EAX, NOT 10000H
                MOV        CR0, EAX               
        }

        *(PVOID*)g_FunctionInMemory = MyPsGetCurrentProcessId;
    DbgPrint("HOOK SUCESS");
        _asm
        {
                MOV        EAX, CR0               
                OR        EAX, 10000H                       
                MOV        CR0, EAX                       
                STI                                       
        }
test_sysnap.sys的代码可以在http://hi.baidu.com/sysnap找到
到此为止,我们的工作就完成了,结果就是这样:


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (10)
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
2
好文,学习。
2008-4-1 10:54
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
学习。。。。
2008-4-1 13:14
0
雪    币: 47147
活跃值: (20450)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
4
把你的一张图片转到本地来了。放baidu显示不出来。
2008-4-1 13:48
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
我是菜鸟,学习进步!
2008-4-1 13:55
0
雪    币: 581
活跃值: (149)
能力值: ( LV12,RANK:600 )
在线值:
发帖
回帖
粉丝
6
不好意思,刚上完课,我不知道怎么插入本地图片,
2008-4-1 16:23
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
楼主很年轻  很强大啊。。。
2008-4-1 22:56
0
雪    币: 199
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
鼓励一下,写的很好啊.
2008-4-2 08:42
0
雪    币: 381
活跃值: (140)
能力值: ( LV13,RANK:330 )
在线值:
发帖
回帖
粉丝
9
写的很好,最近又得和钩子相处了
2008-4-18 09:37
0
雪    币: 266
活跃值: (35)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
10
高深的说,慢慢研究研究
2008-4-18 10:18
0
雪    币: 119
活跃值: (313)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
实际上驱动跟03挂钩都一样 驱动麻烦在调试 一不小心bsod 坏心情的!
2008-4-18 21:01
0
游客
登录 | 注册 方可回帖
返回
//