Передача специального символа в качестве аргумента - PullRequest
3 голосов
/ 10 июня 2019

Мне нужно передать строку с шестнадцатеричным значением 00 2C 00 21 моей программе в качестве аргумента командной строки, которую я не могу сделать.

#include<stdio.h>
int main(int argc,char* argv[]){

// argv[1] should have the string that the above hex represents 

//... the program will use that string inside the program

//...also please explain what should i do if i (am/am not) allowed to modify the source  

}

Так как 00 является нулевым символом, я не могу представить его в командной строке и передать в программу. Также мне нужно передать строку, состоящую из различных других символов, шестнадцатеричные значения которых равны 01 или 02 (например, например), которые вы не можете ввести непосредственно с клавиатуры и передать в качестве аргумента.

Что я должен сделать, чтобы моя программа получила строку с шестнадцатеричным представлением 00 2C 00 21.

$./a.out " what should i write here?  " 

Ответы [ 2 ]

4 голосов
/ 10 июня 2019

Вы должны заставить вашу программу принимать строку с escape-символами в ней и анализировать их самостоятельно. Так что это будет вызываться так:

$ ./myprogram '\x00\x2c\x00\x21'
Например,

(\x соответствует тому, что использует сам C, поэтому может быть знакомо пользователям). Одиночные кавычки предназначены для защиты обратных слешей от оболочки, не на 100% уверенных и не в должной форме.

Результатом не будет строка, поскольку строки в C не могут содержать 0 символов.

Вот пример того, как это может выглядеть:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static size_t decode(void *buf, size_t buf_max, const char *s)
{
    unsigned char *put = buf;
    unsigned char * const put_max = put + buf_max;
    while (*s != '\0' && put != put_max)
    {
        if (*s == '\\')
        {
            ++s;
            if (*s == '\\')
                *put++ = *s++;
            else if (*s == 'x')
            {
                ++s;
                char *endp;
                const unsigned long v = strtoul(s, &endp, 16);
                if (endp == s)
                    break;
                *put++ = (unsigned char) v;
                s = endp;
            }
            else
                break;
        }
        else
            *put++ = *s++;
    }
    return put - (unsigned char *) buf;
}

int main(int argc, char *argv[])
{
    unsigned char buf[32];
    const size_t len = decode(buf, sizeof buf, "\\x0hello\\x1\\xaa\\xfe\\xed");
    for (size_t i = 0; i < len; ++i)
    {
        printf("%x\n", buf[i]);
    }
    return 0;
}

Обратите внимание, что тестовый "драйвер" в main() будет заменен в вашем случае, вы хотите пройти, например. argv[1] до decode(). Двойная обратная косая черта защищает от компилятора C, мы действительно хотим получить строку, содержащую escape-символы обратной косой черты.

2 голосов
/ 10 июня 2019

Невозможно передать нулевой байт аргументу программы, используя bash или любую другую оболочку.Это просто потому, что это невозможно в стандарте C.

Стандарт C гласит: C11 5.1.2.2.1p2 (выделено мной):

..параметры главной функции должны соответствовать следующим ограничениям:
- ...
- Если значение argc больше нуля, элементы массива от argv [0] до argv [argc-1] включительно должнысодержат указатели на строки , которые задаются значениями, определяемыми реализацией средой хоста до запуска программы....
- ...

"Строка" - это C11 7.1.1p1 (выделено мной):

Строкаявляется непрерывной последовательностью символов , оканчивающейся на и включающей в себя первый нулевой символ.... Длина строки - это число байтов, предшествующих нулевому символу, а значение строки - это последовательность значений содержащихся символов в порядке.

«Нулевой символ»"является byte with all bits set to 0 C11 5.2.1p2 .Это ноль.На первом «нулевом символе» строка заканчивается.Если массив символов содержит ноль байтов, он не может быть строкой (хех, в точном смысле см. note 78 , строковый литерал не может быть строкой, потому что он может содержатьнулевые символы).Вы не можете передавать несколько 0x00 значений, встроенных в аргументы, в программу на Си, поскольку это не будет передаваемой вами "строкой".

Правильный способ написать собственный синтаксический анализатор,будет принимать "строки" (т.е. ./a.out "00 2C 00 21") и конвертировать в ноль байтов самостоятельно.

Для вашего случая использования, если это просто, я мог бы представить более простой анализатор, чем в другом ответе.Вы могли бы бывшийпередайте аргумент со всеми байтами, увеличенными на 1, затем уменьшите на 1 в вашей программе.

Или вы можете передать специальное байтовое значение, например, ex.0xff (если ваша реализация, операционная система и среда поддерживают передачу 0xff байтов) вместо 0x00 и замените их в вашей программе.Эта опция представлена ​​ниже:

#include <string.h>
#include <stddef.h>
#include <assert.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
   assert(argc >= 2);
   for (size_t i = 0, max = strlen(argv[1]); i < max; ++i) {
        // replace all 0xff by 0x00
        if ( (0xff & argv[1][i]) == 0xff) {
           argv[1][i] = 0x00;
        }
   }
   // use argv[1]
   for (size_t i = 0, max = 4; i < max; ++i) { 
       printf("argv[1][%d] = 0x%02x\n", i, 0xff & argv[1][i]);
   } 
}

и вызов с:

./a.out $'\xff\x2c\xff\x2c'

Проверено на repl.it .

$'...'bash интерпретируется как ANSI-C Цитирование .\xff интерпретируются как шестнадцатеричные константы, поэтому первый аргумент будет равен (char[]){0xff, 0x2c, 0xff, 0x2c, 0x00}.После замены 0xff на 0x00 оно станет (char[]){0x00, 0x2c, 0x00, 0x2c, 0x00}, и вы сможете использовать первые 4 байта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...