自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

靜態(tài)代碼分析之C語言篇

開發(fā) 前端
綜合來看,c代碼靜態(tài)分析目前最主要的難點在于區(qū)間分析,這部分分析的準確性對后續(xù)的漏洞分析的準確性有很大的影響。

一、序言

從本篇起,筆者將開啟c語言代碼安全分析篇章,為大家詳細剖析c語言靜態(tài)代碼分析的各種技術(shù)細節(jié)。

二、依賴分析

依賴分析是c語言靜態(tài)代碼分析中一個非常重要的環(huán)節(jié),它的分析準確與否,關(guān)系到了后續(xù)的漏洞分析的準確性。

什么是依賴分析

依賴圖是源代碼文件與其依賴庫之間的依賴關(guān)系的一種圖形表示。我們知道,在c語言中,項目真正用到的一些組件庫一般只有在編譯的時候才能夠確定,它不像java項目,一份代碼,到處運行,而是一份代碼,多次編譯,因為很多時候,我們?yōu)榱丝缙脚_的需要,需要給同一個組件準備不同的適配方案,這也是c語言項目被一些開發(fā)人員長期詬病的地方。在c/c++項目的分析工作中,我們首要解決的就是程序文件之間的依賴關(guān)系,因為這直接影響到了后續(xù)我們靜態(tài)代碼分析的準確性。

依賴圖的作用

根據(jù)生成的文件依賴圖,我們可以清晰而準確的知道整體項目的組織脈絡(luò)。在后續(xù)的符號識別和函數(shù)調(diào)用鏈生成階段,需要依靠依賴圖來找到目標庫文件,從而保證符號識別和函數(shù)調(diào)用鏈構(gòu)建的準確性。

如何進行依賴分析

針對c/c++項目文件依賴圖的生成,業(yè)內(nèi)的主流辦法是通過解析compile_commands.json文件中記錄的編譯命令來進行生成。詳情可參考pvs-stdio這款商業(yè)sca掃描器的技術(shù)說明文檔(pvs-studio.com)。而compile_commands.json文件可以通過編譯工具(make、ninja等)生成,具體的辦法步驟是:

1.收集編譯過程的輸出信息。
2.將輸出信息重定向到解析器,通過正則匹配的方式,生成compile_commands.json文件。

當然,cmake等變異工具本身也支持生產(chǎn)compile_commands.json文件。具體是在構(gòu)建方案的時候,指定參數(shù):-DCMAKE_EXPORT_COMPILE_COMMANDS=1。

那么,有了compile_commands.json文件之后要如何進行依賴分析呢?

我們先看下compile_commands.json文件的數(shù)據(jù)結(jié)構(gòu):

[
{
"directory": "/Users/pony/work/sourcehub/cmake-examples/01-basic/B-hello-headers/build",
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -I/Users/pony/work/sourcehub/cmake-examples/01-basic/B-hello-headers/include -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -o CMakeFiles/hello_headers.dir/src/Hello.c.o -c /Users/pony/work/sourcehub/cmake-examples/01-basic/B-hello-headers/src/Hello.c",
"file": "/Users/pony/work/sourcehub/cmake-examples/01-basic/B-hello-headers/src/Hello.c"
},
{
"directory": "/Users/pony/work/sourcehub/cmake-examples/01-basic/B-hello-headers/build",
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -I/Users/pony/work/sourcehub/cmake-examples/01-basic/B-hello-headers/include -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -o CMakeFiles/hello_headers.dir/src/main.c.o -c /Users/pony/work/sourcehub/cmake-examples/01-basic/B-hello-headers/src/main.c",
"file": "/Users/pony/work/sourcehub/cmake-examples/01-basic/B-hello-headers/src/main.c"
}
]

其中directory表示當前編譯目錄,command表示當前執(zhí)行的編譯命令,file表示待編譯的源碼文件。其中,在command命令中,參數(shù)I后面跟的是當前源碼所依賴的頭文件目錄的路徑,編譯器在編譯的時候會在給定的這個目錄下搜索相關(guān)的頭文件。

但是,顯然只找到頭文件是不夠的,我們還需要找到函數(shù)的定義位置,這樣我們才能夠真正建立起函數(shù)調(diào)用所在文件和函數(shù)定義所在文件之間的關(guān)聯(lián)關(guān)系。下面我們?yōu)榱烁玫恼f明aurora代碼安全分析引擎是如何對c項目進行依賴分析的,下面,我們以一個c代碼項目為示例進行闡述。

項目目錄:

.
├── CMakeLists.txt
├── README.adoc
├── include
│ └── Hello.h
└── src
├── Hello.c
└── main.c

2 directories, 5 files

CMakeLists.txt

# Set the minimum version of CMake that can be used
# To find the cmake version run
# $ cmake --version
cmake_minimum_required(VERSION 3.5)

# Set the project name
project (hello_headers)

# Create a sources variable with a link to all c files to compile
set(SOURCES
src/Hello.c
src/main.c
)

# Add an executable with the above sources
add_executable(hello_headers ${SOURCES})

# Set the directories that should be included in the build command for this target
# when running g++ these will be included as -I/directory/path/
target_include_directories(hello_headers
PRIVATE
${PROJECT_SOURCE_DIR}/include
)

include/Hello.h

#ifndef __HELLO_H__
#define __HELLO_H__

void print();

#endif

src/Hello.c

#include "Hello.h"
void print()
{
printf("hello world.");
}

src/main.c

#include "Hello.h"

int main(int argc, char *argv[])
{
print();
return 0;
}

這個項目主要由一個主程序和一個組件構(gòu)成,主程序中通過頭文件Hello.h對組件中定義的print函數(shù)進行調(diào)用。那么,首先明確,我們期望建立起來的依賴關(guān)系是main.c和Hello.c之間的依賴關(guān)系。通過對compile_commands文件的分析,我們已經(jīng)可以知道m(xù)ain.c和Hello.c都和Hello.h是有依賴關(guān)系的,只不過一個是API調(diào)用,一個是API定義。那么我們是否可以直接說明這兩個文件是有依賴的呢?那顯然是不行的,因為我們無法確定Hello.c中是否是定義了main.c中調(diào)用的print函數(shù)。只有同時滿足在main.c中調(diào)用了Hello.h中聲明的print函數(shù),且在Hello.c中定義了Hello.h中聲明的print函數(shù),那么我們才能夠認為,main.c和Hello.c之間是存在依賴關(guān)系的,這樣,我們后續(xù)才能夠據(jù)此建立起函數(shù)之間的調(diào)用關(guān)系,即函數(shù)調(diào)用鏈(這對于全局數(shù)據(jù)流構(gòu)建分析很重要)。

我們可以用下面這個示意圖來表示這個推導(dǎo)過程。

流程圖-18.jpg

其中,依賴確定模塊一,通過對compile_commands.json文件及相關(guān)源碼的AST的分析,確定兩個源碼文件引入了同一個頭文件,依賴確定模塊二,通過分析是否同時導(dǎo)入相同頭文件且一方為函數(shù)調(diào)用,另一方為函數(shù)定義,確定main.c文件到Hello.c之間的依賴關(guān)系,而調(diào)用鏈確定模塊,通過分析main函數(shù)中是否包含對函數(shù)print的調(diào)用,進而確定main.c中的main函數(shù)和src/Hello.c中的print函數(shù)的調(diào)用關(guān)系。

三、代碼數(shù)據(jù)庫構(gòu)建

代碼數(shù)據(jù)庫,顧名思義,存儲的代碼相關(guān)的一些數(shù)據(jù)。這里的代碼數(shù)據(jù)庫可以認為是第一層代碼屬性圖,我們一般會在其中存儲源代碼的AST表示形式。至于源代碼的AST表示形式,我們可以通過一些AST提取工具來獲取,比如eclipse提供的cdt工具,亦或者一些開源的前端解析工具,如antlr,均可以完成這部分工作。當然,如果我們利用這些工具提取ast,那么獲得的僅是一個ast unit class的集合,而為了后續(xù)展示方便,同時也為了能夠通過分布式架構(gòu)進行高效的分析,我們還需要對這些ast unit類進行必要的初步解析工作,然后將其轉(zhuǎn)為json格式進行持久化存儲,這部分的處理流程可用下圖表示:

流程圖-12.jpg

四、函數(shù)調(diào)用圖構(gòu)建

什么是函數(shù)調(diào)用圖

函數(shù)調(diào)用圖是函數(shù)之間的調(diào)用關(guān)系的一種圖形表示形式。在進行過程間數(shù)據(jù)流分析的時候,我們需要依賴函數(shù)調(diào)用圖求解函數(shù)調(diào)用鏈,以便于沿著這個調(diào)用鏈進行過程間數(shù)據(jù)流分析。

如何進行函數(shù)調(diào)用圖構(gòu)建

函數(shù)調(diào)用鏈構(gòu)建需要建立在依賴分析的基礎(chǔ)之上。在依賴分析中其實已經(jīng)對這部分進行了形象的闡述,這里就不做過多的贅述??偟膩碚f,依托依賴分析的結(jié)果,我們可以獲得兩個源碼文件之間的依賴關(guān)系,然后基于此,再對這些源碼中的函數(shù)定義信息進行遍歷分析,判斷下函數(shù)調(diào)用語句的函數(shù)簽名和函數(shù)定義的函數(shù)簽名是否一致,即可構(gòu)建我們后續(xù)分析所需的函數(shù)調(diào)用鏈。

五、控制流及數(shù)據(jù)流分析

什么是控制流及數(shù)據(jù)流

程序控制流表示的是程序的各個語句結(jié)構(gòu)之間的控制關(guān)系,總的來說有兩種形式,一種是基本塊控制關(guān)系圖,另一種是表達式控制關(guān)系圖。前者注重在基本塊之間的控制關(guān)系,后者注重在表達式之間的控制關(guān)系。而數(shù)據(jù)流表示的是程序中數(shù)據(jù)的流動關(guān)系,其中,我們會比較關(guān)注變量的定義和引用關(guān)系,即def-use鏈,以及一些賦值語句引起的數(shù)據(jù)流動關(guān)系。

為什么要進行控制流和數(shù)據(jù)流分析

控制流分析和數(shù)據(jù)流分析是程序靜態(tài)分析中比較核心的分析環(huán)節(jié),我們在進行代碼安全審計的時候,我們認定某一處存在漏洞,如sql注入漏洞,一般需要有比較完整的污點傳播路徑,才能夠比較有充分的理由其漏洞進行判定,而污點傳播路徑依賴于數(shù)據(jù)流分析,而數(shù)據(jù)流分析也依賴于控制流分析。總的來說,java和c的數(shù)據(jù)流分析的方法大同小異,針對java方面的控制流及數(shù)據(jù)流分析方法,我已在前一篇文章中進行了詳細的描述,這里就不做過多的贅述了,詳情見:《DevSecOps建設(shè)之白盒續(xù)篇 - FreeBuf網(wǎng)絡(luò)安全行業(yè)門戶》。

六、代碼安全分析

棧溢出漏洞

那么,我們?nèi)绾螌㈧o態(tài)代碼分析技術(shù)應(yīng)用在代碼安全分析上呢?我們以pwnable.kr上的一個棧溢出靶場bof為例,詳細闡述aurora白盒引擎是如何進行c代碼安全分析的。漏洞代碼如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
char overflowme[32];
printf("overflow me : ");
gets(overflowme); // smash me!
if(key == 0xcafebabe){
system("/bin/sh");
}
else{
printf("Nah..\n");
}
}
int main(int argc, char* argv[]){
func(0xdeadbeef);
return 0;
}

簡要說明:這是一段比較典型的棧溢出漏洞代碼,在函數(shù)func中,我們聲明并定義了一個字符數(shù)組overflowme,并給其分配了32個字節(jié)的內(nèi)存空間。程序中調(diào)用gets函數(shù),獲取命令行輸入流,如果字符流長度在overflowme這個變量定義的合法區(qū)間內(nèi),那么這個程序?qū)凑照5倪壿嬜?,如果超過了overlfowme定義的合法區(qū)間,那么,在沒有對這個輸入流長度進行合法性校驗的前提下,將會造成棧溢出漏洞,覆蓋變量key的內(nèi)存空間,影響預(yù)期的分支流程走勢,即走到if的then邏輯部分,執(zhí)行system函數(shù),反彈一個bash窗口。這個調(diào)用過程發(fā)生時,內(nèi)存的棧幀分布示意圖如下:

流程圖-13.jpg

其中,下層為func棧幀,上層為main棧幀,棧幀的排布由高地址向低地址排布,先調(diào)用的函數(shù)占據(jù)高地址,后調(diào)用的函數(shù)占據(jù)低地址,而數(shù)據(jù)寫入規(guī)則則是由低地址到高地址。我們在實際利用的時候,如果要改變if分支中的邏輯,可通過輸入以下payload實現(xiàn)攻擊:

payload="a"*52+ chr(0xbe) + chr(0xba) + chr(0xfe) +chr(0xca);

其中,chr(0xbe) + chr(0xba) + chr(0xfe) +chr(0xca)即為0xdeadbeef,而52表示key和overflow之間的距離,這些數(shù)據(jù)可以通過gdb調(diào)試獲得。通過給定overflowme超出其合法值范圍的payload,我們即可實現(xiàn)對原有邏輯的篡改。

那么,針對這種漏洞,我們要怎樣通過靜態(tài)分析方法進行檢測呢?

我們可以從棧溢出的原理出發(fā),來思考我們的防護策略。我們知道,棧溢發(fā)生的本質(zhì)原因是在對變量進行傳值的時候,未對輸入數(shù)據(jù)的字節(jié)長度進行合法性校驗。那么,我們在檢測的時候可以枚舉源代碼中的內(nèi)存寫入操作的函數(shù)調(diào)用表達式(如gets、memcpy等),通過分析其在進行內(nèi)存寫入的時候,寫入數(shù)據(jù)的值的大小是否比聲明的區(qū)間大來判定其是否存在漏洞。那么,問題就集中在了對兩者值的大小分析(區(qū)間分析)上了。針對緩沖區(qū)大小,我們可以根據(jù)變量對應(yīng)的def-use鏈找到變量定義的位置,然后結(jié)合其變量定義表達式中和變量區(qū)間定義相關(guān)的ast數(shù)據(jù),進行綜合分析判定。針對輸入的數(shù)據(jù)的區(qū)間大小,我們也可以用類似方式進行分析。當然,區(qū)間的值可能并不一定是常量,如果要追求分析的準確性,那么就要對區(qū)間范圍進行進一步的約束求解。

七、區(qū)間分析

什么是區(qū)間分析

區(qū)間分析是指通過約束求解算法,對變量和表達式的取值范圍進行跟蹤,為進一步的程序分析提供精確的數(shù)據(jù)支持。

為什么要做區(qū)間分析

通過上文可知,我們在做一些代碼安全分析的時候,比如棧溢出分析,如果我們不能夠比較準確的分析緩沖區(qū)和輸入數(shù)據(jù)的區(qū)間大小,那么我們是無法準確地判斷是否存在棧溢出漏洞的。

如何做區(qū)間分析

學(xué)術(shù)上,針對區(qū)間分析早有很多相關(guān)的方法分析方法,如王雅文、宮云戰(zhàn)等在第五屆中國測試學(xué)術(shù)會議上發(fā)表的論文《區(qū)間運算在軟件缺陷檢測中的應(yīng)用》中就提到了一種區(qū)間分析方法。論文中提出,可針對不同的變量類型設(shè)置不同的初始區(qū)間值,然后通過表達式區(qū)間分析、條件區(qū)間分析、控制流區(qū)間分析三個不同緯度對變量的取值范圍進行約束求解。

1. 表達式區(qū)間分析

如表達式3*(++i),如果i的取值范圍是[1,1],那么執(zhí)行完3*(++i)之后,其取將范圍將變成:[6,6]。

2. 條件區(qū)間分析

例如:

if(x>2){

}
else{
}

區(qū)間分析在代碼質(zhì)量領(lǐng)域的應(yīng)用

區(qū)間分析,可以應(yīng)用于程序中的不可達代碼塊檢測、代碼覆蓋率分析等。例如:

void func(){
int i=5;
if(i<0){
i++;
}
}

初始控制流圖:

流程圖-14.jpg

轉(zhuǎn)為帶區(qū)間信息的控制流圖:

流程圖-15.jpg

從帶區(qū)間的控制流圖中可以發(fā)現(xiàn),在執(zhí)行到stmt_3語句時,因為i<0這個條件的取值范圍是[-∞,-1]n[5,5]=?,所以i<0分支下的所有表達式語句上i的區(qū)間范圍會被設(shè)置為null,表示不可達到。統(tǒng)計出所有執(zhí)行的語句數(shù)量及總語句數(shù)量即可計算得語句覆蓋率,計算公式為:可執(zhí)行語句數(shù)量/語句總數(shù)。統(tǒng)計所有可達分支數(shù)量及總的分支數(shù)量即可計算得分支覆蓋率,計算公式為:可達分支/分支總數(shù)。

區(qū)間分析在代碼安全領(lǐng)域的應(yīng)用

比如上面的那個溢出漏洞案例。我們可以得到其初始控制流圖為:

流程圖-16.jpg

轉(zhuǎn)化為區(qū)間為帶區(qū)間信息的控制流圖:

流程圖-17.jpg

其中在stmt_3(即語句gets(overflowme))中,overflowme的區(qū)間范圍易求得為[0,32],而用戶輸入的數(shù)據(jù)的范圍是[0,+∞],這里用1000作為缺省值,當然為了以防萬一,也可以設(shè)置大一點,顯而易見這里用戶輸入的數(shù)據(jù)的大小的區(qū)間范圍遠大于overflowme最大的取值32,那么這里是肯定存在溢出漏洞的。當然,要比較精確的判定溢出漏洞,我們還可以結(jié)合compile_commands.json中的編譯命令(如一些編譯時設(shè)定的堆棧溢出的防護策略)進行綜合判定。

八、CI流程自動化

一款靜態(tài)代碼安全分析工具,要好用,不僅要引擎能力足夠硬核,在流程方面也應(yīng)該自動化,讓使用者能夠很方便的配置,很容易地集成到一些主流的CI piplines中。下面我們以gitlab ci,詳細闡述我們是如何實現(xiàn)c代碼自動化安全、代碼質(zhì)量檢測。

以下是示例ci例子

# This file is a template, and might need editing before it works on your project.
# This is a sample GitLab CI/CD configuration file that should run without any modifications.
# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
# it uses echo commands to simulate the pipeline execution.
#
# A pipeline is composed of independent jobs that run scripts, grouped into stages.
# Stages run in sequential order, but jobs within stages run in parallel.
#
# For more information, see: https://docs.gitlab.com/ee/ci/yaml/README.html#stages

stages: # List of stages for jobs, and their order of execution
- build
- test
- deploy

build-job: # This job runs in the build stage, which runs first.
image: ci_vtsmap:latest
stage: build
script:
- echo "Compiling the code..."
- mkdir build && cd build
- cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ..
- curl -F "file=@compile_commands.json" -F "user=${GITLAB_USER_NAME}" -F "projectname=${CI_PROJECT_NAME}" -F "language=c" -F "email=xxx@gmail.com" -F "type=git"-F "branch=${CI_COMMIT_BRANCH}" -F "commitid=${CI_COMMIT_SHA}" -F "gitaddr=${CI_PROJECT_URL}" -F “token=xxx” -x POST http://[domain]/pushjob/
- make .
- echo "Compile complete."

deploy-job: # This job runs in the deploy stage.
stage: deploy # It only runs when *both* jobs in the test stage complete successfully.
script:
- echo "Deploying application..."
- echo "Application successfully deployed."

其中,我們會在原有的ci腳本中加入兩行命令,一行時在原有的cmake命令上加上參數(shù)-DCMAKE_EXPORT_COMPILE_COMMANDS=1 來生成compile_commands.json文件,以便于后端的靜態(tài)分析引擎進行依賴分析。以下命令用于提交任務(wù)信息:

curl -F "file=@compile_commands.json" -F "user=${GITLAB_USER_NAME}" -F "projectname=${CI_PROJECT_NAME}" -F "language=c" -F "email=xxx@gmail.com" -F "type=git" -F "branch=${CI_COMMIT_BRANCH}" -F "commitid=${CI_COMMIT_SHA}" -F "gitaddr=${CI_PROJECT_URL}" -F “token=xxx” -x POST http://[domain]/pushjob/

九、總結(jié)

綜合來看,c代碼靜態(tài)分析目前最主要的難點在于區(qū)間分析,這部分分析的準確性對后續(xù)的漏洞分析的準確性有很大的影響。所以在c代碼靜態(tài)分析方面,區(qū)間分析方面需要花比較大的功夫去鉆研,不僅要保證分析的分析的準確性,同時也要考慮到分析的效率,因為很多c代碼項目,如linux內(nèi)核等,代碼量非常龐大,如果沒有一個比較合理的算法,加快代碼分析速度,那么接入到企業(yè)內(nèi)部使用,其體驗感也是非常差的。當然,精度和速度兩者一般情況下是一種此消彼長的關(guān)系的,如何從中達到一個平衡,還需要不斷的進行測試和實踐。

王雅文,宮云戰(zhàn),楊朝紅,肖慶,區(qū)間運算在軟件缺陷檢測中的應(yīng)用,第五屆中國測試學(xué)術(shù)會議論文集,2008,51-52。

責任編輯:武曉燕 來源: FreeBuf.COM
相關(guān)推薦

2013-10-31 11:08:15

2021-06-08 13:56:34

工具靜態(tài)代碼

2013-04-08 10:47:42

Xcode靜態(tài)代碼分析

2009-08-27 10:44:21

C#靜態(tài)變量

2021-04-13 11:15:54

網(wǎng)絡(luò)安全C語言循環(huán)結(jié)構(gòu)

2021-04-08 11:10:22

網(wǎng)絡(luò)安全C語言if…else…

2023-12-29 08:27:36

C語言靜態(tài)變量開發(fā)

2023-11-21 15:59:34

C語言Windows

2009-08-27 16:00:03

C#靜態(tài)字段C#實例字段

2022-01-19 08:00:00

靜態(tài)代碼動態(tài)代碼開發(fā)

2022-11-28 08:15:14

Go語言代碼

2022-12-05 09:32:29

Go 語言風(fēng)格規(guī)范

2012-04-25 11:04:13

Visual Stud

2024-03-19 08:02:28

集群GaussDB指標

2022-03-25 00:00:00

Splunk搜索SPL

2020-11-23 08:54:14

Go語言結(jié)構(gòu)體

2009-08-21 08:59:35

C#語言規(guī)范

2024-08-05 14:10:04

2020-11-30 06:17:03

Go語言

2020-11-26 06:40:24

Go語言基礎(chǔ)
點贊
收藏

51CTO技術(shù)棧公眾號