AFLNet定制协议:
最近需要针对协议扩展一下AFLNet,因此我打算根据官方文档进行修改。按照官方页面的说明,如果需要扩展到其他协议,那么需要在aflnet.h和aflnet.c中定义extract_requests_*和extract_response_codes_*函数(*表示需要定制的协议名称)。
本篇来分析一下aflnet.h中的结构体。
在aflnet.h中定义了三个结构体以及两个枚举量enum,我们来挨个分析。
typedef struct {
int start_byte;
int end_byte;
char modifiable;
unsigned int *state_sequence;
unsigned int state_count;
} region_t;
region_t *Regions保存了发完被测服务器的消息信息。其中,start_byte是开始的比特,end_byte是结束的比特,modifiable是可修改的flag信息, *state_sequence指向保存的从服务器反馈的状态信息,state_count是state_sequence中保存的状态数量。
typedef struct {
char *mdata; /* Buffer keeping the message data */
int msize; /* Message size */
} message_t;
其中,mdata指向被保存的message数据,msize是message的长度。
typedef struct {
u32 id; /* state id */
u8 is_covered; /* has this state been covered */
u32 paths; /* total number of paths exercising this state */
u32 paths_discovered; /* total number of new paths that have been discovered when this state is targeted/selected */
u32 selected_times; /* total number of times this state has been targeted/selected */
u32 fuzzs; /* Total number of fuzzs (i.e., inputs generated) */
u32 score; /* current score of the state */
u32 selected_seed_index; /* the recently selected seed index */
void **seeds; /* keeps all seeds reaching this state -- can be casted to struct queue_entry* */
u32 seeds_count; /* total number of seeds, it must be equal the size of the seeds array */
} state_info_t;
该结构体主要包含了协议状态的相关信息,如状态id,该状态是否被覆盖(可用于计算模糊测试的覆盖度),经过该状态的路径数量,当该状态被选中时扩展的新路径数量,该状态被选中的数量,生成的测试数量,保存到过该状态的种子以及全部种子数量。
enum {
/* 00 */ PRO_TCP,
/* 01 */ PRO_UDP
};
enum {
/* 00 */ INVALID_SELECTION,
/* 01 */ RANDOM_SELECTION,
/* 02 */ ROUND_ROBIN,
/* 03 */ FAVOR
};
两个枚举量分别定义了使用的传输层协议和种子选择算法。
接下来我们分析一下aflnet.c里面的extract_request_*和extract_response_codes_*函数。
具体分析一下关于smtp协议的两个函数。
region_t* extract_requests_smtp(unsigned char* buf, unsigned int buf_size, unsigned int* region_count_ref)
{
char *mem;
unsigned int byte_count = 0; //消息包中正指向的字符顺序
unsigned int mem_count = 0;
unsigned int mem_size = 1024;
unsigned int region_count = 0;
region_t *regions = NULL;
char terminator[2] = {0x0D, 0x0A}; //终止符,分别为/r和/n,smtp通过/r/n结束。
mem=(char *)ck_alloc(mem_size);
unsigned int cur_start = 0;
unsigned int cur_end = 0;
while (byte_count < buf_size) {
memcpy(&mem[mem_count], buf + byte_count++, 1); //从buf+byte_count++所指向的内容中取1字节复制到mem[mem_count]中
//Check if the last two bytes are 0x0D0A
if ((mem_count > 1) && (memcmp(&mem[mem_count - 1], terminator, 2) == 0)) {
// 创造一个新的region
region_count++;
regions = (region_t *)ck_realloc(regions, region_count * sizeof(region_t));
regions[region_count - 1].start_byte = cur_start;
regions[region_count - 1].end_byte = cur_end;
regions[region_count - 1].state_sequence = NULL;
regions[region_count - 1].state_count = 0;
// 到下一个数据包,重新定义消息的开始和结束
mem_count = 0;
cur_start = cur_end + 1;
cur_end = cur_start;
} else {
mem_count++;
cur_end++;
//Check if the last byte has been reached
if (cur_end == buf_size - 1) {
// 创造新的区域
region_count++;
regions = (region_t *)ck_realloc(regions, region_count * sizeof(region_t));
regions[region_count - 1].start_byte = cur_start;
regions[region_count - 1].end_byte = cur_end;
regions[region_count - 1].state_sequence = NULL;
regions[region_count - 1].state_count = 0;
//退出while循环
break;
}
if (mem_count == mem_size) {
//enlarge the mem buffer
mem_size = mem_size * 2;
mem=(char *)ck_realloc(mem, mem_size);
}
}
}
if (mem) ck_free(mem);
//in case region_count equals zero, it means that the structure of the buffer is broken
//hence we create one region for the whole buffer
if ((region_count == 0) && (buf_size > 0)) {
regions = (region_t *)ck_realloc(regions, sizeof(region_t));
regions[0].start_byte = 0;
regions[0].end_byte = buf_size - 1;
regions[0].state_sequence = NULL;
regions[0].state_count = 0;
region_count = 1;
}
*region_count_ref = region_count;
return regions;
}
该函数提取了SMTP协议的requests,即由客户端发出的消息。
当一条消息包结束(遇到终止符)时创建一个新的region,并且重新定义消息的开始和结尾。
如果没有遇到终止符,则继续往后数,直到最后一个字节,创建新的region,同时退出while循环。
如果一直没有创建region,则说明缓存区的结构出问题了,为整个缓存都创建一个region。
unsigned int* extract_response_codes_smtp(unsigned char* buf, unsigned int buf_size, unsigned int* state_count_ref)
{
char *mem;
unsigned int byte_count = 0;
unsigned int mem_count = 0;
unsigned int mem_size = 1024;
unsigned int *state_sequence = NULL;
unsigned int state_count = 0;
char terminator[2] = {0x0D, 0x0A}; //终止符
mem=(char *)ck_alloc(mem_size);
//初始化状态机
state_count++;
state_sequence = (unsigned int *)ck_realloc(state_sequence, state_count * sizeof(unsigned int));
state_sequence[state_count - 1] = 0;
while (byte_count < buf_size) {
memcpy(&mem[mem_count], buf + byte_count++, 1);
if ((mem_count > 0) && (memcmp(&mem[mem_count - 1], terminator, 2) == 0)) {
//Extract the response code which is the first 3 bytes
char temp[4];
memcpy(temp, mem, 4);
temp[3] = 0x0;
unsigned int message_code = (unsigned int) atoi(temp);
if (message_code == 0) break;
state_count++;
state_sequence = (unsigned int *)ck_realloc(state_sequence, state_count * sizeof(unsigned int));
state_sequence[state_count - 1] = message_code;
mem_count = 0;
} else {
mem_count++;
if (mem_count == mem_size) {
//enlarge the mem buffer
mem_size = mem_size * 2;
mem=(char *)ck_realloc(mem, mem_size);
}
}
}
if (mem) ck_free(mem);
*state_count_ref = state_count;
return state_sequence;
}
该函数提取了SMTP协议的response_code,即由服务器端发出的消息。
根据对SMTP协议的分析,确定提取响应码的位置,根据响应码确定不同的服务器状态,并加入到状态机中。