为什么printf说明符格式%n不起作用?

Ale*_*lio 2 c printf specifier

这是我的代码:

#include <stdio.h>

int main(void) {
    int n;

    fprintf(stdout, "Hello%n World\n", &n);
    fprintf(stdout, "n: %d\n", n);

    return 0;
} 
Run Code Online (Sandbox Code Playgroud)

这是我的输出:

Hellon: 0
Run Code Online (Sandbox Code Playgroud)
  1. 为什么fprintf格式说明符"%n"不起作用?
  2. 为什么要打印的字符串被中断?

ISO/IEC 9899:201x C11 - 7.21.6.1 - fprintf功能

转换说明符及其含义是:

(......)

%n参数应该是一个指向有符号整数的指针,其中写入到目前为止通过调用fprintf写入输出流的字符数.没有参数被转换,但是消耗了一个参数.如果转换规范包括任何标志,字段宽度或精度,则行为未定义....

(......)

这是我在Code :: Blocks上使用的编译器版本:

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=C:/Program\ Files/mingw-w64/x86_64-8.1.0-posix-seh-rt_v6-rev
0/mingw64/bin/../libexec/gcc/x86_64-w64-mingw32/8.1.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../../../src/gcc-8.1.0/configure --host=x86_64-w64-mingw32 --bu
ild=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --prefix=/mingw64 --with-sysr
oot=/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64 --enable-shared --enable
-static --disable-multilib --enable-languages=c,c++,fortran,lto --enable-libstdc
xx-time=yes --enable-threads=posix --enable-libgomp --enable-libatomic --enable-
lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --
enable-version-specific-runtime-libs --disable-libstdcxx-pch --disable-libstdcxx
-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls
 --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=noco
na --with-tune=core2 --with-libiconv --with-system-zlib --with-gmp=/c/mingw810/p
rerequisites/x86_64-w64-mingw32-static --with-mpfr=/c/mingw810/prerequisites/x86
_64-w64-mingw32-static --with-mpc=/c/mingw810/prerequisites/x86_64-w64-mingw32-s
tatic --with-isl=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-pkgv
ersion='x86_64-posix-seh-rev0, Built by MinGW-W64 project' --with-bugurl=https:/
/sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/x
86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x
86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/
include' CXXFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/x86_64-810-posix-seh-rt_v6
-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include
 -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include' CPPFLAGS=' -I/c/
mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prere
quisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw
32-static/include' LDFLAGS='-pipe -fno-ident -L/c/mingw810/x86_64-810-posix-seh-
rt_v6-rev0/mingw64/opt/lib -L/c/mingw810/prerequisites/x86_64-zlib-static/lib -L
/c/mingw810/prerequisites/x86_64-w64-mingw32-static/lib '
Thread model: posix
gcc version 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)
Run Code Online (Sandbox Code Playgroud)

chq*_*lie 8

Microsoft文档中所述%n,默认情况下在MinGW系统上使用的Microsoft C库中禁用:

重要

由于%n格式本质上不安全,因此默认情况下禁用该格式.如果%n在格式字符串中遇到,则调用无效参数处理程序,如参数验证中所述.要启用%n支持,请参阅_set_printf_count_output.

%n微软声称是否真的不安全是值得商榷的.显示支持此声明的示例将此printf功能与使用可变格式字符串相结合,该字符串可由攻击者通过缓冲区溢出错误进行更改.

在某些Microsoft系统上,您可以通过以下方式修复程序:

#include <stdio.h>

int main(void) {
    int n;

    _set_printf_count_output(1);

    fprintf(stdout, "Hello%n World\n", &n);
    fprintf(stdout, "n: %d\n", n);

    return 0;
} 
Run Code Online (Sandbox Code Playgroud)

对于更便携的方法:您可以使用解决方法来避免使用%n并仍然获得相同的结果:

#include <stdio.h>

int main(void) {
    int n;

    n = fprintf(stdout, "Hello");
    fprintf(stdout, " World\n");
    fprintf(stdout, "n: %d\n", n);

    return 0;
} 
Run Code Online (Sandbox Code Playgroud)

输出:

Hello World
n: 5
Run Code Online (Sandbox Code Playgroud)

  • *攻击者可以通过缓冲区溢出错误更改可变格式字符串* Microsoft 对此进行了很好的指责。而且它破坏了可移植性。多么*方便*。 (2认同)
  • mingw 有没有办法在不更改源代码的情况下解决这个问题,只需链接正确的 lib 文件?我认为他们甚至提供了一个增强的 libc,修复了所有 printf 错误。 (2认同)