programing

단일 인수(변환 지정자 없음)를 사용하는 printf가 권장되지 않는 이유는 무엇입니까?

goodsources 2022. 7. 10. 21:25
반응형

단일 인수(변환 지정자 없음)를 사용하는 printf가 권장되지 않는 이유는 무엇입니까?

읽고 있는 있어요.printf단일 인수(변환 지정자 없음)는 권장되지 않습니다.할 것을 합니다.

printf("Hello World!");

와 함께

puts("Hello World!");

또는

printf("%s", "Hello World!");

그런지 요?printf("Hello World!");★★★★★★★★★★★★★★★★★?이 책에는 취약성이 포함되어 있다고 쓰여 있습니다.러한한 ?? ?? ?? ??? ??

printf("Hello World!");IMHO를 사용하다

const char *str;
...
printf(str);

ifstr 때 ''가 들어 있는 문자열을 됩니다.%s specificators,되지 않은 동작, 「크래시」, 「크래시」는 정의되지 않은 동작을 나타냅니다.puts(str)이 문자열은 그대로 표시됩니다.

예:

printf("%s");   //undefined behaviour (mostly crash)
puts("%s");     // displays "%s\n"

printf("Hello world");

문제가 없고 보안 취약성도 없습니다.

문제는 다음과 같습니다.

printf(p);

서 ''는p는 사용자에 의해 제어되는 입력에 대한 포인터입니다.포맷 스트링 공격의 경향이 있습니다.사용자는 변환 사양을 삽입하여 프로그램을 제어할 수 있습니다.예를 들어 다음과 같습니다.%x%n메모리를 덮어씁니다.

:puts("Hello world")동작에 있어서 와 동등하지 않다printf("Hello world"), 하하에게는printf("Hello world\n")을 「Call」로 할 수 puts.

의 경우 gcc를 확인하는 경고를 할 수 .printf() ★★★★★★★★★★★★★★★★★」scanf()

gcc 매뉴얼은 다음과 같습니다.

-Wformat되어 있습니다.-Wall체크의 하려면 , 의 옵션을 사용해 주세요-Wformat-y2k,-Wno-format-extra-args,-Wno-format-zero-length,-Wformat-nonliteral,-Wformat-security , , , , 입니다.-Wformat=2는 사용할 수 에 되어 있지 않습니다.-Wall.

-Wformat에서는 유효하게 되어 있습니다.-Wall옵션에서는 다음 경우를 찾는 데 도움이 되는 몇 가지 특별한 경고가 활성화되지 않습니다.

  • -Wformat-nonliteral형식 지정자로 문자열 리터럴을 전달하지 않으면 경고가 표시됩니다.
  • -Wformat-security위험한 구문을 포함할 수 있는 문자열을 전달하면 경고가 표시됩니다.의의입니다 of of of of of of -Wformat-nonliteral.

★★★★★★★★★★★★★★를 유효하게 하는 것은 합니다.-Wformat-security코드베이스에 있는 몇 가지 버그(오류 처리 모듈, 에러 처리 모듈, xml 출력 모듈)가 밝혀졌습니다.모두 파라미터에 %문자를 사용하여 호출된 경우 정의되지 않은 작업을 수행할 수 있는 함수가 있습니다.참고로, 우리의 코드베이스는 약 20년이 되었습니다.이러한 문제를 알고 있었다고 해도, 이러한 경고를 유효하게 했을 때에, 코드베이스에 아직 이러한 버그가 몇 개나 존재하고 있는지는 매우 놀랐습니다).

취약성 부분에 대한 정보를 여기에 추가합니다.

printf 문자열 형식의 취약성으로 인해 취약하다고 합니다.이 예에서는 스트링이 하드코딩되어 있으면 무해합니다(이러한 스트링을 완전히 하드코딩하는 것은 권장되지 않습니다).그러나 매개 변수의 유형을 지정하는 것은 좋은 습관입니다.다음 예를 들어보겠습니다.

printf에 일반 문자열이 아닌 형식 문자열을 입력하면(예를 들어 프로그램 stdin을 인쇄하고 싶은 경우), printf는 스택에서 가능한 모든 문자열을 가져옵니다.

예를 들어 숨겨진 정보에 액세스하거나 인증을 우회하는 스택 탐색에 프로그램을 이용하는 데 매우 많이 사용되었고 지금도 여전히 사용되고 있습니다.

예(C):

int main(int argc, char *argv[])
{
    printf(argv[argc - 1]); // takes the first argument if it exists
}

"%08x %08x %08x %08x %08x\n"

printf ("%08x %08x %08x %08x %08x\n"); 

그러면 printf 함수는 스택에서5개의 파라미터를 가져와 8자리 패딩된 16진수로 표시하도록 지시합니다.따라서 출력은 다음과 같습니다.

40012980 080628c4 bffff7a4 00000005 08059c04

자세한 설명 및 기타 예는 여기를 참조하십시오.

다른 답변에 더해서printf("Hello world! I am 50% happy today")쉽게 만들 수 있는 버그로 모든 종류의 심각한 메모리 문제를 일으킬 수 있습니다(UB!).

프로그래머가 문자 그대로의 문자열을 필요로 할 때, 그 이외의 것을 명확하게 해 줄 것을 요구하는 것은, 간단하고, 보다 간단하고, 보다 견고합니다.

그리고 그게 바로printf("%s", "Hello world! I am 50% happy today")당신을 이해해요.그건 완전히 바보같다.

)printf("He has %d cherries\n", ncherries)전혀 같은 것이 아닙니다.이 경우 프로그래머는 '버추얼 스트링' 사고방식이 아니라 '포맷 스트링' 사고방식이 됩니다.)

" "printf하고 효율적이며, 호출이 하고 효율적이며, 호출이 이 있습니다.printf사용자가 지정한 형식 문자열은 안전하지 않습니다.

에 대한 가장 printf을을 %n포맷 지정자.다른 모든 형식 지정자(예:%d,%n는 실제로 format 인수 중 하나로 지정된 메모리주소에 값을 씁니다.즉, 공격자가 메모리를 덮어쓰고 잠재적으로 프로그램을 제어할 수 있습니다.위키피디아는 더 자세한 정보를 제공합니다.

「 」에 printf는 ''를 슬립할 수 .%n포맷 문자열로 변환하면 안전합니다.을 "gcc"로 변경합니다.printf의 합니다.puts (달리기, 을 테스트한다.)gcc -O3 -S를 참조해 주세요.

「 」에 printf하면 공격자가 으로 ''을 할 수 .%n당신의 형식 문자열, 당신의 프로그램의 제어권을 가지고 속으로.합니다. 하세요.-Wformat-security 의도 있습니다.printf는 사용자가 문자열이 하며, 를 에 했는지 확인할 .printf. 예를 들어, 자바에는 구글의 오류에 취약과 체커 프레임워크는.

이것은 잘못된 조언.네, 만약 독자 print,에 실행 시간 문자열이 있다.

printf(str);

상당히 당신은 항상 써야 한다 위험해요.

printf("%s", str);

대신,으로는 신, 문, 일, instead, 때, 때, 때, 때, instead, instead, instead, instead, instead, instead, instead, 의 여부를 알 수 없기 때문입니다.str에는, 「」가 되어 있을 이 있습니다.%사인. compile-time 상수 문자열이 있지만, 전혀 아무런 문제가 없다.

printf("Hello, world!\n");

다른 것들 중 제네시스의 C프로그래밍 책은 말 그대로에서(, 가장 고전적인 C프로그램.그래서 누구나 그 사용 비난 조의 차라리 나는 한 사람으로 다소 기분이 상할 것이다!)이단의고 있다.

printf("Hello World\n")

등가로 자동 컴파일하다

puts("Hello World")

실행 파일을 분해하여 확인할 수 있습니다.

push rbp
mov rbp,rsp
mov edi,str.Helloworld!
call dword imp.puts
mov eax,0x0
pop rbp
ret

사용.

char *variable;
... 
printf(variable)

보안 문제로 이어질 수 있으니 printf를 절대 그런 식으로 사용하지 마세요!

따라서 당신의 책은 사실 정확합니다. printf를 하나의 변수와 함께 사용하는 것은 권장되지 않지만 printf(my string\n")는 자동으로 puts가 되기 때문에 계속 사용할 수 있습니다.

또, 그 외의 상세한 회답과 함께, 제공된 질문에 대해서, 정확하고 간결하게 회답하고 싶다고 생각하고 있습니다.


?는 왜?printf단일 인수(변환 지정자 없음)가 권장되지 않습니다.

A printf일반적으로 단일 인수를 사용하는 함수 호출은 권장되지 않으며 항상 코드화해야 하는 것처럼 올바르게 사용해도 취약성도 없습니다.

상태 는 C를 사용합니다.printf콘솔 출력으로 간단한 텍스트 문구를 제공할 수 있습니다.

또한 이 유일한 인수가 문자열 리터럴인지 문자열 포인터인지 구분해야 합니다.이것은 유효하지만 일반적으로 사용되지 않습니다.물론 후자의 경우 포인터가 유효한 문자열을 가리키도록 올바르게 설정되어 있지 않은 경우 불편한 출력이나 정의되지 않은 동작이 발생할 수 있습니다.다만, 포맷 지정자가 복수의 인수를 지정함으로써 각각의 인수에 일치하지 않는 경우에도 이러한 현상이 발생할 수 있습니다.

물론 변환이 발생하지 않기 때문에 단일 인수로 제공되는 문자열에 형식 또는 변환 지정자가 있는 것도 적절하지 않습니다.

, 「」와 같이 합니다."Hello World!"질문에서 제공한 형식 지정자가 없는 유일한 인수입니다.

printf("Hello World!");

권장되지 않거나 "악습관"이 전혀 없으며 취약성도 없습니다.

사실, 많은 C 프로그래머들이 그 HelloWorld 프로그램과 함께 C 또는 심지어 프로그래밍 언어를 배우고 사용하기 시작했습니다.printf이런 종류의 진술은 처음입니다.

만약 그들이 비천하다면 그들은 그렇지 않을 것이다.

제가 읽고 있는 책에는 이렇게 적혀 있어요.printf단일 인수(변환 지정자 없음)는 권장되지 않습니다.

그럼 책이나 작가 자체에 초점을 맞추죠만약 저자가 정말로 잘못된 주장을 하고 있다면, 그리고 왜 그렇게 하고 있는지 명확하게 설명하지 않고 심지어 그것을 가르치고 있다면, 나는 그것을 나쁜 으로 간주할 것이다.이와는 대조적으로 좋은 책은 특정 종류의 프로그래밍 방법 또는 함수를 피해야 하는 이유를 설명해야 한다.

위에서 말씀드린 바와 같이printf인수(문자열 리터럴)가 1개만 있고 형식 지정자가 없는 경우 권장되지 않거나 "bad practice"로 간주되지 않습니다.

저자에게 물어봐야 할 것은, 그 말이 무슨 뜻이었는지, 아니면 다음 판이나 각인을 위해 관련 부분을 명확히 하거나 수정해 달라고 하는 것이다.

의 다소 불쾌한 측면printf메모리 부재의 판독이 가능한 플랫폼에서도 포맷 문자의 1개인 제한적(허용 가능한) 해를 입힐 수 있습니다.%n는 다음 인수를 쓰기 가능한 정수에 대한 포인터로 해석하여 지금까지 출력된 문자 수를 식별된 변수에 저장합니다.저는 이 기능을 사용한 적이 없습니다.경량 printf 스타일의 메서드를 사용하는 경우도 있습니다.이 메서드는 실제로 사용하는 기능만 포함하지만(그 기능이나 이와 유사한 기능은 포함하지 않습니다), 신뢰할 수 없는 소스로부터 수신된 표준 printf 함수를 사용하면 ar를 읽을 수 없는 이상의 보안 취약성이 노출될 수 있습니다.비트리 스토리지

아무도 언급하지 않았기 때문에, 그들의 실적에 대한 메모를 덧붙이겠습니다.

통상적인 상황에서는 컴파일러 최적화가 사용되지 않는다고 가정합니다(즉,printf()실제 콜printf()가 아니라fputs())라고 생각합니다.printf()특히 긴 현의 경우 효율이 떨어집니다.그 이유는printf()는 문자열을 해석하여 변환 지정자가 있는지 확인해야 합니다.

이를 확인하기 위해 몇 가지 테스트를 실시했습니다.테스트는 Ubuntu 14.04(gcc 4.8.4)에서 실행됩니다.제 기계는 인텔 i5 CPU를 사용합니다.테스트 대상 프로그램은 다음과 같습니다.

#include <stdio.h>
int main() {
    int count = 10000000;
    while(count--) {
        // either
        printf("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM");
        // or
        fputs("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM", stdout);
    }
    fflush(stdout);
    return 0;
}

둘 다 다음과 같이 컴파일됩니다.gcc -Wall -O0시간은 다음을 사용하여 측정합니다.time ./a.out > /dev/null다음은 일반적인 실행 결과입니다(다섯 번 실행했는데 모든 결과는 0.002초 이내입니다).

를 위해printf()바리안트:

real    0m0.416s
user    0m0.384s
sys     0m0.033s

를 위해fputs()바리안트:

real    0m0.297s
user    0m0.265s
sys     0m0.032s

이 효과는 스트링이 매우 길면 증폭됩니다.

#include <stdio.h>
#define STR "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
#define STR2 STR STR
#define STR4 STR2 STR2
#define STR8 STR4 STR4
#define STR16 STR8 STR8
#define STR32 STR16 STR16
#define STR64 STR32 STR32
#define STR128 STR64 STR64
#define STR256 STR128 STR128
#define STR512 STR256 STR256
#define STR1024 STR512 STR512
int main() {
    int count = 10000000;
    while(count--) {
        // either
        printf(STR1024);
        // or
        fputs(STR1024, stdout);
    }
    fflush(stdout);
    return 0;
}

를 위해printf()variant (3회 실행, 실제 플러스/소수 1.5초):

real    0m39.259s
user    0m34.445s
sys     0m4.839s

를 위해fputs()variant (3회 실행, 실제 플러스/소수 0.2초

real    0m12.726s
user    0m8.152s
sys     0m4.581s

주의: gcc에 의해 생성된 어셈블리를 검사한 결과, gcc에 의해 최적화되는 것을 알게 되었습니다.fputs()에 전화를 걸다.fwrite()콜, 심지어-O0. (the.printf()콜은 변경되지 않습니다).컴파일러가 다음 문자열 길이를 계산하기 때문에 테스트가 무효가 될지는 잘 모르겠습니다.fwrite()컴파일 시.

언급URL : https://stackoverflow.com/questions/31290850/why-is-printf-with-a-single-argument-without-conversion-specifiers-deprecated

반응형