IDA 逆 WDF 驱动时的函数识别插件

Posted Nemesis

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IDA 逆 WDF 驱动时的函数识别插件相关的知识,希望对你有一定的参考价值。

快一年没更新了,累,工作累,各种累,想换个工作,突然发现找不到合适的工作了,哎,自己往火坑里跳,怪不得别人。

 

import idautils
import idaapi
import idc

print("new ------------------------------------------")

#ea = idc.get_curline()
#print(ea)

# 
def GetWdfVersionBindObject(addr):
    for x in XrefsTo(addr,flags=0):
        cur_addr = x.frm
        cur_asm = GetDisasm(cur_addr)
        if (cur_asm.startswith("call")):
            pass
        else:
            continue

        func_addr = idc.get_func_attr(cur_addr,FUNCATTR_START)
        
        pre_addr = cur_addr
        while True:
            if pre_addr <= func_addr:
                break
            pre_addr = idc.prev_head(pre_addr)
            pre_asm = GetDisasm(pre_addr)
            if (pre_asm.startswith("lea")):
                t = idc.get_operand_type(pre_addr, 0)
                # 寄存器
                if (t == 1):
                    pass
                else:
                    break
            
                data = idc.get_operand_value(pre_addr, 0)
                # r8
                if (data == 8):
                    t = idc.get_operand_type(pre_addr, 1)
                    data = idc.get_operand_value(pre_addr, 1)
                    return data
    return 0


# 从模块中找到对应符号地址
fpWdfVersionBind = idc.get_name_ea(0x140000000, "WdfVersionBind")
print(\'Address : WdfVersionBind : %#x\'%fpWdfVersionBind)
# 根据对应符号地址,找到其第三个参数地址
pObject = GetWdfVersionBindObject(fpWdfVersionBind)
print(\'Address : Wdf Object : %#x\'%pObject)
# 获取的版本号
verBig = idaapi.get_dword(pObject + 0x10)
verMin = idaapi.get_dword(pObject + 0x14)
print("version :", verBig, verMin)


# 根据函数索引取函数名字
def GetNameByID(id):
    return ""


def MakeWdfFunctionInfo(addr):
    for x in XrefsTo(addr,flags=0):
        cur_addr = x.frm
        cur_asm = GetDisasm(cur_addr)
        if (cur_asm.startswith("mov")):
            pass
        else:
            continue
        #print("cur", cur_addr, cur_asm)
        
        pre_addr = idc.prev_head(cur_addr)
        pre_asm = GetDisasm(pre_addr)
        #print("pre", pre_addr, pre_asm)
        if (pre_asm.startswith("imul")):
            # 第 0 个参数类型是1,所以是寄存器
            type0 = idc.get_operand_type(pre_addr, 0)
            if (type0 == 1):
                pass
            else:
                break
            # 寄存器参数索引是 0,是rax
            data = idc.get_operand_value(pre_addr, 0)
            if (data == 0):
                pass
            else:
                break

            # 按理说应该是取操作数1,但是这里1里面没值,所以取的是 2
            # 取出来的就是函数索引
            data = idc.get_operand_value(pre_addr, 2)
            
            # 根据函数索引
            func_name = GetNameByID(data)
            
            fun_addr = idc.get_func_attr(pre_addr,FUNCATTR_START)
            
            if (func_name != ""):
                ida_name.set_name(fun_addr, func_name)


# 寻找所有使用到的地方,并且修正对应函数名,编程索引对应函数名
MakeWdfFunctionInfo(get_qword(pObject + 0x20))

 

几十行代码,有注释,一些地方用的是硬编码,需要改一改,但是目前至少能用了,还缺少一个东西,就是数据库,

这个数据库是根据不同版本的 WDF 函数数据库

WDF驱动开发- 一个简单的WDF驱动(non-pnp)

最近因为工作关系,接触了一下WDF驱动开发。

WDF驱动其实是微软公司提供的一套驱动开发的框架。有了这个框架之后,开发驱动会简单一些。WDF本身是从WDM基础上封装而成的。WDF里面封装了很多对象,如WDFDRIVER等。如果要学习使用WDF来开发驱动,个人感觉还是需要WDM的一些基础,不然很多东西挺难理解的。

写了一个简单的WDF驱动(非pnp),基本步骤如下:

创建framework 驱动对象

几乎任何一个WDF驱动一开始就要创建一个framework的驱动对象,这个对象是所有其他对象的parent对象。

 

        //初始化WDF_DRIVER_CONFIG
	WDF_DRIVER_CONFIG_INIT(
							   &cfg,
							   NULL //不提供adddevice函数							);

	cfg.DriverInitFlags = WdfDriverInitNonPnpDriver;  //非pnp驱动
	cfg.DriverPoolTag   = (ULONG)'PEPU';  
	cfg.EvtDriverUnload = EvtDriverUnload;  //卸载函数

	//
	//创建一个framework的驱动对象。
	status = WdfDriverCreate(DriverObject,RegistryPath,WDF_NO_OBJECT_ATTRIBUTES,&cfg,&drv);
	if(!NT_SUCCESS(status))
	
		goto DriverEntry_Complete;
	

	KdPrint(("Create wdf driver object successfully\\n"));

这样,一个framework的驱动对象就创建好了。

创建一个设备(control device)

1. 要创建一个control device,首先得先分配一块内存

	//分配一块内存
	device_init = WdfControlDeviceInitAllocate(drv,&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R);  
	if( device_init == NULL )  
	    
		status = STATUS_INSUFFICIENT_RESOURCES;    
		goto DriverEntry_Complete;  
	

WdfControlDeviceInitAllocate函数分配了一块内存给结构WDFDEVICE_INIT,这个结构在创建control device的时候会被用到。更多细节看MSDN:http://msdn.microsoft.com/en-us/library/windows/hardware/ff545841(v=vs.85).aspx

2. 然后给这个设备绑定一个设备名字,注意这个设备名字只能被内核模式下的代码所看到,比如其他的内核驱动,用户模式代码是看不到的。

就好象是WDM里面的IoCreateDevice函数的第三个参数。

	//设备名字,如: L"\\\\Device\\\\MyWDF_Device",只能被其他的内核驱动看到。
	RtlInitUnicodeString(&ustring, MYWDF_KDEVICE);  

	//将这个设备名字存入DEVICE_INIT结构中
	status = WdfDeviceInitAssignName(device_init,&ustring);

	if(!NT_SUCCESS(status))
	
		goto DriverEntry_Complete;
	

	KdPrint(("Device name Unicode string: %wZ (this name can only be used by other kernel mode code, like other drivers)\\n", &ustring));

 

3. 给设备绑定2个回调。

	//设置2个回调,FileCreate和FileClose
	WDF_FILEOBJECT_CONFIG_INIT(&f_cfg,EvtDeviceFileCreate,EvtFileClose,NULL);

	//存入DEVICE_INIT结构中
	WdfDeviceInitSetFileObjectConfig(device_init,&f_cfg,WDF_NO_OBJECT_ATTRIBUTES);

这样当用户模式的代码调用CreateFile和CloseHandle的时候,这2个回调会被调用。

 

4. 初始化设备属性并且创建设备

这里创建一个control device

	WDF_OBJECT_ATTRIBUTES_INIT(&object_attribs);
	
	//创建一个设备. (control device)
	status = WdfDeviceCreate(&device_init,&object_attribs,&control_device);
	if(!NT_SUCCESS(status))
	
		KdPrint(("create device failed\\n"));
		goto DriverEntry_Complete;
	


5. 创建一个符号连接

有了这个符号连接,用户模式代码才能找到这个设备。就好像WDM里面IoCreateSymbolicLink()做的事情。

	//创建一个符号连接
	RtlInitUnicodeString(&ustring,MYWDF_LINKNAME);	
	status = WdfDeviceCreateSymbolicLink(control_device,&ustring);	
	if( !NT_SUCCESS(status) )  
		
		KdPrint(("Failed to create Link\\n"));	 
		goto DriverEntry_Complete;  
		

	KdPrint(("Create symbolic link successfully, %wZ (user mode code should use this name, like in CreateFile())\\n", &ustring));


6. 完成设备的创建

	WdfControlFinishInitializing(control_device);


到这里,一个设备就创建成功了。这是一个control device,有关control device可以查看http://msdn.microsoft.com/en-us/library/windows/hardware/ff545424(v=vs.85).aspx

 

framework驱动对象的创建和设备对象的创建都发生在DriverEntry()里面。

 

回调函数的实现

接下来就是回调函数的实现,这里就写了几行简单的代码。

static VOID EvtDriverUnload( WDFDRIVER Driver )
  
	KdPrint(("unload driver\\n"));
	KdPrint(("Doesn't need to clean up the devices, since we only have control device here\\n"));
/* EvtDriverUnload */

VOID EvtDeviceFileCreate( __in WDFDEVICE Device, __in WDFREQUEST Request, __in WDFFILEOBJECT FileObject )

	KdPrint(("EvtDeviceFileCreate"));

	WdfRequestComplete(Request, STATUS_SUCCESS);


VOID EvtFileClose( __in  WDFFILEOBJECT FileObject )

	KdPrint(("EvtFileClose"));

只有在FileCreate里面写了一行代码直接将irp请求完成。

这样一个最最简单的WDF驱动就写完了。

完整代码

#include <fltKernel.h>
#include <wdf.h>
#include <wdfdriver.h>
#include <wdfrequest.h>

#define MYWDF_KDEVICE L"\\\\Device\\\\MyWDF_Device"//设备名称,其他内核模式下的驱动可以使用
#define MYWDF_LINKNAME L"\\\\DosDevices\\\\MyWDF_LINK"//符号连接,这样用户模式下的程序可以使用这个驱动设备。



//声明回调
EVT_WDF_DRIVER_UNLOAD EvtDriverUnload;
EVT_WDF_DEVICE_FILE_CREATE EvtDeviceFileCreate;
EVT_WDF_FILE_CLOSE EvtFileClose;


NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject , IN PUNICODE_STRING RegistryPath )
  
	NTSTATUS status;	
	WDF_OBJECT_ATTRIBUTES object_attribs;

	//驱动对象相关
	WDF_DRIVER_CONFIG cfg;//驱动的配置
	WDFDRIVER drv = NULL;//wdf framework 驱动对象

	//设备对象相关
	PWDFDEVICE_INIT device_init = NULL;
	UNICODE_STRING ustring;
	WDF_FILEOBJECT_CONFIG f_cfg;
	WDFDEVICE control_device;
	PDEVICE_OBJECT dev = NULL;

	KdPrint(("DriverEntry [start]\\n"));
	

	//初始化WDF_DRIVER_CONFIG
	WDF_DRIVER_CONFIG_INIT(
							   &cfg,
							   NULL //不提供AddDevice函数
							);

	cfg.DriverInitFlags = WdfDriverInitNonPnpDriver;  //指定非pnp驱动
	cfg.DriverPoolTag   = (ULONG)'PEPU';  
	cfg.EvtDriverUnload = EvtDriverUnload;  //指定卸载函数

	//创建一个framework驱动对象,在WDF程序里面,WdfDriverCreate是必须要调用的。
	//framework驱动对象是其他所有wdf对象的父对象,换句话说framework驱动对象是wdf对象树的顶点,它没有父对象了。
	status = WdfDriverCreate(DriverObject,RegistryPath,WDF_NO_OBJECT_ATTRIBUTES,&cfg,&drv);
	if(!NT_SUCCESS(status))
	
		goto DriverEntry_Complete;
	

	KdPrint(("Create wdf driver object successfully\\n"));


	//创建一个设备

	//先要分配一块内存WDFDEVICE_INIT,这块内存在创建设备的时候会用到。
	device_init = WdfControlDeviceInitAllocate(drv,&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R);  
	if( device_init == NULL )  
	    
		status = STATUS_INSUFFICIENT_RESOURCES;    
		goto DriverEntry_Complete;  
	

	//创建设备的名字,内核模式下,名字类似: L"\\\\Device\\\\MyWDF_Device"
	RtlInitUnicodeString(&ustring, MYWDF_KDEVICE);  

	//将设备名字存入device_init中
	status = WdfDeviceInitAssignName(device_init,&ustring);

	if(!NT_SUCCESS(status))
	
		goto DriverEntry_Complete;
	

	KdPrint(("Device name Unicode string: %wZ (this name can only be used by other kernel mode code, like other drivers)\\n", &ustring));

	//配置FILEOBJECT配置文件,设置FILECREATE,FILECLOSE回调。
	WDF_FILEOBJECT_CONFIG_INIT(&f_cfg,EvtDeviceFileCreate,EvtFileClose,NULL);

	//将FILEOBJECT的设置存入device_init中
	WdfDeviceInitSetFileObjectConfig(device_init,&f_cfg,WDF_NO_OBJECT_ATTRIBUTES);

	//初始化设备属性
	WDF_OBJECT_ATTRIBUTES_INIT(&object_attribs);
	
	//根据前面创建的device_init来创建一个设备. (control device)
	status = WdfDeviceCreate(&device_init,&object_attribs,&control_device);
	if(!NT_SUCCESS(status))
	
		KdPrint(("create device failed\\n"));
		goto DriverEntry_Complete;
	

	//创建符号连接,这样用户模式下的程序可以使用这个驱动。这个是必须的,不然用户模式下的程序不能访问这个设备。
	RtlInitUnicodeString(&ustring,MYWDF_LINKNAME);	
	status = WdfDeviceCreateSymbolicLink(control_device,&ustring);	
	if( !NT_SUCCESS(status) )  
		
		KdPrint(("Failed to create Link\\n"));	 
		goto DriverEntry_Complete;  
		

	KdPrint(("Create symbolic link successfully, %wZ (user mode code should use this name, like in CreateFile())\\n", &ustring));

	WdfControlFinishInitializing(control_device);//创建设备完成。

	/*******************************************
	到这里,我们就成功创建了一个control device。
	control device 是不支持png和power的,而且我们也不需要手工是删除。
	因为framework会帮我们删除,看MSDN
	
	If your driver creates control device objects but does not create framework device objects that support PnP and power management, 
	the driver does not have to delete the control device objects. 
	
	In this case, the framework deletes the control device objects after the driver's EvtDriverUnload callback function returns. 

	更多细节看MSDN,如
	http://msdn.microsoft.com/en-us/library/windows/hardware/ff545424(v=vs.85).aspx
	*******************************************/


	KdPrint(("Create device object successfully\\n"));


	KdPrint(("DriverEntry succeeds [end]\\n"));
	
DriverEntry_Complete:

	return status;




static VOID EvtDriverUnload( WDFDRIVER Driver )
  
	KdPrint(("unload driver\\n"));
	KdPrint(("Doesn't need to clean up the devices, since we only have control device here\\n"));
/* EvtDriverUnload */

VOID EvtDeviceFileCreate( __in WDFDEVICE Device, __in WDFREQUEST Request, __in WDFFILEOBJECT FileObject )

	KdPrint(("EvtDeviceFileCreate"));

	WdfRequestComplete(Request, STATUS_SUCCESS);


VOID EvtFileClose( __in  WDFFILEOBJECT FileObject )

	KdPrint(("EvtFileClose"));

用DDK编译一下就可以了,顺便贴一下sources里面的内容

TARGETNAME=mywdf
TARGETTYPE=DRIVER
DRIVERTYPE=UNKNOWN

KMDF_VERSION_MAJOR=1
KMDF_VERSION_MINOR=9
UMDF_MINOR_VERSION=9

WINDDK=c:\\winddk\\7600.16385.0

TARGETLIBS= $(DDK_LIB_PATH)\\Rtlver.lib \\
            $(DDK_LIB_PATH)\\Wdmsec.lib

#C_DEFINES=$(C_DEFINES)

INCLUDES=$(WINDDK)\\inc\\wdf\\kmdf\\$(KMDF_VERSION_MAJOR)$(KMDF_VERSION_MINOR);..\\

SOURCES = \\
	mywdf.c

这样就得到了一个mywdf.sys,然后安装。一直以来,驱动的安装总是让人那么纠结。一堆的配置inf,好在已经有很多现成的了。网上搜索就可以搜到一堆,这里就不在介绍了。

安装完驱动后,这个驱动有什么用呢?其实啥用都没有,但是可以写几行代码证实一下。如:

#include "stdafx.h"
#include <Windows.h>

#define MYDEVICE L"\\\\\\\\.\\\\MyWDF_LINK"

int _tmain(int argc, _TCHAR* argv[])

	HANDLE hDevice = CreateFile(MYDEVICE,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

	if (hDevice == INVALID_HANDLE_VALUE)
	
		wprintf(L"Failed to open device %s, err: %x\\n", MYDEVICE, GetLastError());
	
	else
	
		wprintf(L"Open device %s successfully\\n", MYDEVICE);
		CloseHandle(hDevice);

		wprintf(L"Closed handle\\n");
	

	return 0;


这是个用户模式代码,运行一下,就会发现

用debugview看一下驱动的输出,看到了驱动里面的log,这就说明用户模式的代码CreateFile和CloseHandle成功的引起驱动调用2个回调。




 



 

 

 

以上是关于IDA 逆 WDF 驱动时的函数识别插件的主要内容,如果未能解决你的问题,请参考以下文章

IDA插件栈字符串识别插件

微软开源驱动程序模块框架WDF

windows设备驱动程序WDF开发

高手指点一下,这段代码为啥不能在ida 中f5

基于WDF的PCI/PCIe接口卡Windows驱动程序-如何为硬件移植驱动程序

ida怎么使用