Windows系统调用学习笔记(一)—— API函数调用过程
Windows系统调用学习笔记(一)—— API函数的调用过程前言Windows API实验:分析ReadProcessMemory第一步:定位函数第二步:开始分析前言一、学习自滴水编程达人中级班课程,官网:https://bcdaren.com二、海东老师牛逼!Windows API描述:Application Programming Interface,简称 API 函数。...
Windows系统调用学习笔记(一)—— API函数调用过程
Windows API
描述:
-
Application Programming Interface,简称 API 函数。
-
Windows有多少个API?
主要是存放在 C:\WINDOWS\system32 下面所有的dll -
几个重要的DLL
Kernel32.dll
:最核心的功能模块,比如管理内存、进程和线程相关的函数等
User32.dll
:是Windows用户界面相关应用程序接口,如创建窗口和发送消息等
GDI32.dll
:全称是Graphical Device Interface(图形设备接口),包含用于画图和显示文本的函数。比如要显示一个程序窗口,就调用了其中的函数来画这个窗口
Ntdll.dll
:大多数API都会通过这个DLL进入内核(0环)
实验1:分析ReadProcessMemory
第一步:定位函数
- 使用IDA打开kernel32.dll(ReadProcessMemory函数在这个dll里)
- 在左侧函数窗口中使用快捷键ctrl+F搜索ReadProcessMemory
- 双击查看反汇编
第二步:开始分析
-
首先将我们传入的参数进行压栈
-
调用了其它模块的函数:NtReadVirtualMemory(函数名红色说明不在当前模块)
-
若返回结果小于0,跳转至loc_7C802204
loc_7C802204调用了sub_7C809419 -
sub_7C809419内部调用了另一个函数RtlNtStatusToDosError
这个函数的作用是设置错误号 -
从sub_7C809419出来后,将eax清零,跳转到loc_7C802F9
loc_7C802F9只做了一件事情,那就是返回
至此,我们可以知道,当调用ReadProcessMemory失败时,返回结果为0 -
若NtReadVirtualMemory返回结果大于等于0,返回结果为1
总结
- ReadProcessMemory函数在kernel32.dll中实际上并没有做什么事情。它只是调用了另一个函数,然后设置了一些返回值
- 如果想知道ReadProcessMemory这个函数到底是怎么执行的,就得找到NtReadVirtualMemory
实验2:分析NtReadVirtualMemory
第一步:定位函数
- 查看kernel32的导入表(Imports),找到NtReadVirtualMemory
- 观察Library字段,发现NtReadVirtualMemory函数来自ntdll
- 使用IDA打开ntdll.dll
- 将光标放置在反汇编界面最上方,使用alt+T搜索NtReadVirtualMemory
这4行代码就是NtReadVirtualMemory的所有代码
第二步:开始分析
-
mov eax, 0BAh
0BAh
:编号,对应操作系统内核中某个函数的编号
我们所用到的API函数,只要进0环,都要提供一个编号 -
mov edx, 7FFE0300h
7FFE0300h
:函数地址,该函数决定了我们用什么方式进0环 -
该函数的功能:提供一个编号和一个函数,通过这个函数进入0环
总结
真正读取进程内存的函数在0环实现,我们所用的函数只是系统提供给我们的函数接口
实验3:编写一个ReadProcessMemory函数
要求:不使用任何DLL,直接调用0环函数
意义:自己实现的API,可以避免3环程序被恶意挂钩
第一步:获得变量地址
运行以下代码(进程1):
#include <stdio.h>
int main()
{
int num = 0x12345678;
printf("&num = %x \n", &num);
getchar();
return 0;
}
运行结果:
注意:不要让进程结束
第二步:构造自定义ReadProcessMemory
代码如下
void MyReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead)
{
__asm
{
lea eax, [ebp+0x14]
push eax ; ReturnLength
push [ebp+0x14] ; BufferLength
push [ebp+0x10] ; Buffer
push [ebp+0x0C] ; BaseAddress
push [ebp+0x08] ; ProcessHandle
mov eax, 0BAh
mov edx, esp
int 02eh ; Windows操作系统提供IDT[0x2e]给用户进行内核调用
add esp, 20
}
}
第三步:调用自定义ReadProcessMemory
完整代码(进程2):
void MyReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead)
{
__asm
{
lea eax, [ebp+0x14]
push eax ; ReturnLength
push [ebp+0x14] ; BufferLength
push [ebp+0x10] ; Buffer
push [ebp+0x0C] ; BaseAddress
push [ebp+0x08] ; ProcessHandle
mov eax, 0BAh
mov edx, esp
int 02eh ; Windows操作系统提供IDT[0x2e]给用户进行内核调用
add esp, 20
}
}
int main()
{
DWORD pBuffer; // 接收数据的缓冲区
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, PID); // 获得进程句柄,PID修改为进程1的PID
MyReadProcessMemory(hProcess, (PVOID)0x12ff7c, &pBuffer, 4, 0); // 调用自定义的ReadProcessMemory
printf("pBuffer = %x \n", pBuffer); // 打印从其他进程中读取的数据
getchar();
return 0;
}
运行结果:
成功从进程1中读取了变量num的值!
更多推荐
所有评论(0)