[原创]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写死
