轻量级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":"test@aa.com","height":1.77},	
		{"lastName":"Jerry","email":"Jerry@bb.com","age":8,"height":1.55},	
		{"email":"cccc@126.com","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测试的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江下枫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值