# R3越级SSDT表进行R0级别进程读取框架

**URL:** <https://forum.90sec.com/t/topic/1325>\
**Category:** 技术文章\
**Tags:** 逆向\
**Created:** [2020 年10 月 5 日 07:52 UTC](https://forum.90sec.com/t/topic/1325 "2020-10-05T07:52:38Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

作者： ![Wker](https://forum.90sec.com/user_avatar/forum.90sec.com/wker/32/8604_2.png) [@Wker](https://forum.90sec.com/u/Wker)\
发布日期： [2020 年10 月 5 日 07:52 UTC](https://forum.90sec.com/t/topic/1325/1 "2020-10-05T07:52:38Z")

</div>

根据上篇文章的结尾，R3用户层越级SSDT表执行内存的读写。  
这是什么意思呢？首先我们还是说一下类似`OpenProcess`这个函数的调用原理，其实上篇文章也提及到，这里再重复一下。  
其实调用OpenProcess函数，实际上我们是去ntdll中调用ZwOpenProcess（通过反汇编发现NtOpenprocess是同一个地址），我们只需要hookSSDT表中的NtOpenprocess函数就可以实现全局的hook OpenProcess函数，达到了保护进程的效果，也就是别的进程无法打开进程，为此我写了一个驱动去实现这个效果，配合上R3的用户交互，大家简单看下效果：

 ![protect](https://forum.90sec.com/uploads/default/original/2X/1/1459a4f5b50367dc51eb27141795692506ab89e1.gif)  
可以看到我加载驱动之后输入指定的PID，系统的NtOpenProcess函数就被hook，hook中判断如果OpenProcess打开的进程PID号等于我们输入的PID，那么返回NT\_STATUS\_ACCESS\_DENIED，导致像OD，CE类型的工具无法附加进程，也就不能对程序进行调试了。

其实我们hook的只是SSDT表：

 ![hook](https://forum.90sec.com/uploads/default/original/2X/d/db22ce7046d83fbb6e291058dea3a2ecafcbbfd4.jpeg)  
如果我们能从R3穿越到R0去执行NtOpenprocess函数，那么他的这个hook也就没有什么太大的意义了。

当然我们也可以再写一个驱动去UNHOOK，但是这对于一些不熟悉驱动的人来说，并不是一件友好的事情，如果有一个框架可以去实现这样的“越级”执行的话呢那是最好不过的了，我在网上的浏览中并没有得到类似的越级框架，那么只能麻烦一下，自己去写一个这样子的小框架。

这个框架开发之前，我以为一上午就写完了，实际上也就是一上午的代码量，但是我却写了一天半，这是为什么呢？因为还有一天的时间我面对的是电脑的蓝屏以及重启，不小心总是会带给我满屏蓝色，但是幸好最后我还是完成了（中途一直不想写了）。

框架有四个文件：

1. R0JmpDLL.dll：这个是框架的主代码
2. R0JmpDLL.lib：编写程序用的一个lib库
3. R0JmpHook.h：lib库的头文件
4. WkerR0JmpDriver.sys：核心驱动

框架的缺陷：只能对win7 32位使用，原因是我的DDK版本貌似只能编译win7，win8的，不支持64位是因为不想写了，其实编写上只是有两点不一样，之后会补上的。  
框架的原理是，我会提供一个函数去让大家去加载核心驱动，加载的方法，也就是调用我提供给大家的dll文件里面有。加载驱动之后，我们就可以通过这个驱动去越级执行了，具体操作如下：

1. 首先导入我们的头文件，这个很简单。
2. 接着导入我们lib：`#pragma comment(lib,"R0JmpDLL.lib")`

完成这两步之后的操作就是我们编程的思路了。

```
R0JMPDLL BOOL R0JmpLoadDriver(char *);

R0JMPDLL BOOL R0JmpUnLoadDriver();

R0JMPDLL HANDLE initSYS();

R0JMPDLL HANDLE R0JmpOpenProcess(HANDLE hDevice,DWORD dwDesiredAccess,DWORD PID);

R0JMPDLL BOOL R0JmpReadProcessMemory(HANDLE hDevice,HANDLE hProcess,LPCVOID lpBaseAddress,LPVOID lpBuffer,DWORD nSize,LPDWORD lpNumberOfBytesRead);

R0JMPDLL BOOL R0JmpWriteProcessMemory(HANDLE hDevice,HANDLE hProcess,LPVOID lpBaseAddress,LPVOID lpBuffer,DWORD nSize,LPDWORD lpNumberOfBytesWritten);

R0JMPDLL VOID R0JmpCloseHandle(HANDLE hDevice,HANDLE hProcess);

```

我给大家提供了这几个函数。

1. `R0JmpLoadDriver`这个函数只有一个参数，就是 **完整的** 驱动程序名称
2. `R0JmpUnLoadDriver`这个当然就是卸载驱动了
3. `initSYS`这个其实实际上就是获取到驱动对象的一个句柄的，简单来说可以这么称呼，返回的就是我们在之后操作中要一种用到的hDevice
4. 下面的函数可以看到，和我们win32下的函数用法是一样的，就多了一个HANDLE hDevice，这个就是我们在`initSYS`中返回的句柄

简单介绍完之后，我们就可以编写了。

随便准备一个测试的被保护程序，让他有个全局变量可以变动就可以了。

我这里简单准备了一个，他的变量地址是：0x4A3600，我们目标是在他被保护的状态下对其数值进行读写。

1. 加载驱动

```
//LoadDriver
char szFullPathname[MAX_PATH] = {0};
char szFullPath[MAX_PATH] = {0};
GetModuleFileName(NULL, szFullPathname, MAX_PATH);
strncpy(szFullPath, szFullPathname, strlen(szFullPathname)-strlen(argv[0]));
strcat(szFullPath,"WkerR0JmpDriver.sys");
R0JmpLoadDriver(szFullPath);

```

这个就是获取当前的路径，然后拼接我们的核心驱动，传进我们的参数驱动加载。

1. 初始化驱动

```
//初始化驱动
HANDLE hDevcie = initSYS();
if (hDevcie == INVALID_HANDLE_VALUE)
{
	printf("驱动初始化失败\n");
	return 1;
}

```

没啥好说的，就是初始化呗。

1. `OpenProcess`打开进程

```
//OpenProcess
int PID = -1; scanf("%d",&PID);
HANDLE oh = R0JmpOpenProcess(hDevcie,PROCESS_ALL_ACCESS,PID);
if (oh == INVALID_HANDLE_VALUE)
{
	printf("无效的句柄\n",oh);
}

```

让用户输入进程的ID，当然自己去找也是可以的，但这里为了简单，就输入一个吧，会返回给我们一个HANDLE句柄，这个句柄就是Read和Write的句柄了，但是需要注意的是，这个句柄你如果用用户层win32下的API是无法使用的，因为你在User mode下，而这个句柄只有在Kernel mode下才能使用，我觉的应该是这个内核句柄的对象有一个字段表示的吧，我也没用windbg详细的分析。

1. `ReadProcessMemory`读取进程内存

```
DWORD realdRead = 0;
//ReadProcessMemory
int adrnum=0;
R0JmpReadProcessMemory(hDevcie,oh,(LPCVOID)0x4A3600,&adrnum,sizeof(adrnum),&realdRead);
printf("读取到的内容:%d\n",adrnum);

```

可以看到基本就和win32下没有什么区别，无非就是多传了一个参数而已。

1. `WriteProcessMemory`写进程内存

```
//WriteProcessMemory
int Newadrnum = 100;
R0JmpWriteProcessMemory(hDevcie,oh,(LPVOID)0x4A3600,&Newadrnum,sizeof(Newadrnum),&realdRead);

```

同样也是和win32没区别，写一个整数进去。

1. `CloseHandle`关闭句柄

```
//CloseHandle
R0JmpCloseHandle(hDevcie,oh);

```

关闭我们之前通过OpenProcess打开的句柄

1. 卸载驱动

```
//UnLoadDriver
R0JmpUnLoadDriver();

```

流程大致也就分为这七步，无非就是在打开和关闭之间我们可以多做点事情，还有一点也是需要注意的，不可以去并行操作，这一点很关键，我在写的时候忘记用StartIO去串行处理了，所以尽量不要并用同时读取或者写入。

看一下程序运行的效果：

 ![|success](https://forum.90sec.com/uploads/default/original/2X/e/e1b04cd725d54f7656477653a631cb3cd856c607.gif)

可以看到Hook NtOpenprocess之后，CE无法附加，但是我们可以正常的OpenProcess进行内存的读写。

之前一直没考虑到过保护的问题，准备把之前的那个Wker\_EXEDebug全面的实现R0级的内存读写，可以实现无视SSDT的HOOK的效果。

Nt的inline hook想要实现框架的难度很高，就算能实现也不是我一个人能写完的。

[R0Jmp框架.zip](https://forum.90sec.com/uploads/short-url/cuC0NaR79J2CJVSsQGAGH3Po4cJ.zip) (7.9 KB)

刚才忘记上传了 😅
