熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Oracle >> 正文

Linux系統病毒研究之一

2022-06-13   來源: Oracle 

  作者Silvio Cesare
  編譯nixen
  
  摘要:本文討論了一個修改ELF文件實現共享庫調用重定向的方法修改可執行文件的程序連接表(Procedure Linkage Table)可以使被感染的文件調用外部的函數這要比修改LD_PRELOAD環境變量實現調用的重定向優越的多首先不牽扯到環境變量的修改其次是更為隱蔽本文將提供一個基於x/Linux的實現
  
  前言
  
  這是nixen搜集的一組有關Linux系統下病毒的研究文章沒有先後的次序文中的代碼可能有破壞性只能用於研究如果你用於非法目的後果自負
  
  感染ELF文件程序連接表實現共享庫調用的重定向
  
  簡介
  
  本文討論了一個修改ELF文件實現共享庫調用重定向的方法修改可執行文件的程序連接表(Procedure Linkage Table)可以使被感染的文件調用外部的函數這要比修改LD_PRELOAD環境變量實現調用的重定向優越的多首先不牽扯到環境變量的修改其次是更為隱蔽本文將提供一個基於x/Linux的實現如果你對UNIX系統病毒比較感興趣請參考以下網址
  
  (UNIX病毒郵件列表)
  ~silvio (作者主頁)
  
  程序連接表(Procedure Linkage Table)
  
  下面是ELF規范中關於程序連接表的敘述
  
  
  
  程序連接表(PLT)
  
  在ELF文件中全局偏移表(Global Offset TableGOT)能夠把位置無關的地址定位到絕對地址程序連接表也有類似的作用它能夠把位置無關的函數調用定向到絕對地址連接編輯器(link editor)不能解決程序從一個可執行文件或者共享庫目標到另外一個的執行轉移結果連接編輯器只能把包含程序轉移控制的一些入口安排到程序連接表(PLT)中在system V體系中程序連接表位於共享正文中但是它們使用私有全局偏移表(private global offset table)中的地址動態連接器(例如ldso)會決定目標的絕對地址並且修改全局偏移表在內存中的影象因而動態連接器能夠重定向這些入口而勿需破壞程序正文的位置無關性和共享特性可執行文件和共享目標文件有各自的程序連接表
  
  
  表使用絕對地址的程序連接表
  PLT:pushl got_plus_
  jmp *got_plus_
  nop; nop
  nop; nop
  PLT:jmp *name_in_GOT
  pushl $offset
  jmp PLT@PC
  PLT:jmp *name_in_GOT
  pushl $offset
  jmp PLT@PC
  
  
  表位置無關的程序連接表
  PLT:pushl (%ebx)
  jmp *(%ebx)
  nop; nop
  nop; nop
  PLT:jmp *name@GOT(%ebx)
  pushl $offset
  jmp PLT@PC
  PLT:jmp *name@GOT(%ebx)
  pushl $offset
  jmp PLT@PC
  
  
  
  注意從兩個表中可以看出兩種方式的指令使用不同的操作數尋址模式但是它們和動態連接器的接口是一樣的
  
  下一步動態連接器和程序本身使用程序連接表和全局偏移表共同解析符號引用
  
  當第一次建立程序的內存影象時動態連接器會把全局偏移表的第二和第三個入口設置為特定的值下面會對這些值進行介紹
  如果程序連接表是位置無關的需要把全局偏移表地址保存在%ebx中進程影象中的每個共享目標文件都有自己的程序連接表而且程序的執行流程改變時也只能跳轉到同一個目標文件的程序連接表入口例如一個程序foo它的動態連接庫為barso它們都有自己程序連接表那麼foo正文段調用某個程序連接表入口時只能跳轉到foo文件自己的程序連接表而不能轉到barso的程序連接表中因此在調用程序連接表入口之前函數調用代碼應該設置全局偏移表的基址寄存器
  為了便於描述我們假設程序要調用另一個目標文件的函數name因此首先需要把程序執行控制權轉移到標記為PLT的代碼處
  這段代碼的第一條指令就是跳轉到name在全局偏移表的入口地址因為name是在另一個目標文件中的調用所以在初始化時全局偏移表沒有保存name的真實地址而只是保存了這段代碼第二條指令pushl的地址
  因而程序會接著執行第二條指令在棧壓入一個重新定位的偏移值(offset)這個重新定位的偏移值是重定位表中的一個位的非負字節偏移值這個特指的重定位入口是R__JMP_SLOT類型的它的偏移值將指定先前jmp指令用到的全局偏移表的入口重定位入口還有一個符號表索引告訴動態連接器哪個符號被引用在這個例子中是name
  在棧中壓入重定位偏移值以後程序接著就跳轉到PLT它是程序連接表的第一個入口pushl指令在棧中壓入第二個全局偏移表的入口(got_plus_或者(%ebx))從而給動態連接器一個單字識別信息程序接著跳轉到全局偏移表的第三個入口中的地址(got_plus_或者(%ebx))將控制權轉移給動態連接器
  當動態連接器獲得控制權它就會展開棧讀出指定的重定位入口找出符號表的值把name的真正地址保存到全局偏移表的name入口中然後將控制權轉移給目的目標
  因此如果再次調用name就會直接從程序連接表入口轉移到name而不必再次調用動態連接器也就是說PLT的jmp指令將轉移到name而不是接著執行push指令
  LD_BIND_NOW環境變量能夠改變動態連接行為如果這個環境變量不為空動態連接器在把控制權交給程序之前會先為程序連接表賦值也就是說在進程初始化期間動態連接器為R__JMP_SLOT類型的重定位入口賦值以便在第一次調用時不必通過動態連接器就能夠跳轉到目標地址反之如果這個環境變量為空動態連接器就暫不為程序連接表入口賦值不對符號進行解析和重定位直到第一次調用一個程序連接表入口才對其做相應的處理這種方式叫作後期連接(lazy binding)方式
  
  注意後期連接(lazy binding)方式一般會大大提高應用程序的性能因為不必為解析無用的符號浪費動態連接器的開銷不過有兩種情況例外第一對一個共享目標函數進行初始化處理花費的時間比調用正式的執行時間長因為動態連接器會攔截調用以解析符號而這個函數功能又比較簡單第二如果發生錯誤和動態連接器無法解析符號動態連接器就會終止程序使用後期連接方式這種錯誤可能會在程序執行過程中隨時發生而有些應用程序對這種不確定性有比較嚴格的限制因此需要關閉後期連接方式在應用程序接受控制權之前讓動態連接器處理進程初始化期間發生的這些錯誤
  
  下面將對其細節做一些解釋
  
  因為在編譯時共享庫的調用不能被連接到程序中所以需要對其做特殊處理直到程序運行時共享庫才是有效的PLT就是為了處理這種情況PLT保存調用動態連接器的有關代碼由動態連接器對所需例程進行定位
  可執行目標是調用PLT的某個入口來實現對共享庫例程的調用而不是直接調用共享庫例程然後由PLT解析符號表示什麼以及進行其它操作
  下列代碼來自ELF規范
  
  PLT:jmp *name_in_GOT
  pushl $offset
  jmp PLT@PC
  
  從這段代碼中可以得到一些重要的信息這是一個例程調用而不是庫調用進程初始化之後name_in_GOT指向後面的push指令offset代表一個重定位偏移值(參見ELF規范)它包含一個符號引用這個符號表示這個庫調用使後面的jmp指令能夠跳轉到動態連接器為了避免下次調用這個共享庫例程時重復這個流程動態連接器接著會修改name_in_GOT讓其直接指向這個例程這樣就能夠節約再次調用的時間
  
  上面的敘述總結了PLT在搜索庫調用時的重要性因此我們可以修改name_in_GOT使其指向我們自己的代碼取代原先庫調用實現病毒的傳染如果在取代之前我們保存GOT的狀態那麼還能夠重新調用原來的庫調用而且可以實現任意庫調用的重定向
  
  感染ELF文件
  
  為了實現庫調用的重定向需要在可執行目標文件中加入新的代碼本文我們將不涉及這方面的問題這在~silvio已經有專門的文章論述
  
  PLT重定向
  
  入口點的算法如下
  
  把正文段標記為可寫
  保存PLT(GOT)入口
  使用新的庫調用地址代替PLT(GOT)入口
  新的庫調用算法如下
  
  實現新的庫調用的功能
  保存原來的PLT(GOT)入口
  調用庫調用
  再次保存PLT(GOT)如果它被修改了的
  使用新的庫調用的地址代替PLT(GOT)入口
  為了更清楚地解釋PLT重定向是如何工作的我們在此解析一段簡單的代碼在這段代碼中被重定向的是printf新的代碼是在printf輸出一個字符串之前打印一條消息
  
  好吧現在開始
  
  首先保存寄存器
  x /* pusha */
  
  把正文段標記為rwx因為正文段通常是不可寫的所以為了能夠修改PLT我們需要把它改為可寫的通過mprotect系統調用
  xbxdxxx /* movl $%eax */
  xbbxxxx /* movl $text_start%ebx */
  xbxxxx /* movl $x%ecx */
  xbaxxxx /* movl $%edx */
  xcdx /* int $x
From:http://tw.wingwit.com/Article/program/Oracle/201311/16530.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.