본문 바로가기
Study Log/정보 보안

[정보 보안] 8. 프로그래밍 기초

by LOGER 2023. 9. 23.

JSON 데이터

(Javascript Object Notation) -> 자바 스크립트의 객체 표현 방식 (key:value 선언/표기 방식)

- 원래 데이터 교환의 목적은 아니였음. 

- 사용하다보니 편해서 표준적인 방법이 됨

- Object와 Array로 구성 (단일값과 리스트)

 

JSONObject

{
  "name" : "LOGER",
  "age" : "Secret",
}

 

JSONArray

[
    {
    	"name":"LOGER",
        "age" : "Secret",
    },
    {
    	"name":"Hwanho",
        "age" : "Secret",
    }
]

 

 

아래 코드를 통해서 C언어에서 Json파일을 자유롭게 다룰 수 있다. 

include "json_c.c"를 하여 사용할 수 있다. 

 

#ifndef __JSONC_HEADER__
#define __JSONC_HEADER__

#ifdef __cplusplus
extern "C"{
#endif

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>
#include <stdarg.h>

#define JSON_MAX_INDEX 100
#define JSON_LAST_ARG_MAGIC_NUMBER -1027
#define JSON_STRBUFSIZE 256

typedef enum json_type_enum { JSON_UNDEFINED = 0x0, JSON_NUMBER = 0x1, JSON_STRING=0x2, JSON_BOOLEAN=0x4, JSON_ARRAY=0x8, JSON_OBJECT=0x10, JSON_NULL=0x20, JSON_INTEGER=0x40, JSON_DOUBLE=0x80 } json_type;
typedef enum json_keyorvalue_enum { JSON_KEY, JSON_VALUE } json_keyorvalue;
typedef struct json_small_stack_s{
	int top;
	int type[20];
	const void * stacktrace[20];
} json_small_stack;
typedef struct json_value_s {
    json_type type;
    void* value;
} json_value;
typedef struct json_object_s {
    int last_index;
    char* keys[JSON_MAX_INDEX];
    json_value values[JSON_MAX_INDEX];
} json_object;
typedef struct json_array_s {
    int last_index;
    json_value values[JSON_MAX_INDEX];
} json_array;

json_value json_string_to_value(const char** json_message);
json_value json_create(const char* json_message);
json_array * json_create_array(const char** json_message);
json_object * json_create_object(const char** json_message);

#define json_get_int(...) ((int)json_to_longlongint(json_get(__VA_ARGS__)))
#define json_get_longlongint(...) json_to_longlongint(json_get(__VA_ARGS__))
#define json_get_float(...) ((float)json_to_double(json_get(__VA_ARGS__)))
#define json_get_double(...) json_to_double(json_get(__VA_ARGS__))
#define json_get_bool(...) json_to_bool(json_get(__VA_ARGS__))
#define json_get_string(...) json_to_string(json_get(__VA_ARGS__))

#define json_to_int(v) ((int)json_to_longlongint(v))
long long int json_to_longlongint(json_value v);
#define json_to_float(v) ((float)json_to_float(v))
double json_to_double(json_value v);
bool json_to_bool(json_value v);
char * json_to_string(json_value v);
bool json_is_null(json_value v);
json_type json_get_type(json_value v);
const char * const json_type_to_string(int type);

//TODO read json file
json_value json_read(const char * const path);

#define json_get(...) (json_get_value(__VA_ARGS__, (void*)JSON_LAST_ARG_MAGIC_NUMBER))
json_value json_get_value(json_value v, ...);
json_value json_get_from_json_value(json_value v, const void* k);
json_value json_get_from_object(json_object* json, const char* key);
json_value json_get_from_array(json_array* json, const int index);
int json_len(json_value v);
int json_get_last_index(json_value v);

void json_fprint_value(FILE * outfp, const json_value v, int tab);
void json_fprint_array(FILE * outfp, const json_array* json, int tab);
void json_fprint_object(FILE * outfp, const json_object* json, int tab);
#define json_fprint(outfp, ...) (json_fprint_value(outfp, json_get(__VA_ARGS__), 0))
#define json_print(...) json_fprint(stdout, __VA_ARGS__)

json_small_stack json_stacktrace_get_stack(void);
void json_stacktrace_push(json_small_stack * jss, int type, const void * key);
void json_stacktrace_print(FILE * fp, const json_small_stack * const jss);

void json_free(json_value jsonv);
void json_free_array(json_array* jsona);
void json_free_object(json_object* jsono);

//int strcasecmp(const char* a, const char* b);
#ifdef __cplusplus
}
#endif
#endif

#ifndef __JSONC_BODY__
#define __JSONC_BODY__
#ifdef __cplusplus
extern "C"{
#endif
static const int MAX_INDEX = JSON_MAX_INDEX;
static const json_value undefined_json = {JSON_UNDEFINED, NULL};

json_value json_get_value(json_value v, ...) {
	void * key = NULL;
	void * vakey = NULL;
	va_list ap;
	va_start(ap, v);
	key = va_arg(ap, void *);
	if((int)key == JSON_LAST_ARG_MAGIC_NUMBER){ 
		return v;
		//fprintf(stderr, "json_get error : json_get needs two arguments at least and each of arguments must be a index(integer) or string(search key) except the first argument\n");
		//return undefined_json;
	}
	if( ! (v.type == JSON_ARRAY || v.type == JSON_OBJECT)){
		fprintf(stderr, "json_get error : the first argument of json_get should be an array or an object (type : %s)\n", json_type_to_string(v.type));
		return undefined_json;
	}
	json_small_stack jss = json_stacktrace_get_stack();

	if(v.type == JSON_OBJECT) {
		if((int)key>=0 && (int)key <= ((json_object *)(v.value))->last_index) json_stacktrace_push(&jss, v.type, ((json_object *)(v.value))->keys[(int)key]);
		else json_stacktrace_push(&jss, v.type, key);
	}
	else json_stacktrace_push(&jss, v.type, key);

	json_value ret = json_get_from_json_value(v, key);
	if(ret.type == JSON_UNDEFINED){
		fprintf(stderr, "error tracing : ");
		json_stacktrace_print(stderr, &jss);
		fprintf(stderr, "\n");
		return ret;
	}

	while(1){
		vakey = va_arg(ap, void *);
		if((int)vakey == JSON_LAST_ARG_MAGIC_NUMBER) break; 

		if(ret.type == JSON_OBJECT) {
			if((int)key>=0 && (int)vakey <= ((json_object *)(ret.value))->last_index) json_stacktrace_push(&jss, ret.type, ((json_object *)(ret.value))->keys[(int)vakey]);
			else json_stacktrace_push(&jss, ret.type, vakey);
		}
		else json_stacktrace_push(&jss, ret.type, vakey);

		ret = json_get_from_json_value(ret, vakey);

		if(ret.type == JSON_UNDEFINED){
			fprintf(stderr, "error tracing : ");
			json_stacktrace_print(stderr, &jss);
			fprintf(stderr, "\n");
			return ret;
		}
	}
    return ret;
}
json_value json_get_from_json_value(json_value v, const void* key) {
    if (v.type == JSON_OBJECT) return json_get_from_object((json_object *)(v.value), (char *)key);
    if (v.type == JSON_ARRAY) return json_get_from_array((json_array *)(v.value), (int)key);
	fprintf(stderr, "json_get_from_json_value error : cannot get a json value with key from json_value that is not an object nor an array(value type : %s)\n", json_type_to_string(v.type));
    return undefined_json;
}
json_value json_get_from_object(json_object* json, const char* key) {
	//when the key is assummed as an index
	if((int)key >=0 && (int)key <= json->last_index)
		return json->values[(int)key];
	if((int)key <= MAX_INDEX && (int)key>= 0){
		fprintf(stderr, "json_get_from_object error : out of index\n");
		return undefined_json;
	}
		
	//when the key is assummed as a string
    if (json == NULL || key == NULL || *key == '\0') return undefined_json;
    for (int i = 0; i <= json->last_index; i++) {
        if (strcmp(json->keys[i], key) == 0) {
            return json->values[i];
        }
    }
	fprintf(stderr, "json_get_from_object error : no value corresponding to the key(%s)\n", key);
    return undefined_json;
}
json_value json_get_from_array(json_array* json, const int index) {
    if (json == NULL || index < 0 || json->last_index < index){
		fprintf(stderr, "json_get_from_array error : out of index\n");
		return undefined_json;
	}
    return json->values[index];
}
int json_len(json_value v){
	return json_get_last_index(v)+1;
}
int json_get_last_index(json_value v){
	if(v.type == JSON_OBJECT) return ((json_object *)(v.value))->last_index;
	if(v.type == JSON_ARRAY) return ((json_array *)(v.value))->last_index;

	fprintf(stderr, "json_get_last_index : the type of json_value is not a JSON_ARRAY nor a JSON_OBJECT");
	return -1;
}

json_value json_string_to_value(const char** json_message) {
    char c;
    char temp[64] = "";
    json_value jsonv;
    jsonv.type = JSON_UNDEFINED;
    jsonv.value = NULL;

    while (c = *((*json_message)++)) {
        switch (c) {
        //in : {something}
        //   : c   		//c and *json_message are same position
        //out: c          p
        //return : JSON OBJECT {something}
        case '{':
            (*json_message)--;
            jsonv.type = JSON_OBJECT;
            jsonv.value = json_create_object(json_message);
            return jsonv;
        case '}':
            printf("parse error : unexpected token '}'\n");
            return jsonv;
        //in : [something]
        //   : cp    //c==c, p= *json_message는 동일한 위치
        //out: c          p
        //return : JSON JSON_ARRAY [something]
        case '[':
            (*json_message)--;
            jsonv.type = JSON_ARRAY;
            jsonv.value = json_create_array(json_message);
            return jsonv;
        case ']':
            printf("parse error : unexpected token ']'\n");
            return jsonv;
        //in : "test"
        //   : cp     //c ==c, p == *json_message 현재 위치
        //out: c     p
        //return: char[5] 'test\0'
        case '\"':
        {
            jsonv.type = JSON_STRING;
			char* str = (char*)malloc(sizeof(char) * JSON_STRBUFSIZE);
			int size = 0;
			if (str == NULL) {
				printf("string malloc error;\n");
				return jsonv;
			}

			//TODO : string process
			while (true) {
				char ch = *((*json_message)++);
				switch(ch){
					case '\\':
					{
						char escape = *((*json_message)++);
						switch(escape){
							case '\"': str[size] = '\"'; break;
							case '\\': str[size] = '\\'; break;
							case '/': str[size] = '/'; break;
							case 'b': str[size] = '\b'; break;
							case 'f': str[size] = '\f'; break;
							case 'n': str[size] = '\n'; break;
							case 'r': str[size] = '\r'; break;
							case 't': str[size] = '\t'; break;
							//Parsing unicodes are not implemented
							case 'u':{
								str[size++] = '\\';
								str[size] = 'u';
								break;
							}
							/*
							//TODO : Implement parsing unicodes;
							case 'u':{
								char chs[4] = {0,};
								for(int i=0; i<4; i++){
									chs[i] = tolower(*((*json_message)++));
									printf("chs[%d] : %c(ascii:%d)\n", i, chs[i], chs[i]);
									if(chs[i] >= 'a' && chs[i] <= 'f') chs[i] = chs[i] - 'a';
									else if(chs[i] >= '0' && chs[i] <= '9') chs[i] = chs[i] - '0';
									else{
										fprintf(stderr, "json_string_to_value error: parse errer at escape string '\\u'\n");
										break;
									}
								}
								str[size++] = chs[0]*16+chs[1];
								str[size] = chs[2]*16+chs[3]; //size will be increased at the end of while
								break;
							}
							*/
							default:
								fprintf(stderr, "json_string_to_value error: parse errer at escape string '\\%c'\n", escape);
						}
						break;
					}
					case '\"':
						str[size] = '\0';
						goto JSON_STRBREAK;
					default:
						str[size] = ch;
				}
				size++;
				if((size+1) % JSON_STRBUFSIZE == 0){
					str = (char *)realloc(str, sizeof(char) * JSON_STRBUFSIZE * (1 + (size+1)/JSON_STRBUFSIZE));
					printf("realloced : %s\n", str);
					if(str == NULL){
						printf("string malloc error;\n");
						return jsonv;
					}
				}
			}
JSON_STRBREAK:
			jsonv.value = str;
            return jsonv;
        }
        default:
        {
			//printf("c : %c\n", c);
            //in : null | false | true
            //   : cp  //c==c, p = *json_message 현재 위치
            //out: c   p
            //return : null | false | true
            if (isalpha(c)) {
                const char* startptr = (*json_message) - 1;
                while (isalpha(*((*json_message)++)));
                (*json_message)--;
                int size = (*json_message - startptr);
                memcpy(temp, startptr, sizeof(char) * size);
                temp[size] = '\0';
                if (strcasecmp(temp, "null") == 0) {
                    jsonv.type = JSON_NULL;
                    return jsonv;
                }
                if (strcasecmp(temp, "false") == 0 || strcasecmp(temp, "true") == 0) {
                    jsonv.type = JSON_BOOLEAN;
                    jsonv.value = malloc(sizeof(bool));
                    if (strcasecmp(temp, "false") == 0) *((bool *)jsonv.value) = false;
                    else *((bool *)jsonv.value) = true;
                    return jsonv;
                }
                printf("BOOLEAN or NULL error\n");
                return jsonv;
            }
            //in : number
            //return : number(integer or double)
            if (isdigit(c) || c == '-' || c == '+' || c == '.') {
                const char* startptr = (*json_message) - 1;
                while (true) {
                    char ch = *((*json_message)++);
                    if ((isdigit(ch) || ch == '.' || ch=='e' || ch=='E' || ch=='+' || ch == '-') == false)
                        break;
                }
                (*json_message)--;
                int size = (*json_message - startptr);
                memcpy(temp, startptr, sizeof(char) * size);
                temp[size] = '\0';
				
				if(strchr(temp, '.') || strchr(temp, 'e') || strchr(temp, 'E')){
					jsonv.type = (json_type) (JSON_NUMBER|JSON_DOUBLE);
					jsonv.value = malloc(sizeof(double));
					if (jsonv.value == NULL) {
						printf("malloc error!\n");
						return jsonv;
					}
					*((double*)jsonv.value) = atof(temp);
					//printf("temp : %s\n type:%x read : double %f\n", temp, jsonv.type,  *((double *)jsonv.value));
				} else{
					jsonv.type = (json_type) (JSON_NUMBER|JSON_INTEGER);
					jsonv.value = malloc(sizeof(long long int));
					if (jsonv.value == NULL) {
						printf("malloc error!\n");
						return jsonv;
					}
					*((long long int*)jsonv.value) = atoll(temp);
					//printf("temp : %s\n type:%x read : integer %lld\n", temp, jsonv.type,  *((long long int *)jsonv.value));
				}
                return jsonv;
            }
        }
        }
    }
	fprintf(stderr, "json_string_to_value error: json parser meets NULL");
	return jsonv;
}

json_value json_create(const char* json_message) {
    return json_string_to_value(&json_message);
}
json_array* json_create_array(const char** json_message) {
    json_array* jsona = (json_array *)malloc(sizeof(json_array));
    memset(jsona, 0x00, sizeof(json_array));
    jsona->last_index = 0;
    char c;
    int stack = 0;
    while (c = *((*json_message)++)) {
        switch (c) {
        case '[':
            if (stack == 0) stack++;
            else {
                (*json_message)--;
                jsona->values[jsona->last_index] = json_string_to_value(json_message);
                jsona->last_index++;
            }
            break;
        case ']':
            jsona->last_index--;
            return jsona;
        default:
            if (isalpha(c) || isdigit(c) || c == '[' || c == '{' || c == '\"' || c == '-' || c=='+' || c=='.') {
                (*json_message)--; 
                jsona->values[jsona->last_index] = json_string_to_value(json_message);
                jsona->last_index++;
            }
        }
    }
	fprintf(stderr, "json_create_array error: json parser meets NULL");
	return jsona;
}
json_object* json_create_object(const char** json_message) {
    json_object* jsono = (json_object*)malloc(sizeof(json_object));
    memset(jsono, 0x00, sizeof(json_object));
    jsono->last_index = 0;
    int stack = 0;
    int keyorvalue = JSON_KEY;
    char c;
    while (c = *((*json_message)++)) {
        switch (c) {
        case '{':
            if (stack == 0) stack++;
            else {
                if (keyorvalue == JSON_KEY) {
                    printf("key cannot be an Object\n");
                    return jsono;
                }
                (*json_message)--;
                jsono->values[jsono->last_index] = json_string_to_value(json_message);
                jsono->last_index++;
                keyorvalue = JSON_KEY;
            }
            break;
        case '}':
            jsono->last_index--;
            return jsono;
        default:
            if (isalpha(c) || isdigit(c) || c == '[' || c == '{' || c == '\"' || c == '-' || c=='+' || c=='.') {
                (*json_message)--;
                if (keyorvalue == JSON_KEY) {
                    json_value v = json_string_to_value(json_message);
                    if (v.type != JSON_STRING) {
                        printf("Key MUST be a string");
                        return jsono;
                    }
                    jsono->keys[jsono->last_index] = (char *)(v.value);
                    keyorvalue = JSON_VALUE;
                }
                else {
                    jsono->values[jsono->last_index] = json_string_to_value(json_message);
                    jsono->last_index++;
                    keyorvalue = JSON_KEY;
                }
            }
        }
    }
	fprintf(stderr, "json_create_object error: json parser meets NULL");
	return jsono;
}

long long int json_to_longlongint(json_value v){
	if( ! (v.type & JSON_NUMBER)){
		fprintf(stderr, "json_to_longlongint error: the type of the json_value is not the type of JSON_NUMBER");
		return 0;
	}
	if( v.type & JSON_INTEGER ) return *((long long int *)(v.value));
	if( v.type & JSON_DOUBLE ) return (long long int)*((double *)(v.value));
	fprintf(stderr, "json_to_longlongint error: unknown numeric type");
	return 0;
}
double json_to_double(json_value v){
	if( ! (v.type & JSON_NUMBER)){
		fprintf(stderr, "json_to_double error: the type of the json_value is not the type of JSON_NUMBER");
		return 0;
	}
	if( v.type & JSON_INTEGER ) return (double)*((long long int *)(v.value));
	if( v.type & JSON_DOUBLE ) return *((double *)(v.value));
	fprintf(stderr, "json_to_double error: unknown numeric type");
	return 0;
}
bool json_to_bool(json_value v){
	if( ! (v.type & JSON_BOOLEAN) ){
		fprintf(stderr, "json_to_bool error: the type of the json_value is not the type of JSON_BOOLEAN");
		return false;
	}
	return *((bool *)(v.value));
}
char * json_to_string(json_value v){
	if( ! (v.type & JSON_STRING) ){
		fprintf(stderr, "json_to_string error: the type of the json_value is not the type of JSON_STRING");
		return NULL;
	}
	return (char *)(v.value);
}
bool json_is_null(json_value v){
	return v.type == JSON_NULL;
}
json_type json_get_type(json_value v){
	return v.type;
}

const char * const json_type_to_string(int type){
	switch(type){
		case JSON_UNDEFINED: return "undefined";
		case JSON_NUMBER: return "number";
		case JSON_NUMBER|JSON_INTEGER: return "number(integer)";
		case JSON_NUMBER|JSON_DOUBLE: return "number(double)";
		case JSON_STRING: return "string";
		case JSON_BOOLEAN: return "boolean";
		case JSON_ARRAY: return "array";
		case JSON_OBJECT: return "object";
		case JSON_NULL: return "null";
		default: return "undefined";
	}
}

void json_fprint_value(FILE * outfp, const json_value v, int tab) {
    if (v.type == JSON_UNDEFINED) fprintf(outfp, "undefined");
    if (v.type == (JSON_NUMBER|JSON_INTEGER)) fprintf(outfp, "%lld", *((long long int *)(v.value)));
    if (v.type == (JSON_NUMBER|JSON_DOUBLE)) fprintf(outfp, "%f", *((double *)(v.value)));
    if (v.type == JSON_ARRAY) json_fprint_array(outfp, (json_array *)(v.value), tab);
    if (v.type == JSON_STRING) fprintf(outfp, "\"%s\"", ((char *)(v.value)));
    if (v.type == JSON_BOOLEAN) fprintf(outfp, *((bool *)(v.value))?"true":"false");
    if (v.type == JSON_OBJECT) {json_fprint_object(outfp, ((json_object*)(v.value)), tab); }
    if (v.type == JSON_NULL) fprintf(outfp, "null");
}
void json_fprint_array(FILE * outfp, const json_array* json, int tab) {
    fprintf(outfp, "[");
    for (int i = 0; i <= json->last_index; i++) {
        json_fprint_value(outfp, json->values[i], tab);
        if(json->last_index != i)
            fprintf(outfp, ", ");
    }
    fprintf(outfp, "]");
}
void json_fprint_object(FILE * outfp, const json_object* json, int tab) {
    fprintf(outfp, "{\n");
    tab++;
    for (int i = 0; i <= json->last_index; i++) {
        for (int i = 0; i < tab; i++) fprintf(outfp, "\t");
        fprintf(outfp, "\"%s\": ", json->keys[i]);
        json_fprint_value(outfp, json->values[i], tab);
        if(json->last_index != i)
            fprintf(outfp, ",\n");
    }
    fprintf(outfp, "\n");
    tab--;
    for (int i = 0; i < tab; i++) fprintf(outfp, "\t");
    fprintf(outfp, "}");
}
/*
void json_fprint(FILE * outfp, const json_value json) {
    json_fprint_value(outfp, json, 0);
	fprintf(outfp, "\n");
}
*/

json_small_stack json_stacktrace_get_stack(void){
	json_small_stack jss = {-1, {JSON_UNDEFINED, }, {NULL, }};
	return jss;
}
void json_stacktrace_push(json_small_stack * jss, int type, const void * key){
	if(jss->top > 19){
		//the stack depth is 20. stop to record a stack trace
		return;
	}
	jss->top++;
	jss->type[jss->top] = type;
	jss->stacktrace[jss->top] = key;
	//printf("push : top:%d, type:%s, key:%d\n", jss->top, json_type_to_string(type), (int)key);
}
void json_stacktrace_print(FILE * fp, const json_small_stack * const jss){
	if(jss->top<0) return;

	fprintf(fp, "(%s)", json_type_to_string(jss->type[0]));

	for(int i=1; i<=jss->top; i++){
		if(jss->type[i-1] == JSON_ARRAY) fprintf(fp, "(%s)[%d]", json_type_to_string(jss->type[i]), (int)jss->stacktrace[i-1]);
		else if(jss->type[i-1] == JSON_OBJECT){ 
			if((int)jss->stacktrace[i-1] <= MAX_INDEX && (int)jss->stacktrace[i-1] >= 0) fprintf(fp, "(%s)[%d]", json_type_to_string(jss->type[i]), (int)jss->stacktrace[i-1]);
			else fprintf(fp, "->(%s)%s", json_type_to_string(jss->type[i]), jss->stacktrace[i-1]);
		}
		else fprintf(fp, "->(%s)", json_type_to_string(jss->type[i]));
	}

	if(jss->type[jss->top] == JSON_ARRAY) fprintf(fp, "(%s)[%d]", json_type_to_string(JSON_UNDEFINED), (int)jss->stacktrace[jss->top]);
	else if(jss->type[jss->top] == JSON_OBJECT){ 
		if((int)jss->stacktrace[jss->top] <= MAX_INDEX && (int)jss->stacktrace[jss->top] >= 0) fprintf(fp, "(%s)[%d]", json_type_to_string(JSON_UNDEFINED), (int)jss->stacktrace[jss->top]);
		else fprintf(fp, "->(%s)%s", json_type_to_string(JSON_UNDEFINED), jss->stacktrace[jss->top]);
	}
	else fprintf(fp, "->(%s)", json_type_to_string(JSON_UNDEFINED));
	
}

void json_free(json_value jsonv) {
    int t = jsonv.type;
	if (t == JSON_NUMBER || t == JSON_STRING || t == JSON_BOOLEAN) {
		free(jsonv.value);
    }
    else if (t == JSON_ARRAY) {
        json_free_array((json_array *)(jsonv.value));
    }
    else if (t == JSON_OBJECT) {
        json_free_object((json_object *)(jsonv.value));
    }
    else {
        //JSON_UNDEFINED or JSON_NULL. It don't have to call free.
    }
}
void json_free_array(json_array* jsona) {
    if (jsona == NULL) return;
    for (int i = 0; i <= jsona->last_index; i++)
        json_free(jsona->values[i]);
    free(jsona);
}
void json_free_object(json_object* jsono) {
    if (jsono == NULL) return;
    for (int i = 0; i <= jsono->last_index; i++) {
        free(jsono->keys[i]);
        json_free(jsono->values[i]);
    }
    free(jsono);
}

/*
int strcasecmp(const char* a, const char* b) {
    while (true) {
        if (*a == '\0' || *b == '\0') {
            if (*a == *b) return 0;
            else return -1;
        }
        if (tolower(*a) != tolower(*b))
            return -1;
        a++;
        b++;
    }
}
*/
#ifdef __cplusplus
}
#endif
#endif

 

파일 입출력

1. 파일열기 (open) -> fopen(filename, mode)

* mode 

r: reading

w: writing

a: append

 

2. 작성 (write)

3. 파일 닫기 (close) -> fclose(파일 변수)

 

 

Git과 github

git: 레포지토리 형상관리 도구

github: 온라인 상에서 레포지토리 관리

commit: 레포지토리 변경이력 (변경 이력 -> 성능 향상 혹은 취약점 수정일 수도 있음)

 

커밋 로그를 다음과 같이 검색하여 취약점 코드를 찾아보며 공부할 수 있음

git log --pretty=format:"%h - %an, %ar : %s“ | grep “vulnerability”

 

이러한 정보들을 이용하여 버그가 발생/수정 된 코드를 데이터베이스화 하여서 AI를 이용할 수 있음

 

정적 vs 동적 분석

코드를 실행하여 찾는 것(동적)과 코드를 실행하지 않고 찾는것(정적)의 차이점?

-> 동적분석은 요청/응답에 따른 결과를 알 수 있다

-> 정적분석은 소스의 라인별 결과를 알 수 있다. 

 

정적분석

도구: sonarQube

https://next.sonarqube.com/sonarqube/projects

 

SonarQube

 

next.sonarqube.com

우리가 만든 코드도 프로젝트를 만들고 sonarQube에 정적 분석을 실행하면 취약점을 방지 할 수 있음

 

AST 분석

(Abstract Syntax Tree) 추상 구문 트리 

소스 코드가 소나큐브에 올라가면 AST처럼 관계도가 그려짐

- 그 중에서 버그/에러라고 판단되는 패턴들을 찾아내서 버그라고 인지함

 

아래 파이썬 파일을 통해서 C코드를 AST로 만들어낼 수 있음 (단, #include, 주석등은 지운 상태로 실행해야함)

- AST가 Json 형태로 나옴

 

import json
import sys
import re

# This is not required if you've installed pycparser into
# your site-packages/ with setup.py
#
sys.path.extend(['.', '..'])

from pycparser import parse_file, c_ast
from pycparser.plyparser import Coord


RE_CHILD_ARRAY = re.compile(r'(.*)\[(.*)\]')
RE_INTERNAL_ATTR = re.compile('__.*__')


class CJsonError(Exception):
    pass


def memodict(fn):
    """ Fast memoization decorator for a function taking a single argument """
    class memodict(dict):
        def __missing__(self, key):
            ret = self[key] = fn(key)
            return ret
    return memodict().__getitem__


@memodict
def child_attrs_of(klass):
    """
    Given a Node class, get a set of child attrs.
    Memoized to avoid highly repetitive string manipulation

    """
    non_child_attrs = set(klass.attr_names)
    all_attrs = set([i for i in klass.__slots__ if not RE_INTERNAL_ATTR.match(i)])
    return all_attrs - non_child_attrs


def to_dict(node):
    """ Recursively convert an ast into dict representation. """
    klass = node.__class__

    result = {}

    # Metadata
    result['_nodetype'] = klass.__name__

    # Local node attributes
    for attr in klass.attr_names:
        result[attr] = getattr(node, attr)

    # Coord object
    if node.coord:
        result['coord'] = str(node.coord)
    else:
        result['coord'] = None

    # Child attributes
    for child_name, child in node.children():
        # Child strings are either simple (e.g. 'value') or arrays (e.g. 'block_items[1]')
        match = RE_CHILD_ARRAY.match(child_name)
        if match:
            array_name, array_index = match.groups()
            array_index = int(array_index)
            # arrays come in order, so we verify and append.
            result[array_name] = result.get(array_name, [])
            if array_index != len(result[array_name]):
                raise CJsonError('Internal ast error. Array {} out of order. '
                    'Expected index {}, got {}'.format(
                    array_name, len(result[array_name]), array_index))
            result[array_name].append(to_dict(child))
        else:
            result[child_name] = to_dict(child)

    # Any child attributes that were missing need "None" values in the json.
    for child_attr in child_attrs_of(klass):
        if child_attr not in result:
            result[child_attr] = None

    return result


def to_json(node, **kwargs):
    """ Convert ast node to json string """
    return json.dumps(to_dict(node), **kwargs)


def file_to_dict(filename):
    """ Load C file into dict representation of ast """
    ast = parse_file(filename, use_cpp=True)
    return to_dict(ast)


def file_to_json(filename, **kwargs):
    """ Load C file into json string representation of ast """
    ast = parse_file(filename, use_cpp=True)
    return to_json(ast, **kwargs)


def _parse_coord(coord_str):
    """ Parse coord string (file:line[:column]) into Coord object. """
    if coord_str is None:
        return None

    vals = coord_str.split(':')
    vals.extend([None] * 3)
    filename, line, column = vals[:3]
    return Coord(filename, line, column)


def _convert_to_obj(value):
    """
    Convert an object in the dict representation into an object.
    Note: Mutually recursive with from_dict.

    """
    value_type = type(value)
    if value_type == dict:
        return from_dict(value)
    elif value_type == list:
        return [_convert_to_obj(item) for item in value]
    else:
        # String
        return value


def from_dict(node_dict):
    """ Recursively build an ast from dict representation """
    class_name = node_dict.pop('_nodetype')

    klass = getattr(c_ast, class_name)

    # Create a new dict containing the key-value pairs which we can pass
    # to node constructors.
    objs = {}
    for key, value in node_dict.items():
        if key == 'coord':
            objs[key] = _parse_coord(value)
        else:
            objs[key] = _convert_to_obj(value)

    # Use keyword parameters, which works thanks to beautifully consistent
    # ast Node initializers.
    return klass(**objs)


def from_json(ast_json):
    """ Build an ast from json string representation """
    return from_dict(json.loads(ast_json))


#------------------------------------------------------------------------------
if __name__ == "__main__":
    if len(sys.argv) > 1:
        # Some test code...
        # Do trip from C -> ast -> dict -> ast -> json, then print.
        ast_dict = file_to_dict(sys.argv[1])
        ast = from_dict(ast_dict)
        print(to_json(ast, sort_keys=True, indent=4))
    else:
        print("Please provide a filename as argument")

 

다음글에서는 위에서 사용한 JSON 분석 파일인 json_c.c와 AST를 만들어주는 파이썬 파일을 결합하여서 연습을 진행해보겠다.