2010-06-01 81 views
5

有人告訴我如何阻止某個程序中的某些特定系統調用嗎?我正在構建一個系統,它需要一段C源代碼,用gcc編譯並運行它。出於安全原因,我需要防止編譯的程序調用某些系統調用。從源代碼級別(例如,剝離gcc頭文件,檢測惡意外部調用...)到可執行級別,有沒有辦法做到這一點?GCC如何阻止程序中的系統調用?

編輯#1:添加有關惡意呼叫的詳細信息。

編輯#2:我的系統是一個GNU/Linux的系統。

編輯#3:

我試圖在幾天之內的一些方法,這裏是到目前爲止,我已經得到了結論:

  1. 掃描的源代碼並沒有解決的主要因爲人們總是可以很好地隱藏他/她的C源文件。
  2. 「重寫C符號」對於圖書館來說效果很好,但對於系統調用,我還沒有達到我想要的。這個想法並沒有死,但是,這樣做肯定會導致我大量的時間攻擊(gcc和/或ld)。
  3. 許可deescalation像一個魅力。我可以使用fakeroot或「訪客」用戶來執行此操作。這種方法也是最容易實現的。

另一個是native client我還沒有試過,但是由於項目和我的工作之間的共同點,我肯定會在不久的將來。

+1

這看起來很不容易。畢竟,幾乎所有可以想象的C程序都會合法地進行系統調用,如:分配內存,讀寫文件,使用標準輸入和標準輸出等。 – 2010-06-01 09:16:39

+0

你認爲什麼樣的程序永遠不會進行系統調用?請發表一個例子。 – 2010-06-01 09:23:55

+0

@高性能標記:嗯,我只是想阻止一些特定的系統調用,因爲它們是有限的,諸如fork(),open()等調用,... @Neil Butterworth:我的意思不是所有的系統調用,而是惡意的系統調用,例如execv()可以用來執行一個BASH腳本,將我的數據抹去在磁盤上。 – 2010-06-01 09:29:19

回答

8

正如其他人所指出的,程序不可能避免進行系統調用,他們會在整個地方執行C庫。

但是,如果您的平臺支持它(例如Linux),您可能會仔細使用LD_PRELOAD機制,取得一些進展:您使用與C庫中相同的符號名稱編寫共享庫所謂的而不是預期的libc函數。 (例如,電子圍欄被構建爲在基於Debian的系統和攔截共享庫調用mallocfree等)

我懷疑你可以利用這一機制來捕獲或參數檢查調用任何libc函數你不喜歡,也許要記下你認爲無條件安全的那些。掃描編譯後的可執行文件對應的代碼INT 80可能是合理的,以避免任何嘗試生成系統調用(0xcd 0x80 - 儘管要小心誤報)。不過我只給這個思想的幾分鐘,我可以很容易地錯過了一些東西或者這可能變成是不切實際的......

+0

關於這種重寫C符號的絕妙想法!這樣我就可以完全控制和檢測有不良目的的呼叫。我一定會嘗試以及其他兩種方式(掃描源文件和權限deescalation)並查看哪個是最合適的。 – 2010-06-01 10:04:05

+1

我剛剛想到了這個機制的更多詳細例子:檢查'fakeroot'包。 – crazyscot 2010-06-01 10:12:53

+3

可以在不使用任何庫的情況下執行系統調用,因此如果您希望構建針對惡意用戶的安全系統,則僅使用LD_PRELOAD是不夠的。 – 2010-06-01 19:44:43

1

只是想說明,這是不可能的,下面的程序:

int main() { 
    return 0; 
} 

使得作爲使用strace報告超過20系統調用。電話包括open(兩次),這是您似乎想要阻止的電話之一。

+0

謝謝,但我的意思是一些特定的,不是全部。 – 2010-06-01 09:33:27

3

你不能。

即使是這樣的程序:

#include <stdio.h> 

int main() 
{ 
    printf("Hello, World\n"); 
    return 0; 
} 

使得至少一個系統調用(發送字符串「你好,世界\ n」來標準輸出)。系統調用是程序與外部世界進行交互的唯一途徑。使用操作系統的安全模型來確保安全。

編輯本評論:

我的意思是不是所有的系統調用,但惡意的系統調用,例如execv()可以用來執行一個BASH腳本,將我的數據抹去在磁盤上。

您的操作系統已經包含阻止此類事情發生的機制。例如,爲了使bash腳本清除數據,該進程必須已具有對該數據的寫入權限。這意味着它一定是由你或者根源啓動的。你唯一真正的選擇是不安裝不值得信任的軟件。

順便說一下,根據您的平臺,execv不一定是系統調用。在Linux上,它是真正的系統調用(execve)的C庫封裝器。

+0

現在你提到了文件系統權限,我通過將它作爲一個自定義用戶執行來找到解決方案,該用戶在該目錄中沒有寫權限。我差點忘了。謝謝:-D。 – 2010-06-01 09:50:23

1

那麼,如果你只是想阻止特定的調用,爲什麼不嘗試編譯前通過源代碼執行grep?並拒絕使用不安全系統調用的程序。

+0

我可以通過使用掃描源代碼的程序自動執行此操作嗎?例如,我有一個包含「unistd」的源文件。h「,這可能會導致惡意情況,另一個源文件的結構名爲」unistd「,其中有一個名爲」.h「的字段,我該如何區分這些情況? – 2010-06-01 09:41:54

+0

您當然可以自動執行。與grep或sed,但如果這不是你的一杯茶任何常見的編程語言,特別是具有良好的正則表達式能力,將做 – 2010-06-01 09:49:23

+0

是的,這可能會工作,但我必須仔細設計算法掃描使用grep或inline Perl,或限制源代碼命名約定謝謝:-) – 2010-06-01 09:52:55

4

您可以通過從封裝器中派生編譯的程序來運行編譯後的程序,並使用Linux ptrace(2)工具攔截並檢查程序調用的所有系統調用。

以下示例代碼顯示了一個運行/ usr/bin/w命令的打包程序,打印由該命令調用的每個系統調用,並在嘗試調用write(2)系統調用時終止該命令。

 
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <sys/ptrace.h> 
#include <sys/wait.h> 
#include <sys/syscall.h> 
#include <sys/reg.h> 

#define BAD_SYSCALL __NR_write 

int main(int argc, char *argv) 
{ 
    pid_t child; 
    int status, syscall_nr; 

    child = fork(); 
    if (child == 0) { 
     /* In child. */ 
     ptrace(PTRACE_TRACEME, 0, NULL, NULL); 
     execl("/usr/bin/w", NULL, NULL); 
     // not reached 
    } 

    /* In parent. */ 
    while (1) { 
     wait(&status); 

     /* Abort loop if child has exited. */ 
     if (WIFEXITED(status) || WIFSIGNALED(status)) 
      break; 

     /* Obtain syscall number from the child's process context. */ 
     syscall_nr = ptrace(PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL); 
     printf("Child wants to execute system call %d: ", syscall_nr); 

     if (syscall_nr != BAD_SYSCALL) { 
      /* Allow system call. */ 
      printf("allowed.\n"); 
      ptrace(PTRACE_SYSCALL, child, NULL, NULL); 
     } else { 
      /* Terminate child. */ 
      printf("not allowed. Terminating child.\n"); 
      ptrace(PTRACE_KILL, child, NULL, NULL); 
     } 
    } 

    exit(EXIT_SUCCESS); 
} 

你可以使用ptrace的更強大的東西,如檢查和更改進程的地址空間(例如,獲得和修改傳遞給系統調用的參數)。

在這個Linux Journal Article及其follow-up可以找到一個很好的介紹。

相關問題