欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]pchunter逆向笔记

[原创]pchunter逆向笔记

之前分析pchunter时的笔记,懒得整理了,凑合看

1.pchunter调试


IDA打开驱动文件


设置调试器





运行vmmon

启动vmware虚拟机系统,

运行调试器



com:pipe,resets=0,reconnect,port=\\.\pipe\kd_windows_7_x64,

com:pipe,resets=0,reconnect,port=\\.\pipe\kd_Windows_7_x64

prot值可以从vmmon处观察

其实都一样




.sympath srv*http://msdl.microsoft.com/download/symbols;D:\localsymbols


dt _driver_object 不好使先设置符号路径,仍然不好使用.reload命令重新加载符号



找到driverEntry的iocontrol例程,找到里面的call eax为处理某个功能号,在此处下断点, dd rbx,查看功能号,输入输出等。


驱动分析技巧:驱动就是主要根据结构特征来定位,driver_object device_object, 各种的device_extension可以参考ddk sample和reactos


功能号:0x13号为判断地址是否可写


功能号:31列出所有的模块名称


通过DirverObject找到 DriverSection, 枚举 KLDR_DATA_TABLE_ENTRY 中的 InLoadOrderLinks 双向链表,获取加载的模块名称


//

qword_FFFFF8800389E558 = (__int64)DriverObject;


WINDBG>dt _driver_object

nt!_DRIVER_OBJECT

+0x000 Type : Int2B

+0x002 Size : Int2B

+0x008 DeviceObject : Ptr64 _DEVICE_OBJECT

+0x010 Flags : Uint4B

+0x018 DriverStart : Ptr64 Void

+0x020 DriverSize : Uint4B

+0x028 DriverSection : Ptr64 Void

+0x030 DriverExtension : Ptr64 _DRIVER_EXTENSION

+0x038 DriverName : _UNICODE_STRING

+0x048 HardwareDatabase : Ptr64 _UNICODE_STRING

+0x050 FastIoDispatch : Ptr64 _FAST_IO_DISPATCH

+0x058 DriverInit : Ptr64 long

+0x060 DriverStartIo : Ptr64 void

+0x068 DriverUnload : Ptr64 void

+0x070 MajorFunction : [28] Ptr64 long

dt _device_object


v8 = qword_FFFFF8800389FF58; //driver_object dt _driver_object

if ( !qword_FFFFF8800389FF58 )

v8 = **(_QWORD **)(qword_FFFFF8800389E558 + 0x28);driver_section

DriverSection输出出来是以下结构体 // dt _LDR_DATA_TABLE_ENTRY

WINDBG>dt _LDR_DATA_TABLE_ENTRY

nt!_LDR_DATA_TABLE_ENTRY

+0x000 InLoadOrderLinks : _LIST_ENTRY

+0x010 InMemoryOrderLinks : _LIST_ENTRY

+0x020 InInitializationOrderLinks : _LIST_ENTRY

+0x030 DllBase : Ptr64 Void

+0x038 EntryPoint : Ptr64 Void

+0x040 SizeOfImage : Uint4B

+0x048 FullDllName : _UNICODE_STRING

+0x058 BaseDllName : _UNICODE_STRING

+0x068 Flags : Uint4B

+0x06c LoadCount : Uint2B

+0x06e TlsIndex : Uint2B

+0x070 HashLinks : _LIST_ENTRY

+0x070 SectionPointer : Ptr64 Void

+0x078 CheckSum : Uint4B

+0x080 TimeDateStamp : Uint4B

+0x080 LoadedImports : Ptr64 Void

+0x088 EntryPointActivationContext : Ptr64 _ACTIVATION_CONTEXT

+0x090 PatchInformation : Ptr64 Void

+0x098 ForwarderLinks : _LIST_ENTRY

+0x0a8 ServiceTagLinks : _LIST_ENTRY

+0x0b8 StaticLinks : _LIST_ENTRY

+0x0c8 ContextInformation : Ptr64 Void

+0x0d0 OriginalBase : Uint8B

+0x0d8 LoadTime : _LARGE_INTEGER

{

if ( v8 <= MmSystemRangeStart )

break;

++v11;

v10 = 0;

if ( v11 > 0x1000 || *(_QWORD *)(v8 + 0x30) == qword_FFFFF8800389E568 )

break;

v8 = *(_QWORD *)v8; //InLoadOrderLinks

}

//

WINDBG>dt _LDR_DATA_TABLE_ENTRY

nt!_LDR_DATA_TABLE_ENTRY

+0x000 InLoadOrderLinks : _LIST_ENTRY

+0x010 InMemoryOrderLinks : _LIST_ENTRY

+0x020 InInitializationOrderLinks : _LIST_ENTRY

+0x030 DllBase : Ptr64 Void

+0x038 EntryPoint : Ptr64 Void

+0x040 SizeOfImage : Uint4B

+0x048 FullDllName : _UNICODE_STRING

+0x058 BaseDllName : _UNICODE_STRING

+0x068 Flags : Uint4B

+0x06c LoadCount : Uint2B

+0x06e TlsIndex : Uint2B

+0x070 HashLinks : _LIST_ENTRY

+0x070 SectionPointer : Ptr64 Void

+0x078 CheckSum : Uint4B

+0x080 TimeDateStamp : Uint4B

+0x080 LoadedImports : Ptr64 Void

+0x088 EntryPointActivationContext : Ptr64 _ACTIVATION_CONTEXT

+0x090 PatchInformation : Ptr64 Void

+0x098 ForwarderLinks : _LIST_ENTRY

+0x0a8 ServiceTagLinks : _LIST_ENTRY

+0x0b8 StaticLinks : _LIST_ENTRY

+0x0c8 ContextInformation : Ptr64 Void

+0x0d0 OriginalBase : Uint8B

+0x0d8 LoadTime : _LARGE_INTEGER

v24 = *(_WORD *)(v8 + 0x48) & 0xFFFE;

memmove((void *)(v17 + 36), *(const void **)(v8 + 0x50), (unsigned int)v24);


//点击内核钩子,FSD


枚举下列模块,记录下他们的加载地址和镜像大小



单机键盘选项卡


读取原始函数地址

1.打开文件,读取文件

2.分配镜像大小,按内存展开

3.找到OEP,

4.在oep函数内存中通过特征码,找到irp处理函数的偏移。


//内核钩子选项卡下的内核钩子

从磁盘中读取文件

根据导出表来检查 钩子

根据区段来检查钩子 判断区段是否可读,0x20000020 is executalbe contains code



IDT hook


35号功能

KeGetProcessorNumberFromIndex

KeSetSystemGroupAffinityThread


dt _KPCR

dt _KPRCB


 (GS:[0x20] is the "KPCR.CurrentPrcb" pointer

http://www.securiteam.com/securitynews/6U00D0AN5G.html


@0环的ETHREAD结构体是记录线程的相关信息,EPROCESS结构体是记录进程相关的信息,同样我们每个CPU也有一个结构体来记录每个CPU的状态这个结构体就是KPCR结构体KPCR结构体如下下面该结构体中几个主要的成员,


先KeStartDynamicProcessor 函数处开始,(rva = 0x4f6a70)从内存中搜索,搜索不到的话 通过kpcr来获取原函数地址,搜索的函数为sub_FFFFxxxxxx1e50()



278a48 ntoskernel的地址加上这个数,就是上面的v1

v1的指向的值加上6就是了


__int64 sub_1405A62E0() ntoskernel对处理器的处理


在KeStartDynamicProcessor函数中找到 v10 = sub_1401A0B10(v7, v6, v5, v9);


在sub_1401A0B10函数中找到


v7 = sub_1403FA9D0(&v33, &Dst, v8, v13, (unsigned __int16)v23, v4, dword_1402B10C0, SHIDWORD(v26), v14, v9, v10);


在sub_1403FA9D0中定位到1403FAA68


1403FAA68为找地址 278a48的关键点,该地址位于hal *__usercall sub_1403FA9D0函数处


读取sidt寄存器来获取中段处理历程,获取挂钩后的地址


pchunter的不一定准,它的结果和windbg !idt输出有不一致的地方,windbg能找到的 51号,在pci驱动中,pchunter找不到



内核选项卡


枚举过滤驱动的功能号45


枚举的驱动:

\FileSystem\RAW \Driver\KbdClass \Driver\i8042prt

\Driver\Tcpip \Driver\nsiproxy \Driver\tdx

\Driver\Mouclass \Driver\NDIS \Driver\PnpManager

ObReferenceObjectByName获取驱动对象,枚举device object 的attchached device,获取attached device的驱动对象名,设备名 和驱动路径


dt _DEVICE_OBJECT

dt _DRIVER_OBJECT


工作线程队列:


功能号:0xc3

在ntoskrnl中

rva 0x8cc01 ExQueueWorkItem

在函数中找道到rva 0x21D600,此处为ExWorkerQueue全局变量,枚举ExWorkerQueue可以得到工作线程队列,枚举WORK_QUEUE_ITEM


参考9.4 内核劳务线程 reactos http://book.51cto.com/art/200912/174648.htm,


ExWorkerQueue是全局数组

一共三类

typedef enum _WORK_QUEUE_TYPE {

CriticalWorkQueue,

DelayedWorkQueue,

HyperCriticalWorkQueue,

MaximumWorkQueue

} WORK_QUEUE_TYPE;



WINDBG>dt _KQUEUE 0xFFFFF80004076600

ntdll!_KQUEUE

+0x000 Header : _DISPATCHER_HEADER

+0x018 EntryListHead : _LIST_ENTRY [ 0xfffff800`04076618 - 0xfffff800`04076618 ]

+0x028 CurrentCount : 0

+0x02c MaximumCount : 1

+0x030 ThreadListHead : _LIST_ENTRY [ 0xfffffa80`018fd208 - 0xfffffa80`018fed28 ]

ThreadListHead   是这类劳务线程链表,nt!_KTHREAD.QueueListEntry的队列


!exqueue命令显示了关于工作队列和工作线程的详细信息。


hal回调:


haldispatch table:

功能号:0xf0

ntoskrnl.exe导出全局变量:HalDispatchTable

从此处HalDsipatchTable+8的地址处枚举0x15个地址


halPrivateDispathTable:

功能号0xf1:

ntoskrne.exe导出变量:HalPrivateDispatchTable:

从此处HalPrivateDispatchTable+8的地址处枚举0x2D个地址


halAcpiDispatchTable:

功能号0xf2:

找到HalInitializeProcessor函数的地址


在.data数据段搜索 特征码1212238880(48414C20h)

找到rva为0x254c0的全局变量


+8处开始计算地址

原函数地址处的也是从此处复制过去的。有什么用呢


wdf

wdf01000派发函数:

功能号:0xaf

当前函数地址通过driverobject获得 " \Driver\wdf01000"

原始函数地址通过,打开SystemRoot\System32\drivers\wdf01000.sys,通过oep查找原函数地址,


下图为ida反汇编wdf01000.sys入口点的部分截图

找到wdf中IRP_MJ_CREATE, IRP_MJ_CLEANUP, IRP_MJ_CLOSE,这几个派发函数位于wdf01000.sys中



找到rva 0x91148,,此rva后面后4个常量地址,这四个函数是干嘛的?


保存后面四个函数的特征码


派发函数好多在ntoskrnl中,派遣函数地址都是填的下面的地址

ntoskernel导出表中找到iocreatedriver,在该函数中找到rva0x661d4,该函数名称为IopInvalidDeviceRequest



wdffunction:


功能号:0x106

原始函数地址

打开SystemRoot\System32\drivers\wdf01000.sys

找.data节

具体的特征码为0x18c,此处+8处为一些列的rva地址

找到rva 0x8f87,此处为一系列的wdfcuntion地址



当前函数地址从wdf01000的加载地址搜索,偏移值和上面相同。

函数名称在应用层收集写死


文件系统:

微端口过滤器:

功能号:0xfa


打开SystemRoot\system32\drivers\fltmgr.sys,按内存映射

从导出表中获取FltEnumerateFilters函数的rva(0x30030),再加上fltmgr驱动实际的加载地址,获得该函数地址

再FltEnumerateFilters函数中找到rva 0x1d2c0

这个是_FLT_FILTER,根据此结构体找个各个函数,


参考文档

对抗与枚举MiniFilter

https://blog.csdn.net/u013761036/article/details/69062799

移除MiniFilter和移除sfilter

https://blog.csdn.net/qq125096885/article/details/53120229

从导出表中获取FltUnregisterFilter函数的rva(0x376A0)再加上fltmgr驱动实际的加载地址,获得该函数地址


文件系统:

功能号:0x100


打开ntoskernel,在重新映射的内存中找到的函数IoRegisterFsRegistrationChangeMountAware,在该函数中找到

找到rva 0x27d230, 0x27d240, 0x27d250, 0x27d220

在实际加载的ntoskernel中找到上面几个rva的实际地址

0x27d250处找到DISK类型的结构的device_object

0x27d230处找到 NETWORK类型的device_object

0x27d240找到CDROM类型的device_object

0x27d220找到Tape类型的devcie_object


可以参考IoRegisterFsRegistrationChangeMountAware函数内对这几个地址的操作来确定device_object

需要再详细逆,可以结合实际的显示地址来进行。


sfilter回调:

功能号:0x101


ObReferenceObjectByName

FileSystem\Raw


枚举过滤driver_object的device_object,

双层循环

第一层,枚举deviceobject

第二层,枚举attacheddevice

根据deveci_object找driver_object的

回调函数地址存在driver_extension中


文件系统过滤驱动调用 FsRtlRegisterFileSystemFilterCallbacks 函数注册需通知回调函数,这些回调函数将在文件系统的相关操作之前被调用


classinitdata回调:


功能号:0x120


打开\SystemRoot\system32\drivers\disk.sys,按节表重新映射文件

找到oep,0x1206c

找到DriverEntry x012F00

在该函数里面找到CLASS_INIT_DATA对它赋值的地方

Windows DDK在其src\storage\class\disk目录中提供了disk.sys的代码

#define FILE_DEVICE_DISK                0x00000007

#define FILE_DEVICE_SECURE_OPEN 0x00000100 辅助定位特征码


npfs派发函数

功能号:0x116

\SystemR\system32\drivers\.npfs.sys

从oep找到driverentry

在driverentry处找irp处理函数


msfs派发函数

功能号:0x118

\SystemR\system32\drivers\msfs.sys

从oep处找driverentry

在driverentry处找irp处理函数


usbport派遣函数

功能号:0x11a

打开文件 \SystemRoot\syst.em32\drivers\usbport.sys,导出表中搜索USBPORT_RegisterUSBPortDriver

RVA 为0xFFFD6FA0

在此函数内搜索到irp处理函数

rva为0x4918,其他的在ntoskernel中




系统调试:

功能号:0xfa

rva 140b70为KdDisableDebugger


找到rva 0x142310,位于sub_1401409C0函数中


rva 0x168410 KeEnterKernelDebugger

rva 0x500010此处为kiDebugRoutine


上面的东西可以参考reactos


显示名称,调试寄存器等

功能号:0xf5

0xf7:

0x19:

硬件执行断点会触发显示调试寄存器的值及线程等。


0x2b1370的值为0x142310处的函数指针

windbg中已知函数地址,

x /a nt!* 排序后按地址对吧



fffff800`03e995f0 nt!KeGetProcessorNumberFromIndex (<no parameter info>)

fffff800`03eb91e8 nt!KeSetSystemGroupAffinityThread (<no parameter info>)

fffff800`03eb9630 nt!KeRevertToUserGroupAffinityThread (<no parameter info>)


KeGetProcessorNumberFromIndex


0xf7

PspCidTable枚举进程


nt!NtQuerySystemInformation (<no parameter info>)

MmGetPhysicalAddress

MmGetVirtualForPhysical

FsRtlIsNameInExpression



fffff800`0408bbc8 nt!PspCidTable = <no type information>


0xFFFFF80004115030 nt:nt_PsInitialSystemProcess

psccidtable杂谈


枚举进程的线程,枚举TrapFrame

dt _EPROCESS dt _ethread dt _KTHREAD

_ethread ->kthread->_KTRAP_FRAME, 此结构内有drx的值

 


0x19获取进程路径


PsLookupProcessByProcessId

根据进程id获取进程名称



对象劫持

功能号:0xb1

\Driver\Disk

ObReferenceObjectByName获得driverobject, 枚举deviceobject 判断是FILE_DEVICE_DISK

ObQueryNameString \Device\Harddisk\DR0

返回dr0的deviceobject


根据deviceobject取+0x40DeviceExtension,为FUNCTIONAL_DEVICE_EXTENSION结构体,取lowerpdo

根据deviceobject 取+0x138 DeviceObjectExtension ,取_DEVOBJ_EXTENSION的+0x030 AttachedTo

如果不等,输出lowerpdo的对象信息


driver/keyboradclass

找到deviceobject, 取+0x40deviceExetension, 根据deviceExetension取+0x10 TopPort,

label2:

如果device_object的 netxt_device 不为空

根据deviceobject 取+0x138 DeviceObjectExtension ,取_DEVOBJ_EXTENSION的+0x030 AttachedTo

如果相等

LABEL1:

取topport的drvier_object, 取device_object, ,取deviceExtension,根据根据deviceExetension取+0x10 TopPort

根据deviceobject 取+0x138 DeviceObjectExtension ,取_DEVOBJ_EXTENSION的+0x030 AttachedTo

goto label2:

如果不等,输出TopPort信息

goto Label1:



直接IO:


枚举进程0xc6

比较进程是否是csrss.exe

EPROCESS + 0x200 objectTable

+0x320 ActiveThreads : Uint4B


bp nt!NtQuerySystemInformation(0x10)


断下后看堆栈

逆向zwSetInfomationProcess??



reactos搜索iopl做参考


下面这个结构在win764位上已经发生了变化

lkd> dt _eprocess

ntdll!_EPROCESS

   +0x000 Pcb              : _KPROCESS


lkd> dt _kprocess

ntdll!_KPROCESS

   +0x000 Header           : _DISPATCHER_HEADER

   +0x010 ProfileListHead  : _LIST_ENTRY

   +0x018 DirectoryTableBase : [2] Uint4B

   +0x020 LdtDescriptor    : _KGDTENTRY

   +0x028 Int21Descriptor  : _KIDTENTRY

   +0x030 IopmOffset       : Uint2B

    +0x032 Iopl             : UChar



GDT:

功能号:

0xbc

KeGetProcessorNumberFromIndex

KeSetSystemGroupAffinityThread

KeRevertToUserGroupAffinityThread

sgdt获取gdt,计算gdt的大小,打印出各个字段



网络


tcpip


功能号:0x9b

打开文件,SystemRoot\system32\drivers\tcpip.sys,按内存映射

根据PE文件结构找到OEP 0x1DD06c

从OEP处找到DriverEntry(rva 0x1DDBC0)

DriverEntry处找到v5 = sub_1ED830(v3);

此函数内有irp处理地址

派发函数好多在ntoskrnl中,派遣函数地址都是填的下面的地址

打开ntoskrernl文件,ntoskernel导出表中找到iocreatedriver,在该函数中找到rva0x661d4,该函数名称为IopInvalidDeviceRequest


驱动程序对象中的MajorFunction数组包含了一组例程,当I/O 管理器接收到一个I/O请求时,它将根据 I/O 请求中的有关信息,找到设备对象的驱动程序对象,并调用驱动程序中相应的例程来处理该I/O 请求。通常,设备驱动程序的初始化例程会填充MajorFunction数组中的例程。对于初始化例程未填充的数组项,创建驱动程序对象的函数(如IopLoadDriver和IoCreateDriver)会将其填充为 IopInvalidDeviceRequest 函数。


__int64 sub_FFFFF88005CE4710()为找IopInvalidDeviceRequtet的过程。


nsiproxy:


打开文件,SystemRoot\system32\drivers\nsiproxy.sys,按内存映射

找到OEP 0xa164,再oep找到DrvierEntry

tdx

打开文件,driverentry中找部分派遣函数,其他在ntoskernel文件中



ndis

功能号:0xd0,0xd1,0xd2,0xd3


/*注意NDIS_HANDLE所指向的就是PNDIS_PROTOCOL_BLOCK的结构,不要有什么怀疑。*/

NDIS_PROTOCOL_BLOCK(协议表) 是NDIS维护所有系统中已注册协义的单向链接表。字段NextProtocol指向下一个协议表。

庆幸的是,当我们注册一新的协议时,NDIS总是会把新注册的协义放在链表的头并返回这张表,所以只要我们注册一个新的协议,通过新协议注册返回的链表头就可以轻而易举的遍历系统中所有协议表。

https://blog.csdn.net/chenyujing1234/article/details/7823959


有必要先介绍一下NDIS网卡驱动和协议驱动之间是如何BINDING 的吧:

(1)NdisRegisterProtocol在注册完一个协议后,不久NDIS会通过调用表中BindAdapterHandler派发函数,通知协议对每一个网卡进行BINDING。或者当系统通PNP找到一块新的网卡时也会调用BindAdapterHandler对协议进行BINDING;

(2)协议在BINDING 调用里,会根据自己的需要使用NdisOpenAdapter将自身绑定到适合的网卡。并返回NdisBindingHandle.NdisBindingHandle。



打开ndis文件,从导出表获取NdisGetVersion地址

NdisDeregisterProtocol地址,(rva ba600)获取到rva后加上驱动中nids模块加载的地址得到真实地址

根据NdisDeregisterProtocol 的rva在打开的ndis文件中的函数内找到rva 0x65fc0

加上ndis的地址就是一堆block块的地址


获取NdisFRegisterFilterDriver地址 rva(53cd0),获取到rva后加上驱动中nids模块加载的地址得到真实地址

在此函数中找到rva 0x66148


NdisMRegisterMiniportDriver(0x4d010)

找到 rva0x65fb8


NdisIMInitializeDeviceInstanceEx(0xbe350)

NdisRegisterTdiCallBack(0xB2130)

NdisIfRegisterProvider(0x45ce0)

找到 rva 0x66b60


NdisIfDeregisterProvider

NdisIfRegisterInterface()

NdisIfDeregisterInterface

NdisMGetOffloadHandlers


根据上面的几组block就可以枚举函数的地址了。


函数名称从环3写死




注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功