首页
社区
课程
招聘
[原创]通过修改物理内存实现跨进程内存读写
发表于: 2024-5-6 21:10 3321

[原创]通过修改物理内存实现跨进程内存读写

2024-5-6 21:10
3321

学习一下利用修改物理内存来跨进程内存读写

系统:win10 21h1 x64

编译环境: vs2022 详情见附录

基础

虚拟地址转物理地址

虚拟地址也称线性地址,一个线性地址+进程的DirBase地址可以转换成物理地址。先来看线性地址的含义

在x64体系中只实现了48位的virtual address,高16位被用作符号扩展,这高16位要么全是0,要么全是1。
不同于x86体系结构,每级页表寻址长度变成9位,由于在x64体系结构中,普通页大小仍为4KB,然而数据却表示64位长,因此一个4KB页在x64体系结构下只能包含512项内容,所以为了保证页对齐和以页为单位的页表内容换入换出,在x64下每级页表寻址部分长度定位9位。

img

从Page Map Level 4(PML4)开始到最后的物理地址,每一个都可以理解成一层页表的索引,索引值就是线性地址上不同的部分,分别缩写是PML4, PDPE, PDE,PTE。

注意,并不是取出来的值就直接指向一下一个页表,个人PC上一般是取值的12-35bit的值,其他置0。具体的后面见代码,或参考看雪的文章

img

使用windbg可以先查看进程对应的DirBase地址,然后再使用!vtop Dirbase地址 虚拟地址查看虚拟地址对应的物理地址,如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
3: kd> !process 258c 0
Searching for Process with Cid == 258c
PROCESS ffffc40d2ab48340
    SessionId: 1  Cid: 258c    Peb: a6e35cd000  ParentCid: 1250
    DirBase: 235ae6000  ObjectTable: ffff998138d4ee00  HandleCount:  38.
    Image: test.exe
 
3: kd> !vtop 235ae6000 0000A6E334FB00
Amd64VtoP: Virt 000000a6e334fb00, pagedir 0000000235ae6000
Amd64VtoP: PML4E 0000000235ae6008
Amd64VtoP: PDPE 00000001087fb4d8
Amd64VtoP: PDE 000000010f7fc8c8
Amd64VtoP: PTE 00000000ad207a78
Amd64VtoP: Mapped phys 000000011b10cb00
Virtual address a6e334fb00 translates to physical address 11b10cb00.

上面得到DirBase的值是235ae6000,然后需要查看物理地址的虚拟地址是0x0000A6E334FB00,就使用命令

1
!vtop 235ae6000 0000A6E334FB00

得到最后对应的物理地址是0x11b10cb00。

简单例子代码如下:

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>
 
int main() {
    char flag[] = {"flag{b7285d748dd042a4929d3dbec778e637}"};
 
    printf("value addr: %p", flag);
    getchar();
 
    return 0;
}

运行后可以打印出来字符串的虚拟地址0000A6E334FB00,然后通过上述步骤得到物理地址。

我们尝试看看物理内存中的字符串,现在已经确定物理内存的地址是0xD0000147,使用!db 0xD0000147来查看物理内存,记住要!,没有感叹号的是查看虚拟内存的

1
2
3
4
5
6
7
8
9
3: kd> !db 0x11b10cb00
#11b10cb00 66 6c 61 67 7b 62 37 32-38 35 64 37 34 38 64 64 flag{b7285d748dd
#11b10cb10 30 34 32 61 34 39 32 39-64 33 64 62 65 63 37 37 042a4929d3dbec77
#11b10cb20 38 65 36 33 37 7d 00 00-f8 82 20 82 f7 7f 00 00 8e637}.... .....
#11b10cb30 00 00 00 00 00 00 00 00-20 13 1f 82 f7 7f 00 00 ........ .......
#11b10cb40 00 00 00 00 00 00 00 00-99 13 1f 82 f7 7f 00 00 ................
#11b10cb50 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#11b10cb60 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#11b10cb70 00 00 00 00 00 00 00 00-44 73 d3 08 fe 7f 00 00 ........Ds......

可以看到物理内存上的字符串内容。

DirBase地址获取

DirBase地址除了通过上述windbg直接得到这个值以外,还可以通过EPROCESS来得到,这个是代码比较需要的

1
2
3
4
5
6
7
8
9
10
11
12
13
3: kd> dt _eprocess ffffc40d2ab48340
nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x438 ProcessLock      : _EX_PUSH_LOCK
   +0x440 UniqueProcessId  : 0x00000000`0000258c Void
   +0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffffc40d`2cb43788 - 0xffffc40d`2cd444c8 ]
   +0x458 RundownProtect   : _EX_RUNDOWN_REF
   .....
3: kd> dx -id 0,0,ffffc40d23c95040 -r1 (*((ntkrnlmp!_KPROCESS *)0xffffc40d2ab48340))
(*((ntkrnlmp!_KPROCESS *)0xffffc40d2ab48340))                 [Type: _KPROCESS]
    [+0x000] Header           [Type: _DISPATCHER_HEADER]
    [+0x018] ProfileListHead  [Type: _LIST_ENTRY]
    [+0x028] DirectoryTableBase : 0x235ae6000 [Type: unsigned __int64]

DirectoryTableBase的值就是DirBase地址了,实际上就是EPROCESS + 0x28的偏移

还可以通过获取CR3寄存器的值,CR3寄存器中的值就是页目录表的物理地址,也就是DirBase

思路

目的:进程B可以通过修改物理内存的内容来修改进程A内存中的数据

实验设置:进程A泄露一个变量地址,然后等待进程B修改,修改后再回复执行,打印变量值看是否修改成功

内核部分思路:

  • 将R3的虚拟地址转换为物理地址
  • 使用MmCopyMemory复制物理地址内容
  • 修改内容
  • 使用mmMapIoSpaceEx将修改后的内容映射回物理地址

代码实现

被修改进程代码

这里写一个例子来充当被攻击(修改内存)的进程。主要就是打印变量内容和地址,然后暂停程序等待一段时间(等待被驱动修改),然后再打印变量内容,看看是否被驱动修改内存成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>
 
int main() {
    char flag[] = {"flag{b7285d748dd042a4929d3dbec778e637}"};
 
    printf("value addr: %p\r\n", flag);
    printf("flag data: %s\r\n", flag);
    getchar();
 
    printf("flag data Now: %s\r\n", flag);
    return 0;
}

驱动代码

这里就是主要逻辑,通过驱动代码取修改目标进程的内存内容,做到跨进程内存读取,修改。

定义一个读取物理内存函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// @brief 读取物理地址的内存内容
/// @param address 物理地址
/// @param buffer 复制内存地址到buffer
/// @param size 复制大小
/// @param BytesTransferred 读取的字节数
/// @return
NTSTATUS ReadPhysicalAddress(IN PVOID64 address, OUT PVOID64 buffer,
                             IN SIZE_T size, OUT SIZE_T* BytesTransferred)
{
    MM_COPY_ADDRESS Read          = {0};
    Read.PhysicalAddress.QuadPart = (LONG64)address;
    return MmCopyMemory(
        buffer, Read, size, MM_COPY_MEMORY_PHYSICAL, BytesTransferred);
}

再定义一个写入物理内存的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/// @brief 写入指定内容到物理内存中
/// @param address 被写入的物理地址
/// @param buffer 需要写入的缓冲区指针
/// @param size 需要写入的大小
/// @param BytesTransferred 写入成功后的大小
/// @return
NTSTATUS WritePhysicalAddress(IN PVOID64 address, IN PVOID64 buffer,
                              IN SIZE_T size, OUT SIZE_T* BytesTransferred)
{
    PVOID            map;
    PHYSICAL_ADDRESS Write = {0};
 
    if (!address) {
        kprintf("Address value error. \r\n");
        return STATUS_UNSUCCESSFUL;
    }
 
    Write.QuadPart = (LONG64)address;
    map            = MmMapIoSpaceEx(Write, size, PAGE_READWRITE);
 
    if (!map) {
        kprintf("Write Memory faild.\r\n");
        return STATUS_UNSUCCESSFUL;
    }
    RtlCopyMemory(map, buffer, size);
    *BytesTransferred = size;
    MmUnmapIoSpace(map, size);
    return STATUS_SUCCESS;
}

我们需要将虚拟地址转换成物理地址,那么首先需要线性地址+DirBase地址,DirBase地址获取是通过PEPROCESS+0x28偏移读取的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// @brief 通过EPROCESS获取DirBase值
/// @param pid 进程PID
/// @param pDirbase 一个UINT64指针,获取成功后返回值
/// @return
NTSTATUS GetDirBaseByEprocess(IN UINT64 pid, OUT PUINT64 pDirbase)
{
    PEPROCESS pEprocess;
    NTSTATUS  status;
 
    status = PsLookupProcessByProcessId((HANDLE)pid, &pEprocess);
    if (!NT_SUCCESS(status)) {
        kprintf("[!] Get Pid=%d _EPROCESS failed!", pid);
        return STATUS_UNSUCCESSFUL;
    }
 
    *pDirbase =
        *(PUINT64)((PUCHAR)pEprocess + WIN10_21H1_EPROCESS2DIRBASE_OFFSET);
    kprintf("[+] uDirBase ==> %llx\r\n", *pDirbase);
 
    return STATUS_SUCCESS;
}

得到DirBase后,就可以虚拟地址转换物理地址。

传入虚拟地址后,取后48bit,然后将这48bit分成4个9bit和最后12bit,分别是PML4,PDPE,PDE,PTE和页内偏移offset。需要注意的是DirBase就已经是物理内存了,所以读取DirBase内容并且一层一层读取都要用自定义函数ReadPhysicalAddress

每一层都是基地址+8*偏移,读取的内容,取12-35bit就是下一层的基地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/// @brief 传入DirBase值和虚拟地址后,回转化成一个物理地址返回
/// @param DirBase DirBase地址,传入一个UINT64值
/// @param addr 传入一个指向虚拟地址的指针,转化成物理地址后会修改这个指针的值
/// @return
NTSTATUS TranslateAddress(IN UINT64 DirBase, _Inout_ PUINT64 addr)
{
    UINT16   PML4, PDPE, PDE, PTE, offset;
    UINT64   mask = 0x7fffff000;
    UINT64   uTmp;
    SIZE_T   BytesTransferred;
    NTSTATUS status;
 
    offset = *addr & 0xfff;
    PTE    = (*addr >> 12) & 0x1ff;
    PDE    = (*addr >> (12 + 9)) & 0x1ff;
    PDPE   = (*addr >> (9 * 2 + 12)) & 0x1ff;
    PML4   = (*addr >> (9 * 3 + 12)) & 0x1ff;
 
    status = ReadPhysicalAddress(
        (PVOID64)(DirBase + PML4 * 8), &uTmp, sizeof(uTmp), &BytesTransferred);
    uTmp &= mask;
    kprintf("[+] PML4(%x) ==> %llx\r\n", PML4, uTmp);
 
    status = ReadPhysicalAddress(
        (PVOID64)(uTmp + PDPE * 8), &uTmp, sizeof(uTmp), &BytesTransferred);
    uTmp &= mask;
    kprintf("[+] PDPE(%x) ==> %llx\r\n", PDPE, uTmp);
 
    status = ReadPhysicalAddress(
        (PVOID64)(uTmp + PDE * 8), &uTmp, sizeof(uTmp), &BytesTransferred);
    uTmp &= mask;
    kprintf("[+] PDE(%x) ==> %llx\r\n", PDE, uTmp);
 
    status = ReadPhysicalAddress(
        (PVOID64)(uTmp + PTE * 8), &uTmp, sizeof(uTmp), &BytesTransferred);
    uTmp &= mask;
    kprintf("[+] PTE(%x) ==> %llx\r\n", PTE, uTmp);
 
    *addr = uTmp + offset;
    kprintf("[+] physical address: %llx\r\n", *addr);
    return STATUS_SUCCESS;
}

最后再主函数中定义一下逻辑。这里直接手动指定进程号和目标进程打印出来的变量地址,然后将虚拟地址转化成物理地址,读取物理地址上的内容并打印出来看看是否正确。再修改物理地址上的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING path)
{
    NTSTATUS status;
    UINT64   pid, uAddr, uDirBase;
    SIZE_T   BytesTransferred;
    UCHAR    charArry[40] = {0};
    UCHAR    example[40] = {"Yes I change memory by physical"};
 
    pid   = 10276;
    uAddr = 0x3629FAFB80;
 
    pDriver->DriverUnload = DriverUnload;
    // 手动指定进程号
    status = GetDirBaseByEprocess(pid, &uDirBase);
    if (!NT_SUCCESS(status)) {
        kprintf("[!] Get DirBase address failed!\r\n");
        return STATUS_UNSUCCESSFUL;
    }
 
    // 将虚拟地址转化成物理地址
    status = TranslateAddress(uDirBase, &uAddr);
    if (!NT_SUCCESS(status)) {
        kprintf("[!] Translate address failed!\r\n");
        return STATUS_UNSUCCESSFUL;
    }
 
    // 读取物理地址内容, 然后修改内容
    ReadPhysicalAddress((PVOID64)uAddr, charArry, 40, &BytesTransferred);
    kprintf("[+] data is %s\r\n", charArry);
 
    // 将example字符串写入物理内存
    WritePhysicalAddress((PVOID64)uAddr, example, 40, &BytesTransferred);
    kprintf("[+] Write end\r\n");
 
    return STATUS_SUCCESS;
}

结果

目标进程

image-20240505235113447

驱动

image-20240505235141011

可以看到目标进程的指定内存被修改,同时驱动也跨进程读取,修改内存成功

参考

使用CR3切换实现读取指定进程内存数据 | pnpon.com

c/c++/易语言驱动内存无痕读写源码 | csdn.net

将虚拟地址转换为物理地址 | learn.microsoft.com

X64下的虚拟地址到物理地址的转换 | bbs.kanxue.com

几种挖掘任意读写驱动的方法 | myzxcg.com/

附录

驱动全部代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include <ntifs.h>
#include <ntddk.h>
#include <intrin.h>
#include <ntdef.h>
 
#define WIN10_21H1_EPROCESS2DIRBASE_OFFSET 0x28
 
#define kprintf(...) \
    KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, __VA_ARGS__))
 
 
NTSTATUS GetDirBaseByEprocess(IN UINT64, OUT PUINT64);
NTSTATUS TranslateAddress(IN UINT64, _Inout_ PUINT64);
NTSTATUS ReadPhysicalAddress(IN PVOID64, OUT PVOID64, IN SIZE_T, OUT SIZE_T*);
NTSTATUS WritePhysicalAddress(IN PVOID64, IN PVOID64, IN SIZE_T, OUT SIZE_T*);
NTKERNELAPI PPEB NTAPI  PsGetProcessPeb(IN PEPROCESS Process);
NTKERNELAPI PVOID NTAPI PsGetProcessWow64Process(IN PEPROCESS Process);
 
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
    kprintf("驱动已卸载.\r\n");
}
 
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING path)
{
    NTSTATUS status;
    UINT64   pid, uAddr, uDirBase;
    SIZE_T   BytesTransferred;
    UCHAR    charArry[40] = {0};
    UCHAR    example[40] = {"Yes I change memory by physical"};
 
    pid   = 10276;
    uAddr = 0x3629FAFB80;
 
    pDriver->DriverUnload = DriverUnload;
    // 手动指定进程号
    status = GetDirBaseByEprocess(pid, &uDirBase);
    if (!NT_SUCCESS(status)) {
        kprintf("[!] Get DirBase address failed!\r\n");
        return STATUS_UNSUCCESSFUL;
    }
 
    // 将虚拟地址转化成物理地址
    status = TranslateAddress(uDirBase, &uAddr);
    if (!NT_SUCCESS(status)) {
        kprintf("[!] Translate address failed!\r\n");
        return STATUS_UNSUCCESSFUL;
    }
 
    // 读取物理地址内容, 然后修改内容
    ReadPhysicalAddress((PVOID64)uAddr, charArry, 40, &BytesTransferred);
    kprintf("[+] data is %s\r\n", charArry);
 
    // 将example字符串写入物理内存
    WritePhysicalAddress((PVOID64)uAddr, example, 40, &BytesTransferred);
    kprintf("[+] Write end\r\n");
 
    return STATUS_SUCCESS;
}
 
/// @brief 读取物理地址的内存内容
/// @param address 物理地址
/// @param buffer 复制内存地址到buffer
/// @param size 复制大小
/// @param BytesTransferred 读取的字节数
/// @return
NTSTATUS ReadPhysicalAddress(IN PVOID64 address, OUT PVOID64 buffer,
                             IN SIZE_T size, OUT SIZE_T* BytesTransferred)
{
    MM_COPY_ADDRESS Read          = {0};
    Read.PhysicalAddress.QuadPart = (LONG64)address;
    return MmCopyMemory(
        buffer, Read, size, MM_COPY_MEMORY_PHYSICAL, BytesTransferred);
}
 
/// @brief 写入指定内容到物理内存中
/// @param address 被写入的物理地址
/// @param buffer 需要写入的缓冲区指针
/// @param size 需要写入的大小
/// @param BytesTransferred 写入成功后的大小
/// @return
NTSTATUS WritePhysicalAddress(IN PVOID64 address, IN PVOID64 buffer,
                              IN SIZE_T size, OUT SIZE_T* BytesTransferred)
{
    PVOID            map;
    PHYSICAL_ADDRESS Write = {0};
 
    if (!address) {
        kprintf("Address value error. \r\n");
        return STATUS_UNSUCCESSFUL;
    }
 
    Write.QuadPart = (LONG64)address;
    map            = MmMapIoSpaceEx(Write, size, PAGE_READWRITE);
 
    if (!map) {
        kprintf("Write Memory faild.\r\n");
        return STATUS_UNSUCCESSFUL;
    }
    RtlCopyMemory(map, buffer, size);
    *BytesTransferred = size;
    MmUnmapIoSpace(map, size);
    return STATUS_SUCCESS;
}
 
/// @brief 通过EPROCESS获取DirBase值
/// @param pid 进程PID
/// @param pDirbase 一个UINT64指针,获取成功后返回值
/// @return
NTSTATUS GetDirBaseByEprocess(IN UINT64 pid, OUT PUINT64 pDirbase)
{
    PEPROCESS pEprocess;
    NTSTATUS  status;
 
    status = PsLookupProcessByProcessId((HANDLE)pid, &pEprocess);
    if (!NT_SUCCESS(status)) {
        kprintf("[!] Get Pid=%d _EPROCESS failed!", pid);
        return STATUS_UNSUCCESSFUL;
    }
 
    *pDirbase =
        *(PUINT64)((PUCHAR)pEprocess + WIN10_21H1_EPROCESS2DIRBASE_OFFSET);
    kprintf("[+] uDirBase ==> %llx\r\n", *pDirbase);
 
    return STATUS_SUCCESS;
}
 
/// @brief 传入DirBase值和虚拟地址后,回转化成一个物理地址返回
/// @param DirBase DirBase地址,传入一个UINT64值
/// @param addr 传入一个指向虚拟地址的指针,转化成物理地址后会修改这个指针的值
/// @return
NTSTATUS TranslateAddress(IN UINT64 DirBase, _Inout_ PUINT64 addr)
{
    UINT16   PML4, PDPE, PDE, PTE, offset;
    UINT64   mask = 0x7fffff000;
    UINT64   uTmp;
    SIZE_T   BytesTransferred;
    NTSTATUS status;
 
    offset = *addr & 0xfff;
    PTE    = (*addr >> 12) & 0x1ff;
    PDE    = (*addr >> (12 + 9)) & 0x1ff;
    PDPE   = (*addr >> (9 * 2 + 12)) & 0x1ff;
    PML4   = (*addr >> (9 * 3 + 12)) & 0x1ff;
 
    status = ReadPhysicalAddress(
        (PVOID64)(DirBase + PML4 * 8), &uTmp, sizeof(uTmp), &BytesTransferred);
    uTmp &= mask;
    kprintf("[+] PML4(%x) ==> %llx\r\n", PML4, uTmp);
 
    status = ReadPhysicalAddress(
        (PVOID64)(uTmp + PDPE * 8), &uTmp, sizeof(uTmp), &BytesTransferred);
    uTmp &= mask;
    kprintf("[+] PDPE(%x) ==> %llx\r\n", PDPE, uTmp);
 
    status = ReadPhysicalAddress(
        (PVOID64)(uTmp + PDE * 8), &uTmp, sizeof(uTmp), &BytesTransferred);
    uTmp &= mask;
    kprintf("[+] PDE(%x) ==> %llx\r\n", PDE, uTmp);
 
    status = ReadPhysicalAddress(
        (PVOID64)(uTmp + PTE * 8), &uTmp, sizeof(uTmp), &BytesTransferred);
    uTmp &= mask;
    kprintf("[+] PTE(%x) ==> %llx\r\n", PTE, uTmp);
 
    *addr = uTmp + offset;
    kprintf("[+] physical address: %llx\r\n", *addr);
    return STATUS_SUCCESS;
}

编译环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Microsoft Visual Studio Community 2022
Version 17.9.6
VisualStudio.17.Release/17.9.6+34728.123
Microsoft .NET Framework
Version 4.8.09032
 
Installed Version: Community
 
Visual C++ 2022   00482-90000-00000-AA134
Microsoft Visual C++ 2022
 
ASP.NET and Web Tools   17.9.199.22661
ASP.NET and Web Tools
 
Azure App Service Tools v3.0.0   17.9.199.22661
Azure App Service Tools v3.0.0
 
C# Tools   4.9.0-3.24121.1+a98c90d56455379836dd5c845b35fa932b00cfa3
C# components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used.
 
Debugging Tools for Windows   10.0.26090.1
Integrates the Windows Debugger functionality (http://go.microsoft.com/fwlink/?linkid=223405) in Visual Studio.
 
Microsoft JVM Debugger   1.0
Provides support for connecting the Visual Studio debugger to JDWP compatible Java Virtual Machines
 
NuGet Package Manager   6.9.2
NuGet Package Manager in Visual Studio. For more information about NuGet, visit https://docs.nuget.org/
 
Test Adapter for Boost.Test   1.0
Enables Visual Studio's testing tools with unit tests written for Boost.Test.  The use terms and Third Party Notices are available in the extension installation directory.
 
Test Adapter for Google Test   1.0
Enables Visual Studio's testing tools with unit tests written for Google Test.  The use terms and Third Party Notices are available in the extension installation directory.
 
TypeScript Tools   17.0.30103.2001
TypeScript Tools for Microsoft Visual Studio
 
Visual Basic Tools   4.9.0-3.24121.1+a98c90d56455379836dd5c845b35fa932b00cfa3
Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used.
 
Visual Studio IntelliCode   2.2
AI-assisted development for Visual Studio.
 
Windows Driver Kit   10.0.26090.1
Headers, libraries, and tools needed to develop, debug, and test Windows drivers (msdn.microsoft.com/en-us/windows/hardware/gg487428.aspx)

[培训]《安卓高级研修班(网课)》月薪三万计划,掌 握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞6
支持
分享
最新回复 (5)
雪    币: 19785
活跃值: (29397)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2024-5-7 09:52
1
雪    币: 207
活跃值: (222)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
请问一下,使用MmMapIoSpaceEx的话,target os version为win7的话就过不了编译;使用MmMapIoSpace,Win10高版本就会失效;我为了兼容性使用的是ZwMapViewOfSection,但是无论是哪个系统,映射到PDE的时候,映射出的就不是PDE的地址了,但pdpte的地址和PDE的索引都是对的,该如何解决呢
2024-5-11 11:58
0
雪    币: 1856
活跃值: (911)
能力值: ( LV5,RANK:75 )
在线值:
发帖
回帖
粉丝
4
ACZR 请问一下,使用MmMapIoSpaceEx的话,target os version为win7的话就过不了编译;使用MmMapIoSpace,Win10高版本就会失效;我为了兼容性使用的是ZwMapVi ...
MmMapIoSpaceEx最低支持的版本我看官方文档是win10, win7应该不行,你可以获取系统版本信息,然后根据版本信息选择MmMapIoSpaceEx或者MmMapIoSpace。至于PDE的值可能就需要手动调试看看具体的问题是什么了,我也不能确定
6天前
0
雪    币: 207
活跃值: (222)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
mi1itray.axe MmMapIoSpaceEx最低支持的版本我看官方文档是win10, win7应该不行,你可以获取系统版本信息,然后根据版本信息选择MmMapIoSpaceEx或者MmMapIoSpace。至于PDE ...
是这样,带Ex的必须在Win10以上用,不带Ex的到了Win10高版本就无效了。为了兼容才用的ZwMapViewOfSection,但是这个函数映射到PDE就不对了,就很怪,我帖子里有发这个代码,如果有空的话可以帮忙看下
6天前
0
雪    币: 0
活跃值: (65)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
拉闸了
5天前
0
游客
登录 | 注册 方可回帖
返回