輕量級json解析庫cJSON的使用與fuzz測試

0x10 cJSON 介紹

0x11 項目簡介

Json作爲一種輕量級的文本數據交換格式,廣爲應用,雖然是JavaScript語言中,出現的一個子集,但是早已成爲獨立的語言格式。其語法格式,類似於Python中的字典。然而,C語言原生並不支持字典,更不用說對Json文件的解析了。

cJSON就這樣應運而生,作爲一個使用鏈表實現的庫,cJSON的存在是爲了儘可能多地消除繁瑣的工作,這樣一款優秀的開源解析庫,大大降低了解析網絡編程中頻繁使用的json文件的難度。項目地址:https://github.com/DaveGamble/cJSON

cJSON的使用方式也較爲簡單,主要有以下兩種方式:

  • 源碼編譯生成 libcjson.so
  • 直接將 cJSON.c cJSON.h 複製到自己的項目裏

0x12 典型源碼說明

cJSON定義的數據結構,從下面的代碼 cJSON.c 不難看出,cJSON 使用鏈表的方式存儲

typedef struct cJSON {
    struct cJSON *next,*prev;    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
    struct cJSON *child;        /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
 
    int type;                    /* The type of the item, as above. cjson結構的類型上面宏定義的7中之一*/
 
    char *valuestring;            /* The item's string, if type==cJSON_String */
    int valueint;                /* The item's number, if type==cJSON_Number */
    double valuedouble;            /* The item's number, if type==cJSON_Number */
 
    char *string;                /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;

解析json用到的函數(頭文件 cjson.h 定義),這也是我們在項目中經常用到的一些函數

extern cJSON *cJSON_Parse(const char *value);//從 給定的json字符串中得到cjson對象

extern char  *cJSON_Print(cJSON *item);//從cjson對象中獲取有格式的json對象

extern char  *cJSON_PrintUnformatted(cJSON *item);//從cjson對象中獲取無格式的json對象
 
extern void   cJSON_Delete(cJSON *c);//刪除cjson對象,釋放鏈表佔用的內存空間

extern int    cJSON_GetArraySize(cJSON *array);//獲取cjson對象數組成員的個數

extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);//根據下標獲取cjosn對象數組中的對象

extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);//根據鍵獲取對應的值(cjson對象)
 
extern const char *cJSON_GetErrorPtr(void);//獲取錯誤字符串

0x20 使用cJSON解析json文件

我們已實際的json格式的數據爲例,講解如何使用cJSON進行解析,所有源碼已放在github上,供讀者參考,項目地址:https://github.com/liyansong2018/cJsonDemo

將github中的 cJSON.c cJSON.h 拷貝到自己的項目中,即可使用。在筆者實踐過程中,繞了不少彎路,主要是由於,對json文本的不理解,對於解析單個json格式的字符串,直接使用cJSON_Parse(str) 這樣的函數,即可轉換成 cJSON 格式,但是,對於一個完整的cJSON文件,並不是簡單的將文本讀出來,轉換成字符串,再進行調用。比如應當用下面的代碼,即 獲取json 對象

讀取文件的完整內容

char * buf;
char * read_file(char * file_name)
{
	FILE * fp = fopen(file_name, "r");
	fseek(fp, 0, SEEK_END);
	long size = ftell(fp);
	buf = (char *)malloc(size + 1);
	rewind(fp);
	fread(buf, sizeof(char), size, fp);
	buf[size] = '\0';
	return buf;
}

0x21 獲取json對象

整體的思路就是開闢堆內存,讀取文件,將文件的內容利用 cJSON_Parse 轉換成指向 cJSON 的指針類型

cJSON * get_json_object(char * file_name)
{
	FILE * fp;
	long len;
	char * content;
	cJSON * result;

	fp = fopen(file_name, "rb");
	fseek(fp, 0, SEEK_END);
	len = ftell(fp);
	fseek(fp, 0, SEEK_SET);
	content = (char *) malloc(len + 1);
	fread(content, 1, len, fp);
	fclose(fp);
	result = cJSON_Parse(content);
	free(content);
	return result;
}

測試用例如下,注意json文件的格式,每組最後一個鍵值對是不需要加符號的!這很重要,不然我們的解析程序會報錯。

{
	"people":[	
		{"firstName":"Tom","lastName":"Jason","email":"[email protected]","height":1.77},	
		{"lastName":"Jerry","email":"[email protected]","age":8,"height":1.55},	
		{"email":"[email protected]","firstName":"z","lastName":"Juliet","age":36,"height":1.33}	
	],

	"animal":{"dog":"wangcai!"}
} 

0x22 解析json文件核心代碼

int main(int argc, char const *argv[])
{	
    cJSON * root = NULL;
    cJSON * item = NULL;
    root = get_json_object(argv[1]);

    if (!root) 
    {
        printf("Error before: [%s]\n",cJSON_GetErrorPtr());
    }
    else
    {
    	printf("有格式打印: %s\n", cJSON_Print(root));
    	printf("原始文件: %s\n", cJSON_PrintUnformatted(root));

    	item = cJSON_GetObjectItem(root, "animal");
    	if(item)
    	{
    		printf("篩選一: %s\n", cJSON_Print(item));
    	}

    	item = cJSON_GetObjectItem(item, "dog");
    	if(item)
    	{
    		printf("篩選二: %s\n", cJSON_Print(item));
    		printf("%s:", item->string);   //看一下cjson對象的結構體中這兩個成員的意思
        	printf("%s\n", item->valuestring);
    	}

    	printf("打印json所有最內層鍵值對: %s\n");
    	print_json(root);
    }
	return 0;
}

0x23 編譯並運行

編譯源代碼,並且刪除中間文件。項目地址:https://github.com/liyansong2018/cJsonDemo
在這裏插入圖片描述
結果如下所示
在這裏插入圖片描述

0x30 使用afl對cJSON庫進行fuzz測試

如果想使用afl-fuzz對cJSON組件進行安全測試,需要修改項目中的主文件 main,44 行改爲 45 行,這是因爲,afl-fuzz需要從輸入的文件直接讀取字符串,而不是直接解析json文件,請注意甄別。
在這裏插入圖片描述
改完代碼之後,需要重寫 Makefile,這裏,項目已經寫好了備份文件,直接使用以下命令替換即可
在這裏插入圖片描述
這時,再運行命令

afl-fuzz -m none -i in -o out ./main @@

成功對cJSON組件進行fuzz測試
在這裏插入圖片描述

0x40 總結

本文介紹瞭如何使用cJSON庫,對json數據格式的文件進行解析,cJSON這樣一款優秀的開源輕量級json解析庫還有很多功能,比如創建json文件,限於篇幅,只是介紹瞭如何使用它進行數據解析。並且最後,結合afl,詳細講述利用源碼編譯的方式進行插樁,達到fuzz測試的效果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章