高鐵北京回杭州的路上,想到一個簡單的話題。
在一個複雜的程序中,發生踩內存是一件非常噁心的事情,很難通過什麼線索直到誰在哪個函數中往哪個地址寫了什麼,比方說數組越界寫什麼的。
去年,我曾經長篇大論了一篇:
https://blog.csdn.net/dog250/article/details/90690292
現在看來,我又忍不住懟一波了…那篇文章裏的方法純粹就是爲了炫技,搞什麼又是純彙編又是內聯彙編,簡直太複雜了。
本文給出一個清新版的,試試看下面的代碼:
// mem_monitor.c
#include <sys/mman.h>
#include <stdio.h>
#include <signal.h>
#include <asm/processor-flags.h>
char *buff = NULL;
void new_func(int index)
{
buff[index] = 'c'; // write memory!
buff[index + 1] = 'd'; // write memory!
}
void main_func()
{
buff[0] = 'a'; // write memory!
buff[1] = 'b'; // write memory!
new_func(2);
buff[4] = 'e'; // write memory!
}
#define F_OFFSET 200
#define PC_OFFSET 192
#define BP_OFFSET 144
#define CR2_OFFSET 240
// 單步信號處理函數
void resume_trap()
{
unsigned long *p;
p = (unsigned long*)((unsigned char *)&p + F_OFFSET);
mprotect(buff, 1024, PROT_READ);
*p &= ~X86_EFLAGS_TF;
}
// 寫保護信號處理函數
void wp_trap(int signo)
{
unsigned long *p;
p = (unsigned long*)((unsigned char *)&p + PC_OFFSET);
printf("---RIP:[%lx]----", *p); // 打印指令地址
p = (unsigned long*)((unsigned char *)&p + BP_OFFSET);
printf("STACK:[%lx]----", *p); // 打印堆棧地址
p = (unsigned long*)((unsigned char *)&p + CR2_OFFSET);
printf("at Address:[%lx]----\n", *p); // 打印寫入的位置
p = (unsigned long*)((unsigned char *)&p + F_OFFSET);
*p |= X86_EFLAGS_TF;
mprotect(buff, 1024, PROT_READ|PROT_WRITE);
return;
}
int main()
{
int i;
buff = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
printf("buffer base at[%lx]\n", buff);
mprotect(buff, 1024, PROT_READ);
signal(SIGSEGV, wp_trap);
signal(SIGTRAP, resume_trap);
main_func();
buff[5] = 'f'; // write memory!
for (i = 0; i < 6; i++) {
printf("%c ", buff[i]);
}
printf("\n");
return 0;
}
來,看下效果:
[root@localhost checker]# gcc mem_monitor.c
[root@localhost checker]# ./a.out
buffer base at[7f0f8c72e000]
---RIP:[40067a]----STACK:[7fffb98ec3c0]----at Address:[7f0f8c72e000]----
---RIP:[400688]----STACK:[7fffb98ec3c0]----at Address:[7f0f8c72e001]----
---RIP:[400653]----STACK:[7fffb98ec3b0]----at Address:[7f0f8c72e002]----
---RIP:[40066a]----STACK:[7fffb98ec3b0]----at Address:[7f0f8c72e003]----
---RIP:[4006a0]----STACK:[7fffb98ec3c0]----at Address:[7f0f8c72e004]----
---RIP:[40083b]----STACK:[7fffb98ec3e0]----at Address:[7f0f8c72e005]----
a b c d e f
和去年那篇相比,本文實現了更加細粒度的內存寫監控。去年那個是函數級別的,本文這個是則是指令級別的,從上面的代碼和執行效果上,可以一目瞭然!
當然了,經理會認爲這些不值得一提。
浙江溫州皮鞋溼,下雨進水不會胖!