WriteProcessMemory
打开IDA,ALT + T搜索函数WriteProcessMemory(x,x,x,x,x)
,进入函数体
可以很清晰地看到有三个函数
NtProtectVirtualMemory(x,x,x,x,x)
NtWriteVirtualMemory(x,x,x,x,x)
NtFlushInstructionCache(x,x,x)
进入导入表查看第二个函数(其实三个函数都会最终到相似的结果,但是第三个我只能搜到Zw开头的,没有Nt开头的)
在ntdll.dll
里
NtWriteVirtualMemory
搜索查看NtWriteVirtualMemory
函数,很简单,只有四行
; NtProtectVirtualMemory
mov eax, 115h ;服务号
mov edx, 7FFE0300h ;指向 函数指针
call dword ptr [edx]
retn 14h
这里有两个陌生的东西,一个是 服务号 ,另一个是那个 0x7FFE0300
服务号:一个编号,表示调用哪一个服务(函数),后面再详细了解
0x7FFE0300
- 随便进一个进程空间
- 查找刚刚看到的地址,获取这个地址里面的值
u
指令查询函数
主要看下面这两个函数
ntdll!KiFastSystemCall:
7c92e4f0 8bd4 mov edx,esp //保存esp
7c92e4f2 0f34 sysenter //快速调用
ntdll!KiIntSystemCall:
7c92e500 8d542408 lea edx,[esp+8] //保存esp
7c92e504 cd2e int 2Eh //中断门
7c92e506 c3 ret
那么怎么区分系统有没有快速调用呢?使用cpuid指令(eax = 1)
进行查询,eax中存放的是参数,结果信息会被保存到ecx和edx寄存器中,其中edx包含SEP位(第11位),指明当前CPU是否支持快速调用
dl寄存器 = 0xFF = 0B1111 1111,所以是支持快速调用的
好吧,结果真是大失所望,根本看不到这个函数是怎么执行的,现在的深度还是不够啊
但是这点知识足够我们做一些小东西了
手动调用系统函数
#include <windows.h>
__declspec(naked) //自己掌握整个函数,别人写一半自己写一半不好把握
void MyWriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesWritten
){
_asm{
push ebp
mov ebp, esp
sub esp, 0x10 //稍微提升一下堆栈,不提升也行
push [lpNumberOfBytesWritten]
push [nSize]
push [lpBuffer]
push [lpBaseAddress]
push [hProcess] //压入5个参数
push 0 //push eip 本来是个call的,这里对齐一下就行
//系统调用,照抄就行
mov eax, 0x115
mov edx, 0x7FFE0300
call dword ptr[edx]
// add esp, 0x04 //pop eip
// add esp, 0x10 //不用做堆栈检查,省略这一部直接退出就行
mov esp, ebp
pop ebp //记得平衡回来
retn 0x14 //外部压入了5个参数
}
}
int main(){
DWORD val = 0x12345678;
DWORD write_val = 0x11112222;
//获取自己的进程PID
DWORD curPID = GetCurrentProcessId();
//下面的函数要用到这个
HANDLE dst_hdl = OpenProcess(PROCESS_ALL_ACCESS, FALSE, curPID);
//自己实现的WriteProcessMemory
//WriteProcessMemory(dst_hdl, &val, &write_val, sizeof(val), NULL);//先试试好不好使
MyWriteProcessMemory(dst_hdl, &val, &write_val, sizeof(val), NULL);
//两个函数的效果是一样的
printf("%X", val);
getchar();
return 0;
}
//打印结果:11112222
实验成功
防:不让别人hook自己的函数或者断点找到突破口
攻:绕过别人的hook检查