Professional Documents
Culture Documents
windows内核高级篇 - 副本
windows内核高级篇 - 副本
0.系统时间通信
X64上,修改IRQL,靠 __writecr8(8) 来修改。
自定义回调的IRQL 一般是2(DPC等级) 不能使用绝大部分函数
靠PEB 传结构体指针通信。
1.确定回调对象的目录
2.打开指定回调对象
OBJECT_ATTRIBUTES objAttr = { 0 };
UNICODE_STRING name;
RtlInitUnicodeString(&name, L"\\CallBack\\SetSystemTime");
InitializeObjectAttributes(&objAttr, &name, OBJ_CASE_INSENSITIVE, NULL,
NULL);
3.注册回调
4.卸载回调
VOID UnRegisterCommunicate()
{
if (G_Callback_HANLE)
{
ExUnregisterCallback(G_Callback_HANLE);
G_Callback_HANLE = NULL;
}
}
1.X64物理地址拆分
kd> dq fffff800`03f81c50
fffff800`03f81c50 4c000ca8`09358b48 3b49000c`a802258d
1111 1000 0
000 0000 00
00 0011 111
1 1000 0001
1100 0101 0000
PXE=*(CR3+1F0*8)
PPE=*(PXE+0*8)
PDE=*(PPE+1F*8)
PTE=*(PDE+181*8)
c50
kd> dq FFFFF6FC0001FC08
fffff6fc`0001fc08 00000000`03f81121 00000000`03f82121
fffff6fc`0001fc18 00000000`03f83121 00000000`03f84121
fffff6fc`0001fc28 00000000`03f85121 00000000`03f86121
fffff6fc`0001fc38 00000000`03f87121 00000000`03f88121
fffff6fc`0001fc48 00000000`03f89121 00000000`03f8a121
fffff6fc`0001fc58 00000000`03f8b121 00000000`03f8c121
fffff6fc`0001fc68 00000000`03f8d121 00000000`03f8e121
fffff6fc`0001fc78 00000000`03f8f121 00000000`03f90121
1.VAD
1.WINDOWS内存管理
<windows内核原理与实现>潘爱民c
1.1内存管理概述
物理地址:内存存储器的索引。32位或36位无符号整数。
逻辑地址:段+偏移。
虚拟地址:懂得都懂
内存换出:进程中的数据或代码保存到磁盘
内存换入:磁盘中的数据或代码读到内存中
/*
页式内存管理
物理内存按 页 管理,Trap Frame 结构。
*/
1.普通寻址
注意CR3是物理地址。
2.PAE寻址
32位虚拟地址 36位物理地址
1.3内存管理算法
为了满足动态内存申请,随机地不规则地申请内存,操作系统使用了堆内存。
位图标记法
每块有个标志位,0未使用,1已使用。
N=M*K
空闲链表法
每个结点都是一块内存
1.4管理概述
PFN database数据库,描述了物理内存各个页面的状态。
PFN数据库实际是一个结构数组,每个页面对应一个一个PFN项,维护了该页面的使用情况。
操作系统还维护了一组链表,来管理不同类型的页表。
1.物理内存数据库
1.System Address Space Layout
2.Working Set
工作集用来管理进程的虚拟地址的物理页。(用户区域
除了每个进程都有一个工作集以外,系统也有一个工作集。
ZwVirtualLock 就是把虚拟地址和对应物理页放到工作集中。(锁的很死
MDL的MmProbeAndLockPages 是使用异常访问函数,访问页的头部地址。(锁地不严
3.MMPfndatabase
dt _MMPFN 有8种状态,但是使用的只有六种
1.结构
kd> dq MMpfndatabase
fffff800`04106220 fffffa80`00000000 000001f4`00000040
fffff800`04106230 0002ffff`00000250 00000003`00000000
fffff800`04106240 00000000`00000000 00080000`00000001
fffff800`04106250 00000001`ffffffff fffffa80`019e1000
fffff800`04106260 01d714e0`07a4bd3e fffffa80`018f4600
fffff800`04106270 fffffa80`01932830 00000000`00004840
fffff800`04106280 00000000`5c7fe000 fffffa80`018d1c10
fffff800`04106290 00000000`00000001 fffffa80`018eb570
kd> !pfn 0
PFN 00000000 at address FFFFFA8000000000
flink 00000000 blink / share count 00000000 pteaddress 00000000
reference count 0000 used entry count 0000 NonCached color 0
Priority 0
restore pte 00000000 containing page 000000 Zeroed
kd> !pfn 1
PFN 00000001 at address FFFFFA8000000030
flink 00000000 blink / share count 00000001 pteaddress
FFFFF6FFFFFFE840
reference count 0001 used entry count 0000 Cached color 0
Priority 0
restore pte 00000000 containing page 0001F2 Active
kd> dt _MMPFN -v
nt!_MMPFN
struct _MMPFN, 13 elements, 0x30 bytes //数组每项大小0x30字节
+0x000 u1 : union <unnamed-tag>, 7 elements, 0x8 bytes
********
********
+0x028 u4 : union <unnamed-tag>, 6 elements, 0x8 bytes
2.查询
kd> dq MmIsAddressValid
fffff800`03f81c50 90fff871`c9e9d233 90909090`90909090
fffff800`03f81c60 6c894808`245c8948 57182474`89481024
fffff800`03f81c70 57415641`55415441 482a8b4c`20ec8348
fffff800`03f81c80 000000b9`4808428b 348d4c00`00058000
fffff800`03f81c90 8b4d006d`5c8d4b40 e6c14904`e3c148f9
fffff800`03f81ca0 4cd92b48`e08b4d04 ea8b48c9`8b49f12b
fffff800`03f81cb0 85ffff40`99e8ff33 33000000`bf840fc0
fffff800`03f81cc0 f1032aeb`014f8df6 17750018`45fe3585
//contains 代表对应的物理页(物理地址)的意思。
kd> !pte fffff800`03f81c50
VA fffff80003f81c50
PXE at FFFFF6FB7DBEDF80 PPE at FFFFF6FB7DBF0000 PDE at FFFFF6FB7E0000F8
PTE at FFFFF6FC0001FC08
contains 0000000000199063 contains 0000000000198063 contains 00000000001E3063
// contains 0000000003F81121
pfn 199 ---DA--KWEV pfn 198 ---DA--KWEV pfn 1e3 ---DA--KWEV
/*pfn 3f81 */ -G--A--KREV
/*
dt _MMPFN 有8种状态,但是使用的只有六种
PMMPFNLIST MmPageLocationList[NUMBER_OF_PAGE_LISTS] = {
&MmZeroedPageListHead,
&MmFreePageListHead,
&MmStandbyPageListHead,
&MmModifiedPageListHead,
&MmModifiedNoWritePageListHead,
&MmBadPageListHead,
NULL,//未使用
NULL //未使用};
*/
MmZeroedPageListHead,
MmFreePageListHead,
MmStandbyPageListHead,
MmModifiedPageListHead,
MmModifiedNoWritePageListHead,
MmBadPageListHead, //MmMarkPhysicalMemoryAsBad(StartAddress,size)
4.Active Page
+0x018 u3 : <unnamed-tag>
+0x000 ReferenceCount : 0
+0x002 e1 : _MMPFNENTRY
+0x000 PageLocation : 0y000//当前页面状态的类型。
+0x000 WriteInProgress : 0y0//读
+0x000 Modified : 0y0//页面已修改状态。如果要将页面移除内存,需要换出数
据。
+0x000 ReadInProgress : 0y0//写
+0x000 CacheAttribute : 0y01
+0x001 Priority : 0y000
+0x001 Rom : 0y0
+0x001 InPageError : 0y0
+0x001 KernelStack : 0y0
+0x001 RemovalRequested : 0y0
+0x001 ParityError : 0y0
6.u4
+0x028 u4 : <unnamed-tag>
+0x000 PteFrame :
0y1111111111111111111111111111111111111111111111111101 (0xffffffffffffd)
//指向该页面的PTE所在页表的页帧编号。
+0x000 Unused : 0y000
+0x000 PfnImageVerified : 0y0
+0x000 AweAllocation : 0y0
+0x000 PrototypePte : 0y0
+0x000 PageColor : 0y000000 (0)
7.MmZeroedPageListHead
kd>// dq MmZeroedPageListHead
fffff800`04108400 00000000`00058599 00000000`00000000
fffff800`04108410 ffffffff`ffffffff ffffffff`ffffffff
fffff800`04108420 00000000`00000000 00000000`00000000
fffff800`04108430 00000000`00000000 00000000`00000000
fffff800`04108440 00000000`00000000 00000000`00000000
fffff800`04108450 00000000`00000000 00000000`00000000
fffff800`04108460 00000000`00000000 00000000`00000000
fffff800`04108470 00000000`00000000 00000000`00000000
kd>// dq MMPFNDATABASE
fffff800`04106220 fffffa80`00000000 000001f4`00000040
fffff800`04106230 0002ffff`00000250 00000003`00000000
fffff800`04106240 00000000`00000000 00080000`00000001
fffff800`04106250 00000001`ffffffff fffffa80`019e1000
fffff800`04106260 01d714e0`07a4bd3e fffffa80`018f4600
fffff800`04106270 fffffa80`01932830 00000000`00004840
fffff800`04106280 00000000`5c7fe000 fffffa80`018d1c10
fffff800`04106290 00000000`00000001 fffffa80`018eb570
kd>// dt _MMPFN fffffa80`00000000+58599*0x30 -r1
nt!_MMPFN
+0x000 u1 : <unnamed-tag>
// +0x000 Flink : 0x58559
+0x000 WsIndex : 0x58559
+0x000 Event : 0x00000000`00058559 _KEVENT
+0x000 Next : 0x00000000`00058559 Void
+0x000 VolatileNext : 0x00000000`00058559 Void
+0x000 KernelStackOwner : 0x00000000`00058559 _KTHREAD
+0x000 NextStackPfn : _SINGLE_LIST_ENTRY
+0x008 u2 : <unnamed-tag>
// +0x000 Blink : 0x585d9
+0x000 ImageProtoPte : 0x00000000`000585d9 _MMPTE
+0x000 ShareCount : 0x585d9
+0x010 PteAddress : 0xfffff680`00000002 _MMPTE
+0x000 u : <unnamed-tag>
+0x010 VolatilePteAddress : 0xfffff680`00000002 Void
+0x010 Lock : 0n2
+0x010 PteLong : 0xfffff680`00000002
+0x018 u3 : <unnamed-tag>
+0x000 ReferenceCount : 0
+0x002 e1 : _MMPFNENTRY
+0x000 e2 : <unnamed-tag>
+0x01c UsedPageTableEntries : 0
+0x01e VaType : 0 ''
+0x01f ViewCount : 0 ''
+0x020 OriginalPte : _MMPTE
+0x000 u : <unnamed-tag>
+0x020 AweReferenceCount : 0n128
+0x028 u4 : <unnamed-tag>
+0x000 PteFrame :
0y1111111111111111111111111111111111111111111111111101 (0xffffffffffffd)
+0x000 Unused : 0y000
+0x000 PfnImageVerified : 0y0
+0x000 AweAllocation : 0y0
+0x000 PrototypePte : 0y0
+0x000 PageColor : 0y000000 (0)
8.MmFreePageListHead
9.MmStandbyPageListHead
10.MmBadPageListHead
2.VAD解析
申请的非分页内存,无需初始化,就挂上了物理页。
申请的分页内存,需要立即初始化挂上物理页,否则PTE无效,需要错误处理例程修复(异常)。
硬件PTE:有效,直接指向物理内存。
原型PTE:无效,可能换出,可能需要请求
U位:当前核心的物理页,被别的核心访问,置1。
2.无效PTE
1.页面文件
PFN* 代表物理页换出到磁盘中的磁盘编号。从0开始。
换入时,CreateFileMapping映射的文件句柄,加上 页面文件偏移,就是换出的数据的地址。
判断流程,先判断P位有效位,再判断10 11位。
私有内存:
映射内存:
3.页面正在转移
不能判断换入还是换出
4.原型PTE
当PTE的P位=0时,如果是共享内存,则查询Section->ProtoPte。
2.拿物理页小技巧
BOOL AllocateUserPhysicalPages(
HANDLE hProcess,
PULONG_PTR NumberOfPages,
PULONG_PTR PageArray
);
NTSTATUS
NtAllocateUserPhysicalPages(
__in HANDLE ProcessHandle,
__inout PULONG_PTR NumberOfPages,
__out_ecount(*NumberOfPages) PULONG_PTR UserPfnArray
)
//这样拿到的物理页,可以挂内核和用户地址,不在VAD内,但是不可执行。
3.VAD
VAD:一般用于管理用户地址,内核地址不用VAD。
4.修改Start和End
//上个VAD的end 修改成下个VAD的end
87e3a378 7 8b10 8b11 2 Private READWRITE
87e3af68 8 8b20 8b21 2 Private READWRITE
//修改后
87e3a378 7 8b10 /*8b21*/ 2 Private READWRITE
87e3af68 8 8b20 8b21 2 Private READWRITE
//效果就是R3.API遍历内存,遍历不到
3.VAD映射
私有内存是short类型 映射内存是long类型
win7以后:dt _MMVAD_long
StatringVpn EndingVpn算法
ZwQueryMemory就是遍历VAD。
1.实验
int main()
{
system("pause");
PVOID mem=VirtualAlloc(0,0x1,MEM_RESERVE,PAGE_NOACCESS);
printf("地址:%x\n", mem);
system("pause");
mem=VirtualAlloc(0, 0x1,MEM_COMMIT,PAGE_NOACCESS);
printf("地址:%x\n", mem);
system("pause");
return 0;
}
//未申请前
1: kd> !vad 8665a3a8+0x278
VAD Level Start End Commit
8665a620 0 0 0 4613 Mapped NO_ACCESS Pagefile
section, shared commit 0
86ad7380 5 10 1f 0 Mapped READWRITE Pagefile
section, shared commit 0x10
880a7718 4 20 2f 0 Mapped READWRITE Pagefile
section, shared commit 0x10
86662178 5 30 33 0 Mapped READONLY Pagefile
section, shared commit 0x4
883c18a8 3 40 40 0 Mapped READONLY Pagefile
section, shared commit 0x1
88241268 4 50 50 1 Private READWRITE
87f8e510 5 60 c6 0 Mapped READONLY
\Windows\System32\locale.nls
8667d838 2 d0 1fd 84 Mapped Exe EXECUTE_WRITECOPY
\Users\TalShang\Desktop\38.VAD映射.exe
87f77b28 4 2d0 3cf 4 Private READWRITE
86470918 3 430 52f 30 Private READWRITE
86675588 5 75950 7599b 4 Mapped Exe EXECUTE_WRITECOPY
\Windows\System32\apphelp.dll
866471b8 4 75d00 75d49 3 Mapped Exe EXECUTE_WRITECOPY
\Windows\System32\KernelBase.dll
883905f0 5 77110 771e3 2 Mapped Exe EXECUTE_WRITECOPY
\Windows\System32\kernel32.dll
866576e0 1 77900 77a3b 9 Mapped Exe EXECUTE_WRITECOPY
\Windows\System32\ntdll.dll
8826a090 3 77b40 77b40 0 Mapped Exe EXECUTE_WRITECOPY
\Windows\System32\apisetschema.dll
882a5510 4 7f6f0 7f7ef 0 Mapped READONLY Pagefile
section, shared commit 0x5
86f3c860 2 7ffa0 7ffd2 0 Mapped READONLY Pagefile
section, shared commit 0x33
86597d60 3 7ffd6 7ffd6 1 Private READWRITE
86675538 4 7ffdf 7ffdf 1 Private READWRITE
//
// Both fields are zero, return failure.
//
return MM_INVALID_PROTECTION;
}
ProtectCode = MmUserProtectionToMask2[Field2];
}
else {
if (Field2 != 0) {
//
// Both fields are non-zero, return failure.
//
return MM_INVALID_PROTECTION;
}
ProtectCode = MmUserProtectionToMask1[Field1];
}
CCHAR MmUserProtectionToMask1[16] = {
0,
MM_NOACCESS,//这个是0x18
MM_READONLY,
-1,
MM_READWRITE,
-1,
-1,
-1,
MM_WRITECOPY,
-1,
-1,
-1,
-1,
-1,
-1,
-1 };
CCHAR MmUserProtectionToMask2[16] = {
0,
MM_EXECUTE,
MM_EXECUTE_READ,
-1,
MM_EXECUTE_READWRITE,
-1,
-1,
-1,
MM_EXECUTE_WRITECOPY,
-1,
-1,
-1,
-1,
-1,
-1,
-1 };
#define MM_ZERO_ACCESS 0 // this value is not used.
#define MM_READONLY 1
#define MM_EXECUTE 2
#define MM_EXECUTE_READ 3
#define MM_READWRITE 4 // bit 2 is set if this is writable.
#define MM_WRITECOPY 5
#define MM_EXECUTE_READWRITE 6
#define MM_EXECUTE_WRITECOPY 7
#define MM_PROTECTION_WRITE_MASK 4
#define MM_PROTECTION_COPY_MASK 1
#define MM_PROTECTION_OPERATION_MASK 7 // mask off guard page and nocache.
#define MM_PROTECTION_EXECUTE_MASK 2
//第二次申请
8830ae18 5 200 200 0 Private NO_ACCESS
87e88138 4 210 210 1 Private NO_ACCESS
1.当页是大页的时候,会查询VAD.__MMVAD_FLAGS.Protection
2.当页是小页的时候,会查询_MMPFN的原型PTE。
//在执行的时候,发现当前PTE的属性和原型PTE不一样,就会把PTE改成原型PTE的属性,原理是发生异常,
走OE号异常中断。
锁住硬件PTE,可以让CE的Protection显示为NO_ACCESS
//CE
CE的Protection不会显示PTE的属性。
1.修改PTE
1.修改系统DLL地址PTE
总结:修改后没有作用,属性会被还原。
//系统DLL的地址:7ffdd000
//修改成功
0: kd> !pte 7ffdd000
VA 7ffdd000
PDE at C0601FF8 PTE at C03FFEE8
contains 0000000038DEC867 contains 8000000037392865
pfn 38dec ---DA--UWEV pfn 37392 ---DA--UR-V
1: kd> !pte 7ffdd000
VA 7ffdd000
PDE at C0601FF8 PTE at C03FFEE8
contains 0000000007781867 contains 0000000000000000
pfn 7781 ---DA--UWEV not valid
//又被改回去了
1: kd> !pte 7ffdd000
VA 7ffdd000
PDE at C0601FF8 PTE at C03FFEE8
contains 0000000038DEC867 contains 8000000037392867
pfn 38dec ---DA--UWEV pfn 37392 ---DA--UW-V
2.修改普通地址PTE
总结:修改也无效。
2.修改PFN项(私有内存
0: kd> dd MMPFNDATABASE
83f7c700 84e00000 00000000 00000001 0003ffff
83f7c710 0003ff7e 7ffeffff 80000000 7fff0000
83f7c720 80741000 000bffff c03fff78 c0601ff8
83f7c730 00000000 00000001 00000002 00000001
83f7c740 00000000 00000002 00000000 00000001
83f7c750 00000002 00000001 00000000 00000002
83f7c760 00000000 00000000 00000000 80000000
83f7c770 00000000 00000000 00000000 00000000
//MMPFN
0: kd> dt _MMPFN 84e00000+2ffc2*1c
nt!_MMPFN
+0x000 u1 : <unnamed-tag>
+0x004 u2 : <unnamed-tag>
+0x008 PteAddress : 0xc03ffef8 _MMPTE
+0x008 VolatilePteAddress : 0xc03ffef8 Void
+0x008 Lock : 0n-1069547784
+0x008 PteLong : 0xc03ffef8
+0x00c u3 : <unnamed-tag>
// +0x010 OriginalPte : _MMPTE
+0x010 AweReferenceCount : 0n128
+0x018 u4 : <unnamed-tag>
//OriginalPte
0: kd> dt _MMPTE 84e00000+2ffc2*1c+0x10 -r1
nt!_MMPTE
+0x000 u : <unnamed-tag>
+0x000 Long : 0x80
+0x000 VolatileLong : 0x80
+0x000 HighLow : _MMPTE_HIGHLOW
+0x000 Flush : _HARDWARE_PTE
+0x000 Hard : _MMPTE_HARDWARE
+0x000 Proto : _MMPTE_PROTOTYPE
// +0x000 Soft : _MMPTE_SOFTWARE
+0x000 TimeStamp : _MMPTE_TIMESTAMP
+0x000 Trans : _MMPTE_TRANSITION
+0x000 Subsect : _MMPTE_SUBSECTION
+0x000 List : _MMPTE_LIST
//Soft
0: kd> dt _MMPTE_SOFTWARE 84e00000+2ffc2*1c+0x10 -r1
nt!_MMPTE_SOFTWARE
+0x000 Valid : 0y0
+0x000 PageFileLow : 0y0000
// +0x000 Protection : 0y00100 (0x4) 0x4代表可读可写,0110(6)=011000000=C0
+0x000 Prototype : 0y0
+0x000 Transition : 0y0
+0x000 InStore : 0y0
+0x000 Unused1 : 0y0000000000000000000 (0)
+0x000 PageFileHigh : 0y00000000000000000000000000000000 (0)
#define MM_READWRITE 4 // bit 2 is set if this is writable.
#define MM_WRITECOPY 5
#define MM_EXECUTE_READWRITE 6
0: kd> ed 84e00000+2ffc2*1c+0x10 C0
0: kd> dt _MMPTE_SOFTWARE 84e00000+2ffc2*1c+0x10 -r1
nt!_MMPTE_SOFTWARE
+0x000 Valid : 0y0
+0x000 PageFileLow : 0y0000
+0x000 Protection : 0y00110 (0x6)
+0x000 Prototype : 0y0
+0x000 Transition : 0y0
+0x000 InStore : 0y0
+0x000 Unused1 : 0y0000000000000000000 (0)
+0x000 PageFileHigh : 0y00000000000000000000000000000000 (0)
3.MiLocateAddres
X64和x86不一样
1.IDAF5分析
ntdll!_MM_AVL_TABLE
+0x000 BalancedRoot : _MMADDRESS_NODE
+0x014 DepthOfTree : Pos 0, 5 Bits
+0x014 Unused : Pos 5, 3 Bits
+0x014 NumberGenericTableElements : Pos 8, 24 Bits//元素个数
+0x018 NodeHint : Ptr32 Void //某个热点结点
+0x01c NodeFreeHint : Ptr32 Void
1: kd> dt _MMADDRESS_NODE
ntdll!_MMADDRESS_NODE
+0x000 u1 : <unnamed-tag>
+0x004 LeftChild : Ptr32 _MMADDRESS_NODE
+0x008 RightChild : Ptr32 _MMADDRESS_NODE
+0x00c StartingVpn : Uint4B
+0x010 EndingVpn : Uint4B
_MMADDRESS_NODE* i=NULL;
_MMVAD* v5=NULL;
int result = 0;
int v6 = 0;
for (i = vadroot->BalancedRoot.RightChild;; i = (_MMADDRESS_NODE*)v5)
{
if (Vpn < i->StartingVpn)
{
v5 = (_MMVAD*)i->LeftChild;
if (!v5)
{
LABEL_7:
v6 = 2;
result = v6;
goto LABEL_8;
}
continue;
}
if (Vpn <= i->EndingVpn)
{
break;
}
v5 = (_MMVAD*)i->RightChild;
if (!v5)
{
v6 = 3;
goto LABEL_7;
}
}
result = 1;
LABEL_8:
*Node = (_MMVAD*)i;
return result;
}
KAPC_STATE apc = { 0 };
KeStackAttachProcess(ep,&apc);
KeUnstackDetachProcess(&apc);
return STATUS_SUCCESS;
}
3.驱动锁页
注意:只针对私有内存;
注意:只是在页面无效或发生页面异常,对PTE进行修复。
if (!MmIsAddressValid((PVOID)VirtualAddress))
{
KeUnstackDetachProcess(&apc);
return FALSE;
}
}
}
if (!offest)
{
KeUnstackDetachProcess(&apc);
return FALSE;
}
MMPFNdatabase = *(ULONG*)(*(ULONG*)(MMPFNdatabase + offest));
PHYSICAL_ADDRESS phy;
phy.QuadPart = CR3;
PVOID mem=MmMapIoSpace(phy,0x1000,MmCached);
PDPTE=*(ULONG64*)((ULONG64)mem + PDPTE * 8);
MmUnmapIoSpace(mem,0x1000);
PDPTE = PDPTE & ~0xFFF;
phy.QuadPart = PDPTE;
mem = MmMapIoSpace(phy, 0x1000, MmCached);
PDE = *(ULONG64*)((ULONG64)mem + PDE * 8);
MmUnmapIoSpace(mem, 0x1000);
PDE = PDE & ~0xFFF;
phy.QuadPart = PDE;
mem = MmMapIoSpace(phy, 0x1000, MmCached);
PTE = *(ULONG64*)((ULONG64)mem + PTE * 8);
MmUnmapIoSpace(mem, 0x1000);
PTE = PTE & ~0xFFF;
KeUnstackDetachProcess(&apc);
return TRUE;
}
3.VAD映射2
1.共享内存
//MappedSubsection _MSUBSECTION,一段一段地管理内存段
0: kd> dt _MSUBSECTION 0x87986058
nt!_MSUBSECTION
+0x000 ControlArea : 0x87986008 _CONTROL_AREA
+0x004 SubsectionBase : 0x8b27f038 _MMPTE
+0x008 NextSubsection : 0x87986078 _SUBSECTION
+0x008 NextMappedSubsection : 0x87986078 _MSUBSECTION
+0x00c PtesInSubsection : 1
+0x010 UnusedPtes : 0
+0x010 GlobalPerSessionHead : (null)
+0x014 u : <unnamed-tag>
+0x018 StartingSector : 0
+0x01c NumberOfFullSectors : 2
+0x020 u1 : <unnamed-tag>
+0x024 LeftChild : 0x8b27f040 _MMSUBSECTION_NODE
+0x028 RightChild : 0x87986098 _MMSUBSECTION_NODE
+0x02c DereferenceList : _LIST_ENTRY [ 0x14b - 0x0 ]
+0x034 NumberOfMappedViews : 6
0: kd> dx -id 0,0,863e98e8 -r1 (*((ntkrpamp!_MSUBSECTION *)0x87986058)).u
(*((ntkrpamp!_MSUBSECTION *)0x87986058)).u [Type: <unnamed-tag>]
[+0x000] LongFlags : 0x2 [Type: unsigned long] //0x2
[+0x000] SubsectionFlags [Type: _MMSUBSECTION_FLAGS]
0: kd> dt _MMSUBSECTION_FLAGS 0x87986058
nt!_MMSUBSECTION_FLAGS
+0x000 SubsectionAccessed : 0y0
+0x000 Protection : 0y00100 (0x4)//VAD的Protect描述符
+0x000 StartingSector4132 : 0y0110000000 (0x180)
+0x002 SubsectionStatic : 0y0
+0x002 GlobalMemory : 0y0
+0x002 DirtyPages : 0y0
+0x002 Spare : 0y1
+0x002 SectorEndOffset : 0y100001111001 (0x879)
//+0x030 ViewLinks : _LIST_ENTRY [0x87db4cc0-0x87dbbf48]
0: kd> dt _MMVAD 0x87db4cc0-0x30
nt!_MMVAD
+0x000 u1 : <unnamed-tag>
+0x004 LeftChild : (null)
+0x008 RightChild : (null)
+0x00c StartingVpn : 0x76e40
+0x010 EndingVpn : 0x76f13
+0x014 u : <unnamed-tag>
+0x018 PushLock : _EX_PUSH_LOCK
+0x01c u5 : <unnamed-tag>
+0x020 u2 : <unnamed-tag>
+0x024 Subsection : 0x86fe7e98 _SUBSECTION
+0x024 MappedSubsection : 0x86fe7e98 _MSUBSECTION
+0x028 FirstPrototypePte : 0x92b64928 _MMPTE
+0x02c LastContiguousPte : 0xfffffffc _MMPTE
+0x030 ViewLinks : _LIST_ENTRY [ 0x87da6b10 - 0x87e310c0 ]//触发写拷贝,
会把这个链表断链一节。只是修改一部分,没有将整个DLL复制一份。
+0x038 VadsProcess : 0x87db4d41 _EPROCESS //别的进程
0: kd> dt _EPROCESS 0x87db4d41-1
ntdll!_EPROCESS
+0x16c ImageFileName : [15] "svchost.exe"
0: kd> dt _MMVAD_FLAGS2
nt!_MMVAD_FLAGS2
+0x000 FileOffset : Pos 0, 24 Bits
+0x000 SecNoChange : Pos 24, 1 Bit
+0x000 OneSecured : Pos 25, 1 Bit
+0x000 MultipleSecured : Pos 26, 1 Bit
+0x000 Spare : Pos 27, 1 Bit
+0x000 LongVad : Pos 28, 1 Bit
+0x000 ExtendableFile : Pos 29, 1 Bit
+0x000 Inherit : Pos 30, 1 Bit
+0x000 CopyOnWrite : Pos 31, 1 Bit//
2.VAD锁页
1.私有内存大多第一个 _MMVAD_FLAGS就够了
2.映射内存还需要_MMVAD_FLAGS2
或者通过 ZwCreateSection ZwMapViewOfFile 来锁页
int main()
{
HMODULE h = LoadLibraryA("ntdll.dll");
ZwCreateSectionProc ZwCreateSection = (ZwCreateSectionProc)GetProcAddress(h,
"ZwCreateSection");
NtMapViewOfSectionProc NtMapViewOfSection =
(NtMapViewOfSectionProc)GetProcAddress(h, "ZwMapViewOfSection");
system("pause");
return 0;
}
4.页异常
1.通用异常错误码
3~16位
2.页异常
P 0.错误是由不存在的页引起的
1.错误是由页保护冲突引起的
W/R 0.读的时候错误
1.写的时候错误
U/S 0.内核模式访问的错误
1.用户模式访问的错误
RSVD 0.故障不是由保留位冲突引起的
1.The fault was caused by a reserved bit set to 1 in some paging-structure
entry.
I/D 0.执行的时候没有出错
1.执行的时候出错
PK 0.The fault was not caused by protection keys.//密钥保护
1.There was a protection-key violation.
SS 0.The fault was not caused by a shadow-stack access.
1.The fault was caused by a shadow-stack access.//影子栈,win10上被废弃,Linux
上被使用
SGX 0.The fault is not related to SGX.
1.The fault resulted from violation of SGX-specific access-control
requirements.//新机制,目前没使用
3.异常保存位置
_KTRAP_FRAME
struct _KTRAP_FRAME
{
union
{
ULONGLONG ErrorCode; //在这里
ULONGLONG ExceptionFrame;
};
};
4.检测MDL和MmMapIo
x64和x86同理
1.检测MDL
IoAllocateMdl会在KPCR.PRCB.PPLookasideList[3]中找到ExAllocatePoolWithTag这个函数。
检测方案:
1.替换KPCR指针
2.HOOK获取映射的虚拟地址
3.检测线程是否挂靠
4.获取线程所属进程
5.获取当前进程对比
6.统计计数,封号
_KPRCB
kd> !prcb
PRCB for Processor 0 at fffff80004007e80:
//效果并不怎么明显咩,待修复
#include<ntifs.h>
ULONG64 ExAllocate_Func = 0;
VOID DRIVERUNLOAD(_In_ struct _DRIVER_OBJECT* DriverObject)
{
ExAllocate_Func = (ULONG64)ExAllocatePoolWithTag;
}
BOOLEAN KeIsAttachedProcess();
PVOID ExAllocatePoolWithTag_HOOK(
POOL_TYPE PoolType,
SIZE_T NumberOfBytes,
ULONG Tag
)
{
//直接读r13d 就能出地址 这里懒得实现
DbgBreakPoint();
if (KeIsAttachedProcess() == TRUE)
{
DbgBreakPoint();
PEPROCESS ep=IoThreadToProcess(NtCurrentThread());
PUNICODE_STRING name = NULL;
NTSTATUS sta=SeLocateProcessImageName(ep,&name);
if (sta==STATUS_SUCCESS)
{
DbgPrintEx(77, 0, "%wZ",name);
ExFreePool(name);
}
}
PVOID mem=ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag);
return mem;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
pDriver->DriverUnload = DRIVERUNLOAD;
DbgBreakPoint();
//单线程 只修改了一个KPCR
ULONG64 _KPRCB=__readgsqword(0x20);
ULONG64 ExAllocate = *(ULONG64*)(_KPRCB+0x780+3*0x10);
ExAllocate = ExAllocate + 0x030;
ExAllocate_Func = ExAllocate;
*(ULONG64*)ExAllocate =(ULONG64)ExAllocatePoolWithTag_HOOK;
return STATUS_SUCCESS;
}
5.x64基础
1.X64调用约定
算法和x86一样,当前RIP+指令长度+偏移
注意:64位下,地址偏移计算通常会进位,去掉进位 才是真正的地址。
2.内联汇编
.code ;代表节区
ret
abc endp
end
.code ;代表节区
end
int main()
{
int result=abc(2, 1);
printf("%d\n", result);
return 0;
}
3.x64函数debug和Realse
4.x64inline hook
//12字节HOOK
mov rax,0x123456789
jmp rax //call rax call qword ptr [rax]
//6字节HOOK
FF25 XXXXXXXX
//12字节HOOK
FF25 XXXXXXXXXXXXXXXX
//call
FF15
//代码在文件夹内
6.x64段
x64下 base和limit对于64位的段 不再有意义
//待研究 x86/x64体系探索
rd msr 0xc0000080
L位:1是64位模式,0是compatibility模式
x64系统段是128位。
1.调用门
7.x64页表
PML4的NX位不影响PTE的NX位
1G页 只有PML4 PDPTE
//需要确定物理地址的大小 (通过CPUID)
1.X64分页
2.CPUID
0~7位,是物理地址的位数
8~15位,是线性地址的大小
48 C7 C0 08 00 00 80 mov rax, 0xFFFFFFFF80000008;rax功能号
moc rcx, 0;注意清空rcx
0F A2 cpuid
rax=0000000000003030
0x30=48 根地址总线
0x30=48 位线性地址大小
#include<intrin.h> //封装了汇编指令
__cpuid(功能号,返回值)
__cpuidex(,,)
3.获取PML4,PDPTE,PDE,PTE
1.WIN7获取
2.WIN10获取
//获取动态基址
.text:00000001402C1570 ; PVOID __stdcall
MmGetVirtualForPhysical(PHYSICAL_ADDRESS PhysicalAddress)
.text:00000001402C1570
public MmGetVirtualForPhysical
.text:00000001402C1570
MmGetVirtualForPhysical proc near ; DATA XREF: .pdata:00000001405345E4↓o
.text:00000001402C1570 48 8B C1 mov
rax, rcx
.text:00000001402C1573 48 C1 E8 0C shr
rax, 0Ch
.text:00000001402C1577 48 8D 14 40 lea
rdx, [rax+rax*2]
.text:00000001402C157B 48 03 D2 add
rdx, rdx
.text:00000001402C157E 48 B8 08 00 00 00 80 FA FF FF mov
rax, 0FFFFFA8000000008h
.text:00000001402C1588 48 8B 04 D0 mov
rax, [rax+rdx*8]
.text:00000001402C158C 48 C1 E0 19 shl
rax, 19h
//.text:00000001402C1590 48 BA 00 00 00 00 80 F6 FF FF mov
rdx, 0FFFFF68000000000h
8.突破页表隔离
//Intel独有双页表,AMD只有单页表,双页表限制的是NX位,让R0不能执行R3的地址,R3不能执行R0
//整个CR3有 4096/16=0x100(256)行,前面128项是用户态,后面128项是内核态
//R3进R0,切换CR3,不会刷新TLB,因为从缓存里拿的
1: kd> dt _KPROCESS ffffad0e02bbc080
nt!_KPROCESS
+0x028 DirectoryTableBase : 0x84b2a002 //内核CR3结尾为2
//两个CR3相差一个页
+0x280 UserDirectoryTableBase : 0x84b29001 //用户CR3结尾为1
+0x288 AddressPolicy : 0 ''//地址策略,非0代表只有一个CR3。 这个地方不能修改,蓝
屏。
//注意:
修改用户CR3的PML4项,也要修改内核CR3的PML4项,因为有轮询扫。
1.用户CR3的保存
1: kd> dt _EPROCESS
nt!_EPROCESS
+0x500 Vm : _MMSUPPORT_FULL
1: kd> dt _MMSUPPORT_FULL
nt!_MMSUPPORT_FULL
+0x000 Instance : _MMSUPPORT_INSTANCE
+0x0c0 Shared : _MMSUPPORT_SHARED
1: kd> dt _MMSUPPORT_SHARED
nt!_MMSUPPORT_SHARED
+0x000 WorkingSetLock : Int4B
+0x004 GoodCitizenWaiting : Int4B
+0x008 ReleasedCommitDebt : Uint8B
+0x010 ResetPagesRepurposedCount : Uint8B
+0x018 WsSwapSupport : Ptr64 Void
+0x020 CommitReleaseContext : Ptr64 Void
+0x028 AccessLog : Ptr64 Void
+0x030 ChargedWslePages : Uint8B
+0x038 ActualWslePages : Uint8B
+0x040 WorkingSetCoreLock : Uint8B
+0x048 ShadowMapping : Ptr64 Void //0xffff9e01`aeeb700 保存了用户cr3,拿不到
PDPTE。
2.KVASCODE
CR3只映射KVASCODE,因为用户CR3在系统调用时,只用到KPCR
3.无附加映射
//win10上无附加映射失败
MmMapIoSpace,不附加,每个物理地址都映射失败。
//需要修改PreviousMode 保证是内核模式
//驱动调用Nt系列函数一直失败 不知道为什么
PVOID MapPhysicalAddress(ULONG64 PhysicalAddress)
{
HANDLE sectionHandle;
OBJECT_ATTRIBUTES obj;
UNICODE_STRING sectionName;
RtlInitUnicodeString(§ionName,L"\\Device\\PhysicalMemory");
InitializeObjectAttributes(&obj,§ionName,OBJ_CASE_INSENSITIVE,NULL,NULL);
// NTSTATUS sta=NtOpenSection(§ionHandle,SECTION_ALL_ACCESS,&obj);
NTSTATUS sta=ZwOpenSection(§ionHandle, SECTION_ALL_ACCESS, &obj);
if (!NT_SUCCESS(sta)) return 0;
PVOID mem = 0;
LARGE_INTEGER offest;
offest.QuadPart = PhysicalAddress;
SIZE_T size = PAGE_SIZE;
sta=ZwMapViewOfSection(
sectionHandle,
NtCurrentProcess(),
&mem,
0,
PAGE_SIZE,
&offest,
&size,
ViewUnmap,
MEM_TOP_DOWN,
PAGE_READWRITE
);
if (!NT_SUCCESS(sta))
{
NtClose(sectionHandle);
return 0;
}
NtClose(sectionHandle);
return mem;
}
9.x64下x86API进内核
TEB64 +0x2000 =TEB32
PEB64 -0x1000 =PEB32
垫片:IAT HOOK 原名叫windows shim 在 winodws/AppPatch目录下
垫片:https://www.freebuf.com/articles/system/114287.html
x64下x86程序,能做简单HOOK达到无痕。
1.x86api简单调用流程
用Yzdbg x86dbg看不到wow64cpu
//win7 是fs:[0x30] win10直接变成一个地址
2.WOW64CPU
1.找到对应点
IDA找到对应点
2.调用流程
1.判断服务号
2.计算TEB32
3.获取函数地址
4.调用函数whNtOpenProcess
5.调用完然后返回到WOW64cpu
4.WOW64.whNtOpenProcess
5.WOW64CPU
1.返回
9.x64下x86API进内核
1.ntdll64
2.syscall
EIP 176h
CS 174h
SS
ESP 175
EFALG
RIP c0000082h //rdmsr c0000082h
CS c0000081h
SS 174h
RSP 175
EFALG
1.RIP c0000082h
2.CS c0000081h
3.KiSystemCall64
1.保存堆栈KTRAP_FRAME,关闭内核读写数据隔离
2.将eprocess的SecurityDomain设置到KPCR中,并写两次MSR
//3.疑似打开Smep,但并没有置位
4.切换Mxcsr
//5.判断协程
6.调用函数
7.切回Mxcsr
8.恢复DR寄存器
9.写两次MSR
//10.判断KVA,切换用户CR3
11.恢复rcx=rip,r11=eflags,切回rsp,rbp,调用sysret.
1.保存堆栈KTRAP_FRAME,关闭内核读写数据隔离
2.将eprocess的SecurityDomain设置到KPCR中,并写两次MSR
3.疑似打开Smep,但并没有置位
4.切换Mxcsr
5.判断协程
6.调用函数
10.Instruent HOOK监控X86进程API
r10=rip,rip=KPROCESS.InstrumentaionCallback
1.NtSetInformationProcess
总结:
1.当前线程所属进程有SeDebug权限
2.目标进程是WOW64PROCESS
3.当前线程所属进程是WOW64PROCESS
4.函数地址是R3地址
5.函数地址放到PEB32.0x254
NTSYSCALLAPI NTSTATUS NTAPI NtSetInformationProcess(
__in HANDLE ProcessHandle,
__in PROCESSINFOCLASS ProcessInformationClass,
__in_bcount(ProcessInformationLength) PVOID ProcessInformation,
__in ULONG ProcessInformationLength
);
ProcessInstrumentationCallback = 40;
11.X64SSDT HOOK
__readmsr(0xc0000082) //判断影子页表 KiSystemCall64Shadow
1.注意低四字节进位问题
/*
利用LARGE_INTERGE 来代替ULONG64 保证低四字节的计算不会影响到高四字节。
*/
2.x64SSDT 算法
/*
1.取出函数表地址 fffff800`5bc24cc0
/*
2.按四字节解析函数地址表,找到函数偏移
3.*(函数地址表+索引*4)=函数偏移
4.函数地址表+函数偏移>>4(二进制)=目标函数地址
3.PsGetProcessSectionBaseAddress
判断x86进程还是x86进程
//eprocess
struct _EWOW64PROCESS* WoW64Process; //0x428
struct _EWOW64PROCESS
{
VOID* Peb; //PEB32
//0x0
USHORT Machine;
//0x8
enum _SYSTEM_DLL_TYPE NtdllType;
//0xc
};
5.WIN7/WIN10特征码定位查找SSDT
//特征码定位SSDT表,SSDT表的算法
//查找函数序号,进程遍历(名称对比),导出表遍历(x32和x64)
//注意低四字节溢出到高四字节
.h
//功能区
ULONG64 findSSDTtable();
ULONG32 findOrderByFuncName(char* funcName);
ULONG64 findFuncAddrBySSDT(ULONG64 SSDTtable, ULONG64 order);
ULONG64 ssdt=findSSDTtable();
ULONG32 ord=findOrderByFuncName("ZwWriteVirtualMemory");
ULONG64 funcAddr=findFuncAddrBySSDT(ssdt,ord);
return STATUS_SUCCESS;
}
12.注入之修复Shellcode
/*
利用shellcode来对PE文件进行修复,但是火绒注入的shellcode是利用VirtualAlloc来申请内存,所以
要修复它的申请内存。
1.bp bl bc *(xx)
13.注入绕过堆栈回溯
1.堆栈回溯
1.RtlWalkFrameChain() 可以看到堆栈的调用地址
//主要靠ebp来找地址
//堆栈回溯示例
typedef ULONG (WINAPI* pRtlWalkFrameChain)(
OUT PVOID* Callers,
IN ULONG Count,
IN ULONG Flags);
int main()
{
ULONG64 retAddr[20] = { 0 };
pRtlWalkFrameChain RtlWalkFrameChain =
(pRtlWalkFrameChain)GetProcAddress(GetModuleHandleA("ntdll.dll"),"RtlWalkFrameCh
ain");
RtlWalkFrameChain((PVOID*)retAddr,20,0);
//判断返回地址即可
return 0;
}
2.反堆栈回溯
call xxxxxxxx
改为:push retAddress(用for循环搜索0xc3)
jmp xxxxxxxx
__asm
{
push ebp //或者可以自建堆栈
push 标签
push retAddress
push retAddress
push retAddress
push retAddress
push retAddress
push retAddress
push retAddress //理论上能搞无限个
mov ebp,0//目的是破坏ebp,让RtlWalkFrameChain结果为0
****
jmp xxxxxxxx
标签:
pop ebp
}
//实例
/*void AntiEbpTrace()
{
PUCHAR retAddr = (PUCHAR)Game_func;
__asm
{
push ebp
push label
push retAddr
push retAddr
push retAddr
mov ebp,0
jmp Game_func
label:
pop ebp
}
}*/
方法2:
E8 FF FF FF FF 不好写
3.注入流程
/*x86注入*/
BOOLEAN injectDll(HANDLE gamePid, PUCHAR dllAddr, ULONG fileSize)
{
if (!gamePid || !dllAddr || !fileSize) return FALSE;
//game Pid
PEPROCESS ep = NULL;
NTSTATUS sta=PsLookupProcessByProcessId(gamePid,&ep);
if (!NT_SUCCESS(sta)) return FALSE;
ObDereferenceObject(ep);
/*申请内存*/
PVOID dll_File = NULL;
PUCHAR dll_Shellcode = NULL;
PVOID dll_inMem = NULL;
SIZE_T dll_File_Size = fileSize;
SIZE_T dll_Shellcode_Size = sizeof(MemLoadShellcode_x86);
SIZE_T dll_inMem_Size = sizeofImage;
KAPC_STATE apc = { 0 };
KeStackAttachProcess(ep, &apc);
do
{
sta=ZwAllocateVirtualMemory(
NtCurrentProcess(),
&dll_File,
0,
&dll_File_Size,
MEM_COMMIT,
PAGE_READWRITE);
if (!NT_SUCCESS(sta)) break;
sta = ZwAllocateVirtualMemory(
NtCurrentProcess(),
&dll_Shellcode,
0,
&dll_Shellcode_Size,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (!NT_SUCCESS(sta)) break;
sta = ZwAllocateVirtualMemory(
NtCurrentProcess(),
&dll_inMem,
0,
&dll_inMem_Size,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (!NT_SUCCESS(sta)) break;
} while (0);
if (dll_File == NULL || dll_Shellcode == NULL || dll_inMem == NULL)
{
KeUnstackDetachProcess(&apc);
ExFreePool(dllNewAddr);
return FALSE;
}
memcpy(dll_File, dllNewAddr, dll_File_Size);
memcpy(dll_Shellcode, MemLoadShellcode_x86, dll_Shellcode_Size);
memset(dll_inMem, 0, dll_inMem_Size);
/*修复shellcode*/
*(PULONG)&dll_Shellcode[0x346] = dll_inMem;
/*找到创建线程*/
ULONG64 ssdt=findSSDTtable();
if (ssdt)
{
ULONG32 order=findOrderByFuncName("ZwCreateThreadEx");
if (order)
{
ZwCreateThreadEx=
(pfnZwCreateThreadEx)findFuncAddrBySSDT(ssdt,order);
}
}
if (!ZwCreateThreadEx)
{
KeUnstackDetachProcess(&apc);
ExFreePool(dllNewAddr);
return FALSE;
}
/*启动线程*/
MODE mode = KernelMode;
SetThreadMode(KeGetCurrentThread(), mode, &mode);
HANDLE th = NULL;
sta=ZwCreateThreadEx(
&th,
THREAD_ALL_ACCESS,
NULL,
NtCurrentProcess(),
dll_Shellcode,
dll_File,
0,
0,
0x10000,
0x20000,
0
);
SetThreadMode(KeGetCurrentThread(), mode, &mode);
if (NT_SUCCESS(sta))
{
PETHREAD eth = 0;
sta=ObReferenceObjectByHandle(th,THREAD_ALL_ACCESS,
*PsThreadType,KernelMode,ð,0);
NtClose(th);
if (NT_SUCCESS(sta))
{
KeWaitForSingleObject(eth,Executive,KernelMode,FALSE,0);
/*线程运行完毕*/
memset(dll_inMem, 0, PAGE_SIZE);//清空PE头
ObDereferenceObject(eth);
ZwFreeVirtualMemory(NtCurrentProcess(),
&dll_File,
&dll_File_Size,
MEM_RELEASE);
ZwFreeVirtualMemory(NtCurrentProcess(),
&dll_Shellcode,
&dll_Shellcode_Size,
MEM_RELEASE);
}
}
else
{
ZwFreeVirtualMemory(NtCurrentProcess(),
&dll_File,
&dll_File_Size,
MEM_RELEASE);
ZwFreeVirtualMemory(NtCurrentProcess(),
&dll_Shellcode,
&dll_Shellcode_Size,
MEM_RELEASE);
ZwFreeVirtualMemory(NtCurrentProcess(),
&dll_inMem,
&dll_inMem_Size,
MEM_RELEASE);
}
ExFreePool(dllNewAddr);
KeUnstackDetachProcess(&apc);
return TRUE;
}
4.程序崩溃原因
5.x64修复64位dll
/*修复0x511 8字节*/
/*注意Pe的SizeOfimage*/
14.隐藏内存注入
1.指定地址挂物理页(绕过内存检测)
/*隐藏内存注入*/
/*win10需要修复VAD的问题*/
/*1903 1909需要修复物理内存的问题*/
/*需要提前附加到目标进程*/
BOOLEAN SetPhyPages(ULONG64 virtualAddress, ULONG64* pfnArray, ULONG
numberofPages);
BOOLEAN AllocateAndHideMemory(ULONG64 virtualAddress,ULONG fileSize)
{
typedef NTSTATUS(__stdcall* pfnZwAllocateUserPhysicalPages)(
HANDLE ProcessHandle,
PULONG_PTR NumberOfPages,
PULONG_PTR UserPfnArray);
pfnZwAllocateUserPhysicalPages ZwAllocateUserPhysicalPages = NULL;;
/*找到申请内存*/
ULONG64 ssdt = findSSDTtable();
if (ssdt)
{
ULONG32 order = findOrderByFuncName("ZwAllocateUserPhysicalPages");
if (order)
{
ZwAllocateUserPhysicalPages =
(pfnZwAllocateUserPhysicalPages)findFuncAddrBySSDT(ssdt, order);
}
}
if (!ZwAllocateUserPhysicalPages) return FALSE;
/*调用*/
ULONG64 numberofPages = (fileSize >> 12) + 1+ 0x20;
ULONG64* pfnArray = ExAllocatePool(PagedPool,(numberofPages*8)+1);
memset(pfnArray, 0, (numberofPages * 8) + 1);
MODE mode = KernelMode;
SetThreadMode(KeGetCurrentThread(), mode, &mode);
NTSTATUS
sta=ZwAllocateUserPhysicalPages(NtCurrentProcess(),&numberofPages,pfnArray);
SetThreadMode(KeGetCurrentThread(), mode, &mode);
if (!NT_SUCCESS(sta)) return FALSE;
p = (HardwarePte*)GetPDE(tempaddr);
if (MmIsAddressValid(p) && p->valid == 0)
{
ULONG64 initPTE = (pfnArray[i] << 12) | 0x867;
memcpy(p, &initPTE, 8);
i++;
}
p = (HardwarePte*)GetPTE(tempaddr);
if (MmIsAddressValid(p) && p->valid == 0)
{
ULONG64 initPTE = (pfnArray[i] << 12) | 0x867;
memcpy(p, &initPTE, 8);
//i++;
}
tempaddr += PAGE_SIZE;
}
return TRUE;
}
2.错误
//这个地址对于x64进程,会蓝屏(win7也会),错误号为0x1A,VAD错误
3.1903 1909蓝屏原因
蓝屏原因:aimAddress_x86这个x86进程下的最高地址,仍然在VAD的描述范围内,修改它的pte等,
会导致描述不一样。检测到描述
不一样就蓝屏。(win10 2004-2009取消了这个检测)
即使解决了VAD的错误,还有别的错误(mmPFNdatabase)
总结原因:
4.(widnows版本)
Windows 10(21h1) 19043
4.绕过线程检测
//1.线程回调,给线程的函数直接写0xc3 ret了
//2.Kernel32!BaseThreadInitThunnk hook
//3.线程创建日志。
5.另一种寻找地址的思路
15.CPU异常
操作系统指定CPU的EIP,当CPU产生异常的时候,查CPU的IDT表,IDT表的函数由操作系统指定。
最终返回给操作系统。
1.什么是异常?
异常:意外出错,CPU主动生成信息,调用IDT函数,交给操作系统处理,操作系统选择性派发给开发者。
中断:意外打断,CPU意外被打断后派发
2.异常有几种方式? CPU异常 和软件模拟异常
1.异常注册,注册回调函数//(VEH)(SEH)
2.异常登记 记录出错信息和出错位置,打包成一个结构体
3.异常派发
3.异常类型:
SEH:编译器或者C/C++中,try{} catech{} 线程级异常,方便判断
VEH: 进程级异常,全局性的,不方便判断
4.为什么要设计两套异常?
VEH:全局性的,比如一触发异常就打印日志,不需要判断异常类型。//VEH捕获异常太多,处理太多,慢
SEH:局部性的,可以判断异常类型 //SEH捕获一部分异常,快
//注意:在内核中,进程只是地址空间,所以内核中只有SEH异常。
/*
除0异常 是0号异常
通用异常13号
缺页异常14号
*/
1.VEH简单解析
int main()
{
AddVectoredExceptionHandler(0, except);
int x = 0;
int y = 0;
int z = x / y;
printf("-------------\n");
return 0;
}
16.异常2
异常的三种类型
1.断点异常
2.页异常
3.通用异常
异常流程分析(残)
返回ntdll.KiUserDispatch
17.异常3(VEH)
先调用VEH,再调用SEH,再调用VCH(做日志记录)
VEH,有个异常链表,出现异常要一个个call链表的地址,
遇到 EXCEPTION_CONTINUE_EXECUTION,表示不再call地址
遇到 EXCEPTION_CONTINUE_SEARCH,表示继续call地址
1.VEH调用流程
1.注册流程
VEH
{
LIST_ENTRY unknow;
QWORD count; //默认1
QWORD func;//调用RtlEncodePointer加密后的,RtlDecodePointer调用后解密
}
2.call流程
2.反VEH思路
1.RtlAllocateHeap
2.VEH结构替换
3.&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessUsingVEH:1
18.VEH 无痕HOOK
51.项目
19.SEH(结构化异常)
定义:SEH是线程级异常,栈的异常。
struct _NT_TIB64
{
ULONGLONG ExceptionList;//SEH异常链表地址,SEH 是 SLIST 单链表。
//最后一个节点的地址是-1 (0xFFFFFFFF)
ULONGLONG func //函数地址
};
1.自定义添加SEH异常
void test()
{
Exceptioninfo node;
pExceptioninfo ps;
__asm
{
mov eax,fs:[0]
mov ps, eax
}
node.pExceptioninfo = ps;
node.func = (ULONG)SehHandler;
__asm
{
lea eax, [node]
mov fs: [0],eax
}
int x = 0;
int a = x / x;
}
int main()
{
test();
printf("线程继续执行\n");
system("pause");
return 0;
}
2.SEH校验
3.RtlLookupFunctionEntry
20.VMP云编译与加签名
利用bat文件来编译。
1.一些命令解析
2.BAT编译
注意:使用windows计划任务定时编译(比如下午四点)
21.SEH 2
1.C++异常与C异常
//cpp
try
{
}
catch ( )
{
};
//C
__try
{
}
__except(1)
{
};
__try
{
}
__finally
{
};
2.SEH异常例子
int main()
{
__try
{
int x, y, z;
x = 0;
y = 0;
z = x / y;
printf("---continue Execute---\n");
}
__except (test(GetExceptionCode(),GetExceptionInformation()))
{
printf("---异常发生---\n");
};
}
3.探究SEH信息
3是 "如何派发的"
4.nginx
编译好的驱动放在 html目录下面
22.简单调试器
1.DebugActiveProcess逆向
1.DbgUiConnectToDbg@0(创建调试对象)
ZwCreateDebugObject@16(TEB32+0xF24,0x1F000F,OBJECT_ATTRIBUTE,1)
ObInsertObject 插入到句柄表中
2.DbgUiDebugActiveProcess@4
1.NtDebugActiveProcess@8
1.DbgkpPostFakeProcessCreateMessages@12
看思维导图
2.DbgkpSetProcessDebugObject@16
看思维导图
3.DbgUiDebugActiveProcess@4
看思维导图
2.WaitForDebugEvent逆向
细节总结
好用的细节:
RemoteprocessObject.RundownProtect 修改进程锁
不好用的细节:
11.Sfilter
控制设备与卷设备
1.有fastfat.sys(FAT32) ntfs.sys(ntfs)两种文件系统,文件系统给有自己的一个控制设备,和几个
没有名字的卷设备
2.当某个逻辑卷(C D E F)使用了某个文件系统,就会生成对应的"无名字的"卷设备 ,由卷管理器生成
但是符号链接为 "c:",设备对象为"\\Device\HarddiskVolume1",是真实设备,不是文件系统的设备
对象。
Sfilter的控制设备
1.设备对象名字挂在 "\\FileSystem\\Filters" 下。如果没有"Filters",就挂在 "\\FileSystem"下
2.设备类型为 FILE_DEVICE_DISK_FILE_SYSTEM
12.MiniFilter
1.FltRegisterFilter返回0xC0000034
启动驱动的函数
int Flags = 0;
RegSetValueExA(hkey,
"Flags",
NULL,
REG_DWORD,
(const byte*)&Flags,
4);
RegFlushKey(hkey);
RegCloseKey(hkey);
hkey = NULL;
}
}
BOOL flag = StartServiceA(hService, 0, 0);
if (flag == TRUE)
{
printf("启动服务成功!\r\n");
flag = TRUE;
}
else
{
printf("启动服务失败Error:%d\r\n",GetLastError());
flag = FALSE;
}
CloseServiceHandle(hService);
CloseServiceHandle(hSc);
hService = NULL;
hSc = NULL;
if (flag == TRUE)
{
return TRUE;
}
}
if (!hService)
{
CloseServiceHandle(hService);
}
if (!hSc)
{
CloseServiceHandle(hSc);
}
return FALSE;
}
2.FltGetFileNameInformation
FltParseFileNameInformation(nameInfo);
1: kd> dt nameInfo
Local var @ 0xffffb206b99640f8 Type _FLT_FILE_NAME_INFORMATION*
0xffffb709`30418e30
+0x000 Size : 0x78
+0x002 NamesParsed : 0xf 'FltParseFileNameInformation()后为0xf 之前是0'
+0x004 Format : 1
+0x008 Name : _UNICODE_STRING
"\Device\HarddiskVolume2\Windows\System32\cmd.exe"
+0x018 Volume : _UNICODE_STRING "\Device\HarddiskVolume2"
+0x028 Share : _UNICODE_STRING ""
+0x038 Extension : _UNICODE_STRING "exe" '()后有'
+0x048 Stream : _UNICODE_STRING ""
+0x058 FinalComponent : _UNICODE_STRING "cmd.exe" '()后有'
+0x068 ParentDir : _UNICODE_STRING "\Windows\System32\" '()后有'