Loading... > 本篇是《Hacker Disassembling Uncovered》的笔记。由于这本书出的时间比较长了,我看的版本还是08年出的,里面有很多东西没什么学习的必要,不过多读点书还是好的吧。在这里记个备忘。 ## 第6章 本来想用`kerberos`的,结果下的都用不了,可能是版本太低了。然后学习了`spy monitor`,可以用来API窥测,同时进行实时调试,对MFC和win32的支持很好,对指定的API跟踪后,可以推出虚拟地址,进而结合IDA进行分析,节省时间。快速定位API的好方法。 实例的话,分析了winrar\_3.42.exe版本,进行注册码的绕过,说实话感觉这部分是比较烦的,对没有win开发经验的我来说有点困难,不过还是简单的绕过了`40天试用期`的限制。后续的注册还是不成功,关于关键的几个变量分析的还是不够。一个负责模拟对话框的显示,这个不难;一个是信号的处理,这部分就有点吃力了,交叉引用比较多,不是很懂;还有一共负责注册的变量,没分析出来。 后续的话,实现了一个字符串的替换。`registered by zjgcjy`哈哈 -\_- ```c call Register ->mov eax,[字符串地址指针] push eax push 66h push *** call SetDigItemTextA ``` 这里还遇到一点问题,是FOA和RVA的转换问题。开始有点傻了,地址一直算错,(+﹏+)~晕。转换如下:**RVA - RVA(段) == FOA - FOA(段)** 因为PE载入后,各节内部相对偏移量不变。 [PE结构相关](http://cjynet.top/2017/09/28/windows/Win32ASM-2/) ## 第8章 ### ptrace ptrace提供了一种使父进程得以监视和控制其它进程的方式,它还能够改变子进程中的寄存器和内核映像,因而可以实现断点调试和系统调用的跟踪。使用ptrace,可以在用户层拦截和修改系统调用(sys call)。一个被跟踪的进程运行中,直到发生信号。则进程被中止,并且通知其父进程。在进程中止的状态下,进程的内存空间可以被读写。父进程还可以使子进程继续执行,并选择是否是否忽略引起中止的信号。 #### 追踪子进程 实例如下。 ```c //int ptrace(int request, int pid, int addr, int data); #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <errno.h> #include <sys/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/reg.h> /* /usr/include/sys/reg.h*/ int main() { pid_t child; long orig_rax; child = fork(); if(child == 0) { //设置子进程被跟踪 ptrace(PTRACE_TRACEME, 0, NULL, NULL); execl("/bin/ls", "ls", NULL); } else { wait(NULL); //从子进程的USER域中读取寄存器的值 orig_rax = ptrace(PTRACE_PEEKUSER, child, 8 * ORIG_RAX, NULL); printf("The child made a system call %ld\n", orig_rax); //子进程继续执行 ptrace(PTRACE_CONT, child, NULL, NULL); } return 0; } ``` 解释:父进程fork了一个子进程,并且在子进程中进行系统调用。在执行前,子进程运行了ptrace,设置第一个参数是 PTRACE_TRACEME,这告诉内核当前进程正在被追踪。当子进程运行execve()之后,会把控制权转回父进程。父进程通过wait()来等待内核通知,现在它得到了通知。然后就可以查看子进程都做了什么,比如查看寄存器的值之类。当系统调用出现的时候,内核会保存原始的rax寄存器值(系统调用号),我们可以从子进程的USER段读取这个值,这里是使用ptrace并且设置第一个参数为PTRACE_PEEKUSER。当我们检查完了系统调用之后, 可以调用ptrace并设置参数PTRACE_CONT让子进程继续运行。 输出结果。可以看到execve的系统调用号是59。 ```sh ➜ Desktop./ptrace64 The child made a system call 59 desktop.ini ptrace64 wrar56b1 Hacker Disassembling Uncovered ptrace.c Xman IDA&Z3&angr Shellcoder's Handbook ``` ptrace函数原型如下。 `long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);` 关于ptrace的第一部分的参数表如下。 |Request参数|说明| |:-------|:-------:| |PTRACE_TRACEME|本进程被其父进程所跟踪 |PTRACE_PEEKTEXT,PTRACE_PEEKDATA|从内存地址中读取一个字节,内存地址由addr给出 |PTRACE_PEEKUSR|从USER区域中读取一个字节,偏移量为addr |PTRACE_POKETEXT,PTRACE_POKEDATA|往内存地址中写入一个字节。内存地址由addr给出 |PTRACE_POKEUSR|往USER区域中写入一个字节。偏移量为addr |PTRACE_SYSCALL,PTRACE_CONT|重新运行 |PTRACE_KILL|杀掉子进程,使它退出 |PTRACE_SINGLESTEP|设置单步执行标志 |PTRACE_ATTACH|跟踪指定pid 进程 |PTRACE_DETACH|结束跟踪 ![系统调用追踪流程](./assets/Snipaste_2018-03-20_15-06-05.png) #### 读取子进程系统调用参数 系统调用的参数按顺序存放在rbx,rcx...之中,因此以write系统调用为例看如何读取寄存器的值: ```c #include <sys/ptrace.h> #include <sys/wait.h> #include <sys/user.h> #include <sys/syscall.h> /* SYS_write */ #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/reg.h> /* /usr/include/sys/reg.h*/ int main() { pid_t child; long orig_rax; int status; int iscalling = 0; struct user_regs_struct regs; child = fork(); if(child == 0) { //设置子进程被跟踪 ptrace(PTRACE_TRACEME, 0, NULL, NULL); execl("/bin/ls", "ls", "-l", "-h", NULL); } else { while(1) { wait(&status); //子进程暂停继续,子进程退出则退出 if(WIFEXITED(status)) break; //获取系统调用号 orig_rax = ptrace(PTRACE_PEEKUSER, child, 8 * ORIG_RAX, NULL); if(orig_rax == SYS_write) { //读取user_regs_struct结构体中的数据 ptrace(PTRACE_GETREGS, child, NULL, ®s); //开始,打印寄存器 if(!iscalling) { iscalling = 1; //rdx是长度 printf("SYS_write call with %lld, %lld, %lld\n", regs.rdi, regs.rsi, regs.rdx); } //结束,打印寄存器 else { iscalling = 0; printf("SYS_write call return %lld\n", regs.rax); } } ptrace(PTRACE_SYSCALL, child, NULL, NULL); } } return 0; } ``` 输出如下。 ```sh ➜ Desktop ./a.out SYS_write call with 1, 32001776, 14 总用量 60K SYS_write call return 14 SYS_write call with 1, 32001776, 47 -rwxrwxrwx 1 root root 1.4K 3月 20 15:56 2.c SYS_write call return 47 SYS_write call with 1, 32001776, 49 -rwxrwxrwx 1 root root 8.7K 3月 20 16:02 a.out SYS_write call return 49 SYS_write call with 1, 32001776, 55 -rwxrwxrwx 1 root root 282 3月 15 16:40 desktop.ini SYS_write call return 55 SYS_write call with 1, 32001776, 74 drwxrwxrwx 0 root root 512 3月 19 22:44 Hacker Disassembling Uncovered SYS_write call return 74 ``` 可以看到`ls -l -h`发生了多次write系统调用,这里读取寄存器的时候可以用之前的`PTRACE_PEEKUSER`参数,也可以直接用`PTRACE_PEEKUSER`参数将寄存器的值读取到结构体`user_regs_struct`,该结构体定义在sys/user.h中。程序中`WIFEXITED`宏用来检查子进程是被`ptrace`暂停的还是准备退出,`PTRACE_SYSCALL`作用是使内核在子进程进入和退出系统调用时都将其暂停,等价于调用`PTRACE_CONT`并且在下一个系统调用前暂停。 #### ptrace其他用法 上面两个例子可以看到,ptrace的用法有很多,还比如:修改子进程系统调用参数、向其它程序注入指令等。这些和HOOK有关了,是比较有意思的用法,我也看过了,基本和上面读取子进程的用法差不多,添加了读取数据、修改数据、写入数据这三部分,从而HOOK了write函数。 [参考资料1](http://blog.csdn.net/u012417380/article/details/60470075) [参考资料2](http://www.cnblogs.com/pannengzhi/p/5203467.html) [其他关于HOOK的姿势](http://www.cnblogs.com/LittleHann/p/3854977.html) ``` ``` Last modification:January 16th, 2021 at 01:34 pm © 允许规范转载 Support 确定不打赏一下支持博主吗 ×Close Appreciate the author Sweeping payments Pay by AliPay