[doc] doc 01-2
This commit is contained in:
141
01/README.md
141
01/README.md
@@ -162,7 +162,7 @@ Yama会在**security_ptrace_access_check**这个层次介入,但具体的介
|
||||
|
||||
### /proc/\<pid\>/mem
|
||||
|
||||
**/proc/\<pid\>/mem**是Linux在Vfs中创建的一个重要的控制节点,其在Linux源码的**linux/fs/proc/base.c**文件中实现。
|
||||
**/proc/\<pid\>/mem**是Linux在Vfs中创建的一个重要的控制节点,读写该文件相当于读写进程的内存,其在Linux源码的**linux/fs/proc/base.c**文件中实现。
|
||||
|
||||
```c
|
||||
static const struct file_operations proc_mem_operations = {
|
||||
@@ -268,6 +268,145 @@ static bool may_access_mm(struct mm_struct *mm, struct task_struct *task, unsign
|
||||
|
||||
### process_vm_readv/writev
|
||||
|
||||
**process_vm_readv/writev** 是两个独立的系统调用,类似于Windows中的**Read/WriteProcessMemory**。
|
||||
需要指出的是,**Behavior Nets: Context-Aware Behavior Modeling for Code Injection-Based Windows Malware** 的
|
||||
作者在2.2节中所说的 **NtReadVirtualMemory**、**NtWriteVirtualMemory**实际上是**Read/WriteProcessMemory**在更底层的链接库中的形式。
|
||||
|
||||
在**kernel32.dll**中,**Read/WriteProcessMemory** API被定义,并且在内部实现中引用了 **ntdll**,
|
||||
**NtReadVirtualMemory**、**NtWriteVirtualMemory** 在 **ntdll** 中被实现,这里也是系统调用的实际上下文交换发生的地方。
|
||||
|
||||
那再进一步呢?内核中发生了什么?这就难说了,毕竟Windows是闭源的。尽管可以通过逆向工程的方式窥见一二,但这不是现在的主题。
|
||||
|
||||
与之不同的是,我们完全可以追踪到 **process_vm_readv/writev** 在内核层面做了哪些事,一个不错的切入点是系统调用表。
|
||||
|
||||
> /arch/x86/entry/syscalls/syscall_64.tbl:
|
||||
>
|
||||
> 310 64 process_vm_readv sys_process_vm_readv
|
||||
>
|
||||
> 311 64 process_vm_writev sys_process_vm_writev
|
||||
|
||||
可以明确的看到,310和311号系统调用对应 **process_vm_readv/writev**,
|
||||
而在 **linux/include/linux/syscalls.h** 中,可以找到这两个系统调用的原型:
|
||||
|
||||
```c
|
||||
asmlinkage long sys_process_vm_readv(pid_t pid,
|
||||
const struct iovec __user *lvec,
|
||||
unsigned long liovcnt,
|
||||
const struct iovec __user *rvec,
|
||||
unsigned long riovcnt,
|
||||
unsigned long flags);
|
||||
asmlinkage long sys_process_vm_writev(pid_t pid,
|
||||
const struct iovec __user *lvec,
|
||||
unsigned long liovcnt,
|
||||
const struct iovec __user *rvec,
|
||||
unsigned long riovcnt,
|
||||
unsigned long flags);
|
||||
```
|
||||
|
||||
进一步地,这两个系统调用的实现可以在 **linux/mm/process_vm_access.c** 中被找到:
|
||||
|
||||
```c
|
||||
SYSCALL_DEFINE6(process_vm_readv, pid_t, pid, const struct iovec __user *, lvec,
|
||||
unsigned long, liovcnt, const struct iovec __user *, rvec,
|
||||
unsigned long, riovcnt, unsigned long, flags)
|
||||
{
|
||||
return process_vm_rw(pid, lvec, liovcnt, rvec, riovcnt, flags, 0);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE6(process_vm_writev, pid_t, pid,
|
||||
const struct iovec __user *, lvec,
|
||||
unsigned long, liovcnt, const struct iovec __user *, rvec,
|
||||
unsigned long, riovcnt, unsigned long, flags)
|
||||
{
|
||||
return process_vm_rw(pid, lvec, liovcnt, rvec, riovcnt, flags, 1);
|
||||
}
|
||||
```
|
||||
|
||||
可以很明确的看到,实际上 **process_vm_readv** 和 **process_vm_writev** 的实现指向了一个相同的内核函数 **process_vm_rw**,
|
||||
只不过 **process_vm_writev** 传入了一个为1的写标志。
|
||||
|
||||
**process_vm_rw** 的定义如下:
|
||||
|
||||
```c
|
||||
static ssize_t process_vm_rw(pid_t pid,
|
||||
const struct iovec __user *lvec,
|
||||
unsigned long liovcnt,
|
||||
const struct iovec __user *rvec,
|
||||
unsigned long riovcnt,
|
||||
unsigned long flags, int vm_write)
|
||||
{
|
||||
struct iovec iovstack_l[UIO_FASTIOV];
|
||||
struct iovec iovstack_r[UIO_FASTIOV];
|
||||
struct iovec *iov_l = iovstack_l;
|
||||
struct iovec *iov_r;
|
||||
struct iov_iter iter;
|
||||
ssize_t rc;
|
||||
int dir = vm_write ? ITER_SOURCE : ITER_DEST;
|
||||
|
||||
if (flags != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check iovecs */
|
||||
rc = import_iovec(dir, lvec, liovcnt, UIO_FASTIOV, &iov_l, &iter);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (!iov_iter_count(&iter))
|
||||
goto free_iov_l;
|
||||
iov_r = iovec_from_user(rvec, riovcnt, UIO_FASTIOV, iovstack_r,
|
||||
in_compat_syscall());
|
||||
if (IS_ERR(iov_r)) {
|
||||
rc = PTR_ERR(iov_r);
|
||||
goto free_iov_l;
|
||||
}
|
||||
rc = process_vm_rw_core(pid, &iter, iov_r, riovcnt, flags, vm_write); // <- 看这里看这里
|
||||
if (iov_r != iovstack_r)
|
||||
kfree(iov_r);
|
||||
free_iov_l:
|
||||
kfree(iov_l);
|
||||
return rc;
|
||||
}
|
||||
```
|
||||
|
||||
从定义中可以看出,**process_vm_rw** 将处理逻辑推后到了 **process_vm_rw_core** 函数。
|
||||
而对于这个函数,我们的分析可以掐头去尾:
|
||||
|
||||
```c
|
||||
static ssize_t process_vm_rw_core(pid_t pid, struct iov_iter *iter,
|
||||
const struct iovec *rvec,
|
||||
unsigned long riovcnt,
|
||||
unsigned long flags, int vm_write)
|
||||
{
|
||||
// ... 前省略
|
||||
|
||||
/* Get process information */
|
||||
task = find_get_task_by_vpid(pid);
|
||||
if (!task) {
|
||||
rc = -ESRCH;
|
||||
goto free_proc_pages;
|
||||
}
|
||||
|
||||
mm = mm_access(task, PTRACE_MODE_ATTACH_REALCREDS); // <- 这里是关键
|
||||
if (IS_ERR(mm)) {
|
||||
rc = PTR_ERR(mm);
|
||||
/*
|
||||
* Explicitly map EACCES to EPERM as EPERM is a more
|
||||
* appropriate error code for process_vw_readv/writev
|
||||
*/
|
||||
if (rc == -EACCES)
|
||||
rc = -EPERM;
|
||||
goto put_task_struct;
|
||||
}
|
||||
|
||||
// ... 后省略
|
||||
}
|
||||
```
|
||||
|
||||
源代码中最关键的一点是,**mm_access**最终被用来做鉴权,而我们已经证明过,
|
||||
**mm_access** 最终所依赖的是 **ptrace_may_access**
|
||||
调用链为 **mm_access -> may_access_mm -> ptrace_may_access**。
|
||||
|
||||
故而,我们已经可以得出 **process_vm_readv/writev** 同样受Yama的影响的结论了。
|
||||
|
||||
### /dev/mem
|
||||
|
||||
### 例外的情况
|
||||
|
||||
Reference in New Issue
Block a user