CE的DBK驱动提供了一些很直接的IOCTL接口,包括在分配内核中的非分页内存、执行内核代码、读写任意内存地址、建立mdl映射等,下面展示了DBK驱动通过IOCTL接口提供功能的部分源码;
这里提供了分配/释放非分页内存的功能:
这里提供了给定内核地址就能直接执行内核代码的功能:
之所以DBK驱动提供如此直白的接口但微软还能给签名,是因为DBK驱动做了一点基本的防护,在进程打开DBK驱动创建的设备对象时,它会通过进程文件对应的sig文件来校验进程文件的数字签名,如果校验失败,会打开设备对象失败,从而阻止其他进程使用DBK驱动提供的功能,这也就是为什么CE的安装目录下有sig文件;
我发现CE由于需要加载lua脚本,所以导入表里有lua53-64.dll,可以通过dll劫持让CE在启动之前就加载自己写的dll,dll里加载DBK驱动,并打开其创建的设备对象,之后内存加载自己写的未签名驱动并运行DriverEntry,虽然DriverEntry不是在System进程下运行的但好歹是跑了R0的代码。
1、加载DBK驱动
CreateService创建服务,但在OpenService打开服务之前需要写4个注册表项,不然DBK驱动会加载失败。
2、打开设备对象
打开设备对象后,需要将之前创建的4个注册表项删除掉。
3、利用DBK驱动提供的功能
下面是分配/释放内核中的非分页内存的代码:
下面是读写任意进程的内存地址的代码(包括R0地址):
下面是执行内核地址的代码:
4、将未签名驱动映射到内核内存中并修复其RVA以及导入表,之后运行其DriverEntry
(具体代码太多了,就不展示了)
5、创建驱动项目
要注意的是,由于这个驱动运行的方式不正常,所以要将入口点改为DriverEntry,还要禁用GS防护,这样才能避免SecurityCookie引发的crash问题。
我在里面简单打印了点日志用于验证:
部署目录如下:
管理员权限启动richstuff-x86_64.exe,它会在运行前加载lua53-x64.dll,之后dll会加载richstuffk64.sys驱动并打开其创建的设备对象,再通过IO控制其映射MyDriver.sys到内存中并调用其DriverEntry。
代码以及部署见:https://github.com/TechForBad/CECheater
case IOCTL_CE_ALLOCATEMEM_NONPAGED:
{
struct
input
{
ULONG Size;
}
*
inp;
PVOID address;
int
size;
inp
=
Irp
-
>AssociatedIrp.SystemBuffer;
size
=
inp
-
>Size;
address
=
ExAllocatePool(NonPagedPool,size);
*
(PUINT64)Irp
-
>AssociatedIrp.SystemBuffer
=
0
;
*
(PUINT_PTR)Irp
-
>AssociatedIrp.SystemBuffer
=
(UINT_PTR)address;
if
(address
=
=
0
)
ntStatus
=
STATUS_UNSUCCESSFUL;
else
{
DbgPrint(
"Alloc success. Cleaning memory... (size=%d)\n"
,size);
DbgPrint(
"address=%p\n"
, address);
RtlZeroMemory(address, size);
ntStatus
=
STATUS_SUCCESS;
}
break
;
}
case IOCTL_CE_FREE_NONPAGED:
{
struct
input
{
UINT64 Address;
}
*
inp;
inp
=
Irp
-
>AssociatedIrp.SystemBuffer;
ExFreePool((PVOID)(UINT_PTR)inp
-
>Address);
ntStatus
=
STATUS_SUCCESS;
break
;
}
case IOCTL_CE_ALLOCATEMEM_NONPAGED:
{
struct
input
{
ULONG Size;
}
*
inp;
PVOID address;
int
size;
inp
=
Irp
-
>AssociatedIrp.SystemBuffer;
size
=
inp
-
>Size;
address
=
ExAllocatePool(NonPagedPool,size);
*
(PUINT64)Irp
-
>AssociatedIrp.SystemBuffer
=
0
;
*
(PUINT_PTR)Irp
-
>AssociatedIrp.SystemBuffer
=
(UINT_PTR)address;
if
(address
=
=
0
)
ntStatus
=
STATUS_UNSUCCESSFUL;
else
{
DbgPrint(
"Alloc success. Cleaning memory... (size=%d)\n"
,size);
DbgPrint(
"address=%p\n"
, address);
RtlZeroMemory(address, size);
ntStatus
=
STATUS_SUCCESS;
}
break
;
}
case IOCTL_CE_FREE_NONPAGED:
{
struct
input
{
UINT64 Address;
}
*
inp;
inp
=
Irp
-
>AssociatedIrp.SystemBuffer;
ExFreePool((PVOID)(UINT_PTR)inp
-
>Address);
ntStatus
=
STATUS_SUCCESS;
break
;
}
case IOCTL_CE_EXECUTE_CODE:
{
typedef NTSTATUS (
*
PARAMETERLESSFUNCTION)(UINT64 parameters);
PARAMETERLESSFUNCTION functiontocall;
struct
input
{
UINT64 functionaddress;
/
/
function address to call
UINT64 parameters;
}
*
inp
=
Irp
-
>AssociatedIrp.SystemBuffer;
DbgPrint(
"IOCTL_CE_EXECUTE_CODE\n"
);
functiontocall
=
(PARAMETERLESSFUNCTION)(UINT_PTR)(inp
-
>functionaddress);
__try
{
ntStatus
=
functiontocall(inp
-
>parameters);
DbgPrint(
"Still alive\n"
);
ntStatus
=
STATUS_SUCCESS;
}
__except(
1
)
{
DbgPrint(
"Exception occured\n"
);
ntStatus
=
STATUS_UNSUCCESSFUL;
}
break
;
}
case IOCTL_CE_EXECUTE_CODE:
{
typedef NTSTATUS (
*
PARAMETERLESSFUNCTION)(UINT64 parameters);
PARAMETERLESSFUNCTION functiontocall;
struct
input
{
UINT64 functionaddress;
/
/
function address to call
UINT64 parameters;
}
*
inp
=
Irp
-
>AssociatedIrp.SystemBuffer;
DbgPrint(
"IOCTL_CE_EXECUTE_CODE\n"
);
functiontocall
=
(PARAMETERLESSFUNCTION)(UINT_PTR)(inp
-
>functionaddress);
__try
{
ntStatus
=
functiontocall(inp
-
>parameters);
DbgPrint(
"Still alive\n"
);
ntStatus
=
STATUS_SUCCESS;
}
__except(
1
)
{
DbgPrint(
"Exception occured\n"
);
ntStatus
=
STATUS_UNSUCCESSFUL;
}
break
;
}
/
/
写相关注册表
HKEY hKey;
std::wstring subKey
=
Format
(L
"SYSTEM\\CurrentControlSet\\Services\\%ws"
, DBK_SERVICE_NAME);
LSTATUS status
=
RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey.c_str(),
0
, KEY_WRITE, &hKey);
if
(ERROR_SUCCESS !
=
status)
{
LOG(
"RegOpenKeyEx failed"
);
CloseServiceHandle(hService);
CloseServiceHandle(hMgr);
return
false;
}
std::wstring AValue
=
Format
(L
"\\Device\\%ws"
, DBK_SERVICE_NAME);
RegSetValueEx(hKey, L
"A"
,
0
, REG_SZ, reinterpret_cast<const BYTE
*
>(AValue.data()), AValue.size()
*
sizeof(wchar_t));
std::wstring BValue
=
Format
(L
"\\DosDevices\\%ws"
, DBK_SERVICE_NAME);
RegSetValueEx(hKey, L
"B"
,
0
, REG_SZ, reinterpret_cast<const BYTE
*
>(BValue.data()), BValue.size()
*
sizeof(wchar_t));
std::wstring CValue
=
Format
(L
"\\BaseNamedObjects\\%ws"
, DBK_PROCESS_EVENT_NAME);
RegSetValueEx(hKey, L
"C"
,
0
, REG_SZ, reinterpret_cast<const BYTE
*
>(CValue.data()), CValue.size()
*
sizeof(wchar_t));
std::wstring DValue
=
Format
(L
"\\BaseNamedObjects\\%ws"
, DBK_THREAD_EVENT_NAME);
RegSetValueEx(hKey, L
"D"
,
0
, REG_SZ, reinterpret_cast<const BYTE
*
>(DValue.data()), DValue.size()
*
sizeof(wchar_t));
/
/
写相关注册表
HKEY hKey;
std::wstring subKey
=
Format
(L
"SYSTEM\\CurrentControlSet\\Services\\%ws"
, DBK_SERVICE_NAME);
LSTATUS status
=
RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey.c_str(),
0
, KEY_WRITE, &hKey);
if
(ERROR_SUCCESS !
=
status)
{
LOG(
"RegOpenKeyEx failed"
);
CloseServiceHandle(hService);
CloseServiceHandle(hMgr);
return
false;
}
std::wstring AValue
=
Format
(L
"\\Device\\%ws"
, DBK_SERVICE_NAME);
RegSetValueEx(hKey, L
"A"
,
0
, REG_SZ, reinterpret_cast<const BYTE
*
>(AValue.data()), AValue.size()
*
sizeof(wchar_t));
std::wstring BValue
=
Format
(L
"\\DosDevices\\%ws"
, DBK_SERVICE_NAME);
RegSetValueEx(hKey, L
"B"
,
0
, REG_SZ, reinterpret_cast<const BYTE
*
>(BValue.data()), BValue.size()
*
sizeof(wchar_t));
std::wstring CValue
=
Format
(L
"\\BaseNamedObjects\\%ws"
, DBK_PROCESS_EVENT_NAME);
RegSetValueEx(hKey, L
"C"
,
0
, REG_SZ, reinterpret_cast<const BYTE
*
>(CValue.data()), CValue.size()
*
sizeof(wchar_t));
std::wstring DValue
=
Format
(L
"\\BaseNamedObjects\\%ws"
, DBK_THREAD_EVENT_NAME);
RegSetValueEx(hKey, L
"D"
,
0
, REG_SZ, reinterpret_cast<const BYTE
*
>(DValue.data()), DValue.size()
*
sizeof(wchar_t));
UINT64 DBK_AllocNonPagedMem(ULONG size)
{
struct InputBuffer
{
ULONG size;
};
InputBuffer inputBuffer;
inputBuffer.size
=
size;
UINT64 allocAddress
=
0LL
;
DWORD retSize;
if
(!DeviceIoControl(g_DBKDevice, IOCTL_CE_ALLOCATEMEM_NONPAGED, (LPVOID)&inputBuffer, sizeof(inputBuffer), &allocAddress, sizeof(allocAddress), &retSize, NULL))
{
LOG(
"DeviceIoControl IOCTL_CE_ALLOCATEMEM_NONPAGED failed"
);
return
0
;
}
return
allocAddress;
}
bool
DBK_FreeNonPagedMem(UINT64 allocAddress)
{
struct InputBuffer
{
UINT64 address;
};
InputBuffer inputBuffer;
inputBuffer.address
=
allocAddress;
DWORD retSize;
if
(!DeviceIoControl(g_DBKDevice, IOCTL_CE_FREE_NONPAGED, (LPVOID)&inputBuffer, sizeof(inputBuffer), NULL,
0
, &retSize, NULL))
{
LOG(
"DeviceIoControl IOCTL_CE_FREE_NONPAGED failed"
);
return
false;
}
return
true;
}
UINT64 DBK_AllocNonPagedMem(ULONG size)
{
struct InputBuffer
{
ULONG size;
};
InputBuffer inputBuffer;
inputBuffer.size
=
size;
UINT64 allocAddress
=
0LL
;
DWORD retSize;
if
(!DeviceIoControl(g_DBKDevice, IOCTL_CE_ALLOCATEMEM_NONPAGED, (LPVOID)&inputBuffer, sizeof(inputBuffer), &allocAddress, sizeof(allocAddress), &retSize, NULL))
{
LOG(
"DeviceIoControl IOCTL_CE_ALLOCATEMEM_NONPAGED failed"
);
return
0
;
}
return
allocAddress;
}
bool
DBK_FreeNonPagedMem(UINT64 allocAddress)
{
struct InputBuffer
{
UINT64 address;
};
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2023-8-5 17:10
被TechForBad编辑
,原因: 不再直接提供源码,而是转到github里提供