A pit in sscanf

In a project, I accidentally used sscanf. I won’t explain its usage in detail here. Let’s start with the point and talk about the intention of this article. I also hope that other brothers of the same family will understand.

The program code:

struct xxx{
        unsigned char a[6];
        char    b[16];
};
int main(int argc, char** argv)
{
        struct xxx  xyz;
        memset(&xyz, 0, sizeof(struct xxx));
        strcpy(xyz.b, argv[1]);
        sscanf(argv[2], "%x:%x:%x:%x:%x:%x", &(xyz.a[0]), &(xyz.a[1]), &(xyz.a[2]), &(xyz.a[3]), &(xyz.a[4]), &(xyz.a[5]));
        printf("%s\n", xyz.b);
        return 0;
}

Execution: ./test enp2s0 12:23:34:45:45:56

Print result: xyz.b output is empty

gcc  -g  test.c  -o  test

Debugging results on GDB:

 

Explain, the first step is to initialize the xyz variable memory to 0;

The second step is to assign a value to xyz.b through strcpy, which is shown in hexadecimal in the figure;

In the third part, after executing sscanf, the numerical value of xyz.a is assigned to the element, but the first three bytes of xyz.b are set to 0.

Finally, the output of xyz.b is empty.

Reason explanation: We must abandon the metaphysics handed down from our ancestors, and the problems encountered in the development process must have reasons. The problem here obviously appeared after executing sscanf, so you can only look at the use of sscanf in depth.

sscanf type specifier:

Types of

Qualified input

Type of parameter

c

Single character: read the next character. If a width other than 1 is specified, the function will read width characters, pass them through parameters, and store them in consecutive positions in the array. No null character will be appended at the end.

char *

d

Decimal integer: The + or-sign in front of the number is optional.

int *

e, E, f, g, G

Floating point number: Contains a decimal point, an optional prefix + or -, an optional rear character e or E, and a decimal number. Two valid examples-732.103 and 7.12e4

float *

O

Octal integer.

int *

s

String. This will read consecutive characters until a space character is encountered (the space character can be whitespace, newline, and tab).

char *

u

Unsigned decimal integer.

unsigned int *

x,X

Hexadecimal integer.

int *

sscanf(argv[2], "%x:%x:%x:%x:%x:%x", &(xyz.a[0]), &(xyz.a[1]), &(xyz.a[2]), &(xyz.a[3]), &(xyz.a[4]), &(xyz.a[5]));

%X is used in the program, and the corresponding input parameter type should be int*, indicating that the original type of the parameter should be 4 bytes, and each element in the xyz.a array is a byte. When it is a memory copy, xyz.a[5 ] The actual copied hexadecimal representation is 0x00000067. The memory storage of the x86_64 platform is in little-endian byte order (high byte in high byte, low byte in low byte), and here GDB displays from low byte to high byte, from left to right, so here is the head of xyz Three bytes are covered by three 0x00.

Therefore, only the string mac address in the program can be converted into int, and the prototype of scanf input parameter does not support unsigned char. In fact, from this program alone, the assignment order of xyz.a and xyz.b can be exchanged, and it can also be realized. Let's take a look:

But remember, be careful to use this method to convert mac addresses in actual projects, otherwise you will definitely pay the price! !

 

 

 

Guess you like

Origin blog.csdn.net/qq_24436765/article/details/107361854