当前位置:网站首页>(15)[驱动开发]过写拷贝
(15)[驱动开发]过写拷贝
2022-07-25 05:43:00 【一口一个橘子】
写拷贝是什么
写拷贝是一种内存权限,是3环用的
使用 !vad EPROCESS.VadRoot 查看内存权限,主要关注的地址是0x77d507ea(MessageBoxA)
下方蓝条范围内,是执行_写拷贝权限
什么是写拷贝,就是你要往里面修改数据的时候,就会拷贝一份内容放入另一个内存且自己独占,并修改独占的内存,不会直接修改原来的内存
大概就是我不喜欢流水线大批量生产的东西,于是我自己定制了一份只有自己能用的
别人用 自己用
内存123 内存567
+-------+ +--------------+
|内容1 | ---> |修改后的内容1 |
|内容2 | |内容2 |
|内容3 | |内容3 |
思路
当异常产生时,就会转入异常处理函数,判断当前操作是否合法。我们之前学到了缺页的时候会触发异常,读写权限不对的时候也会出现异常,但是只要不触发异常,他就不会检测这个操作是否合法。
而罪魁祸首居然是PTE没有 r/w权限(0B 0000 0010)的位置
相信大家已经轻车熟路了。刚才没注意,代码里没写MessageBoxA,所以用不到这个函数,所以PTE表里不需要加载这块物理内存,所以没有执行MessageBoxA之前,PTE是空的
3环代码
我们知道call后会push EIP
所以call MessageBoxA后的地址应该是
MessageBoxA(参数1, 参数2, 参数3, 参数4)
EIP <-- ESP
参数1 +0x4
参数2 +0x8
参数3 +0xC
参数4 +0x10
我的shell code 非常简单,只是把第4个参数改了
shell code 到OD里写出人能看懂的语句,然后照抄机器码就行
#include<windows.h>
#include<winioctl.h>
#include<stdio.h>
#define IN_BUFFER_MAXLENGTH 0x10
#define OUT_BUFFER_MAXLENGTH 0x10
//宏定义之获取一个32位的宏控制码 参数:设备类型(鼠标,键盘...Unkonwn);0x000-0x7FF保留,0x800-0xfff随便填一个;数据交互类型(缓冲区,IO,其他);对这个设备的权限
#define OPER1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define OPER2 CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define SYMBOLICLINK_NAME "\\\\.\\MyTestDriver"
HANDLE g_hDevice; //全局驱动句柄
//打开驱动服务句柄
//3环链接名:\\\\.\\AABB
BOOL Open(PCHAR pLinkName)
{
//在3环获取设备句柄
TCHAR szBuffer[10] = {
0 };
//CreateFile 打开的是内核的设备对象
g_hDevice = CreateFile(pLinkName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (g_hDevice != INVALID_HANDLE_VALUE)
return TRUE;
else
return FALSE;
}
//dword写到byte[]里,由于要用两次,所以写了函数
//主要用来填充跳转地址的
void writeDword2Byte(BYTE* dst, DWORD src){
dst[0] = src >> 0;
dst[1] = src >> 8;
dst[2] = src >> 16;
dst[3] = src >> 24;
}
int main(int argc, char* argv[])
{
//通过这个jmp,跳转到要执行的shell code
BYTE shellCode[] = {
0xe9, 00, 00, 00, 00}; //jump xxxx to shell code (hook)
//玩完记得弄回去
BYTE shellCode_writeback[5]; // restore
//真正的shell code
BYTE shellCode_run[] = {
0xC7, 0x44, 0x24, 0x10, 0x01, 0x00, 0x00, 0x00, //mov dword ptr[esp + 0x10], 1
0x55, //push ebp
0x8B, 0xEC, //mov ebp, esp 由于hook覆盖了两条语句,所以要写回来
0xe9, 00, 00, 00, 00}; //jmp back
DWORD dwInBuffer[2], szOutBuffer[4];
DWORD Sz0utBuffer = 0;
DWORD ByteReturned = 0;
DWORD my_PID;
DWORD msg_addr;
BYTE* run_addr;
DWORD offset_jmp;
DWORD offset_jmpback;
BYTE *pMsg_addr;
int i; //C代码声明要放最前面,不然报错
//MessageBoxA( NULL, "before run", NULL, 0); //观察PTE用
msg_addr = (DWORD) MessageBoxA; //这个拿去做加减运算
pMsg_addr = (BYTE*)MessageBoxA; //这个拿去写入
//给ring 0传递的PID
my_PID = (DWORD)GetCurrentProcessId();
printf("addr: %X\tPID: %X", msg_addr, my_PID);
//保存写回去的地址
memcpy(shellCode_writeback, pMsg_addr, sizeof(shellCode));
//申请可执行内存,这里申请的内存不能被其他进程访问,所以要想完美完成实验,
//需要在高2G申请内存,并使其能被用户进程访问
run_addr = (BYTE*)VirtualAlloc(NULL, sizeof(shellCode_run), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// offset = dst - (src + codeLength)
offset_jmp = (DWORD)run_addr - (msg_addr + 5);
writeDword2Byte(shellCode + 1, offset_jmp);
//这里加上sizeof(shellCode)是为了防止跳到自己写的jmp上死循环
offset_jmpback = msg_addr - ((DWORD)run_addr + sizeof(shellCode_run)) + sizeof(shellCode);
writeDword2Byte(shellCode_run + sizeof(shellCode_run)-4, offset_jmpback);
//将要执行的 shell code 写入可执行内存
memcpy(run_addr, shellCode_run, sizeof(shellCode_run));
//准备冻手,准备冻手
//1.通过符号链接,打开设备
if (!Open(SYMBOLICLINK_NAME))
{
printf("设备对象打开失败!!!");
getchar();
return 0;
}
//传入这俩
dwInBuffer[0] = msg_addr;
dwInBuffer[1] = my_PID;
//2.测试通信
DeviceIoControl(g_hDevice, OPER2, dwInBuffer, IN_BUFFER_MAXLENGTH, szOutBuffer, OUT_BUFFER_MAXLENGTH, &ByteReturned, NULL);
//写入jmp xxxx
memcpy(pMsg_addr, shellCode, sizeof(shellCode));
//3.关闭设备
getchar();
CloseHandle(g_hDevice);
//玩完了写回去
memcpy(pMsg_addr, shellCode_writeback, sizeof(shellCode));
return 0;
}
0环代码
由于0环代码通信部分又臭又长,是我从网上扒下来的。感谢这些热心网友提供的代码(不记得从哪里扒的了,只是一直扒扒到能用)
这里指提供关键代码
//ProcessID:3环进程的PID
//addr: 要修改权限的地址
NTSTATUS changePageAttribute(HANDLE ProcessId, DWORD32 addr)
{
NTSTATUS Status = STATUS_SUCCESS;
PEPROCESS pEProcess = NULL;
//恢复状态用的,我也不太清楚这个
KAPC_STATE ApcState = {
0 };
DWORD32* pte;
_asm int 3;
//根据之前所学的公式直接套用即可
pte = (DWORD32*)(((addr >> 9) & 0x7FFFF8) + 0xC0000000);//x的pte
//获取EPROCESS结构体,很重要
Status = PsLookupProcessByProcessId((HANDLE)ProcessId, &pEProcess);
if (!NT_SUCCESS(Status) && !MmIsAddressValid(pEProcess)) {
return STATUS_UNSUCCESSFUL; }
//加上这个try不会蓝屏,但是有些问题依然要重启的
__try {
//进程挂靠,就是把CR3改得和3环应用一样的,这样就能直接读3环应用的地址了
//有上层的API干嘛要自己搞
KeStackAttachProcess(pEProcess, &ApcState);
//好吧,其实0环做的所有事情就只有这一件,加上读写属性
*pte |= 0x2; //0000 0010
//结束挂靠
KeUnstackDetachProcess(&ApcState);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
KeUnstackDetachProcess(&ApcState);
Status = STATUS_UNSUCCESSFUL;
}
ObDereferenceObject(pEProcess);
return Status;
}
如果觉得麻烦,完全可以在0环修改GDT表或者IDT表,弄个门,然后让3环调用,然后复用以前的代码
若果你对上面的函数不满意,还有两个更底层的进程挂靠函数可以用
甚至你可以在EPROCESS[0]的地方找到KPROCESSKPROCESS[0x18]的地方找到DirectoryTableBase,这就是CR3
然后就用汇编代码mov CR3, mycr3,改完PTE在弄回去
先执行驱动,在执行程序
好吧,其他进程调用MessageBoxA会直接崩溃,这说明我成功了,但也失败了
写保护过了,但是 shell code 没有成功,总归是有些失落的
原因是virtualAlloc申请的内存并不能被其他进程拥有。所以接下来要别人执行自己的shellcode,就需要申请高2G的内存,这样线性地址就是固定的了。
然后再把高2G的PTE改写一下,使他可以被用户程序访问
懒得搞了,有机会的话,会来补更的。
失败了但也有收获不是吗
边栏推荐
- Dynamic planning learning notes
- Microservices and related component concepts
- Leetcode 0121. the best time to buy and sell stocks - simulation from back to front
- Terminate 5g chip cooperation! The official response of Intel and zhanrui came
- systemVerilog中automatic用法
- 2021 ICPC Shaanxi warm up match b.code (bit operation)
- 50: Chapter 5: develop admin management service: 3: develop [query whether the admin user name already exists, interface]; (this interface can only be called when logging in; so we have written an int
- Idea commonly used 10 shortcut keys
- R language data The table package performs aggregation transforms of data packets and calculates the grouping interquartile range (IQR) of dataframe data
- An SQL execution process
猜你喜欢

微服务 - 配置中心 - Nacos

Programming hodgepodge (II)

Leetcode 204. 计数质数(太妙了)

Linear algebra (3)

Odoo14 | about the abnormal display of statusbar keyword after use and Its Solutions

Easyrecovery free data recovery tool is easy to operate and restore data with one click

Leetcode 237. delete nodes in the linked list

50:第五章:开发admin管理服务:3:开发【查询admin用户名是否已存在,接口】;(这个接口需要登录时才能调用;所以我们编写了拦截器,让其拦截请求,判断用户是否是登录状态;)

Single sign on (one sign on, available everywhere)

同条网线电脑正常上网,手机连接wifi成功,但是无法访问互联网
随机推荐
School day (summer vacation daily question 2)
Detailed explanation of stepn chain game system development mode (Sports money making mode)
2021年ICPC陕西省赛热身赛 B.CODE(位运算)
Idea commonly used 10 shortcut keys
Sword finger offer special shock edition day 9
Softing pnGate系列网关:将PROFIBUS总线集成到PROFINET网络
Dynamic planning learning notes
ERA5数据集说明
Openfegin remote call lost request header problem
出于数据安全考虑,荷兰教育部要求学校暂停使用 Chrome 浏览器
[typescript manual]
Three billion dollars! Horizon becomes the world's highest valued AI chip Unicorn
Programming hodgepodge (II)
SystemVerilog中$write与$display区别
Get URL of [url reference]? For the following parameters, there are two ways to get the value of the corresponding parameter name and convert the full quantity to the object structure
弹性布局总结
background
Unity中使用UniRx入门总结
CCID released the "Lake warehouse integrated technology research report", and Jushan database was selected as a typical representative of domestic enterprises
HTB-Beep