精华内容
下载资源
问答
  • 三、格式化字符串漏洞 原文:Exploiting Format String Vulnerabilities 作者:scut@team-teso.net 译者:飞龙 日期:2001.9.1 版本:v1.2 格式化字符串漏洞的通常分类是“通道问题”。如果二类不同的...

    三、格式化字符串漏洞

    原文:Exploiting Format String Vulnerabilities

    作者:scut@team-teso.net

    译者:飞龙

    日期:2001.9.1

    版本:v1.2

    格式化字符串漏洞的通常分类是“通道问题”。如果二类不同的信息通道混合为一个,并且特殊的转义字符或序列用于分辨当前哪个通道是激活的,这一类型的漏洞就可能出现。多数情况下,通道之一是数据通道,它不会解析,只会复制,而另一个通道是控制通道。

    虽然对于其本身来说并不是件坏事,如果攻击者能够提供用于某个通道的输入,它可能很快成为严重的安全问题。通常存在错误的转义,或者反转义的途径,或者忽视了某个层面,就像格式化字符串漏洞中那样。所以我们总结一下:通道问题本身没有任何漏洞,但是它们使得 bug 可以利用。

    为了展示它背后的普遍问题,这里是一个常见通道问题的列表:

    场景数据通道控制通道安全问题
    电话系统声音或数据控制音调线路控制
    PPP 协议传输数据PPP 命令流量放大
    栈数据返回地址返回地址控制
    Malloc 缓冲区Malloc 数据管理信息内存写入
    格式化字符串输出字符串格式化参数格式化函数控制

    回到特定的格式化字符串漏洞,有两种典型的场景,其中产生了格式化字符串漏洞。

    第一类(Linux rpc.statd 和 IRIX telnetd 中)。漏洞存在于syslog的第二个参数中。格式化字符串部分是用户提供。

    char tmpbuf[512];
    
    snprintf (tmpbuf, sizeof (tmpbuf), "foo: %s", user); 
    tmpbuf[sizeof (tmpbuf) - 1] = ’\0’; 
    syslog (LOG_NOTICE, tmpbuf);

    第二类(wu-ftpd 和 Qualcomm Popper QPOP 2.53 中)。部分由用户提供的字符串简介传给了格式化函数。

    int Error (char *fmt, ...);
    
    ... 
    int someotherfunc (char *user) { 
        ...
        Error (user); 
        ...
    } 
    ...

    虽然第一类漏洞能够由自动化工具安全监测(例如 pscan 或 TESOgcc),只有工具被告知函数Error用作格式化函数,第二类漏洞才能检测出来。

    但是,你可以自动化识别源码中的额外格式化函数,以及它们的参数的过程,所以总之,寻找格式化字符串的过程可以完全自动化。你甚至可以归纳出,如果有这样的工具来完成这件事,并且它没有在你的源码中发现格式化字符串漏洞,你的源码就没有这类漏洞。这不同于缓冲区溢出漏洞,其中即使由资深审计者手动审计了源码,还是会错过漏洞,并且没有可靠的方式来自动化找出它们。

    3.1 我们能够控制什么?

    通过提供格式化字符串,我们就能够控制格式化函数的行为。我们现在需要检验我们具体能够控制什么,以及如何使用它来扩展这个对进程的部分控制,来完全控制执行流。

    3.2 使程序崩溃

    使用格式化字符串漏洞的简单攻击,就是使进程崩溃。这对于某些事情是实用的,例如使守护进程崩溃,它会转储核心,并且在核心转储中有一些有用的数据。或者在一些网络攻击中,让一个服务无法响应十分有用,例如 DNS 伪造。

    但是,在使其崩溃中有一些趣味。几乎所有 UNIX 系统中,内核都会检测非法指针访问,并且进程会接收到SIGSEGV信号。通常程序会终止并转储核心。

    通过利用格式化字符串,我们可以轻易触发一些无效指针访问,通过仅仅提供像这样的格式化字符串:

    printf ("%s%s%s%s%s%s%s%s%s%s%s%s");

    由于%s展示某个地址中的内存,这个地址位于栈上,栈上也储存了大量其他数据。我们就有很大机会来从非法地址服务数据,这个地址并没有映射。同时,多数何世华函数的实现提供了%n参数的功能,他可以用于向栈上的地址写入。如果它执行了几次,也一定会产生崩溃。

    3.3 查看进程内存

    如果我们可以查看格式户函数的回复 – 也就是输出字符串 – 我们就可以从中收集有用信息,因为它是我们所控制的行为的输出。而且我们可以使用这个结果,来获得我们的客户端字符串做了什么,以及进程的布局是什么样的概览。

    这对于很多东西都很使用,例如为真正的利用寻找正确的偏移,或者仅仅是重新构造目标进程栈帧。

    3.3.1 查看栈

    我们可以展示栈内存的一些部分,通过像这样使用格式化字符串:

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

    这可以工作,因为我们让printf函数来从栈中获取五个参数,并将其展示为 8 位填充的十六进制数值。所以可能的输出是:

    40012980.080628c4.bffff7a4.00000005.08059c04

    这是栈内存的部分转储,从当前的栈底一直到栈顶 – 假设栈向低地址增长。取决于格式化字符传缓冲区的大小,以及输出缓冲区的大小,使用这种技巧,你可以或多或少重构栈内存的一部分。在一些情况下,你甚至可以获取整个栈内存。

    栈的转储提供了关于程序流以及函数局部变量的重要信息,并且可能对于寻找正确偏移以便成功利用有所帮助。

    3.3.2 查看任何地址的内存

    我们也可以查看不同于栈内存的任意地址。为此,我们需要让格式化函数从我们可以提供的某个地址展示内存。这就有两个问题:首先,我们需要找到一个格式化字符串,它将某个地址(传值)用作栈的参数,并且展示其中的内存,并且我们需要提供这个地址,我们在第一种情况中足够幸运,由于%s参数就是干这个的,它展示内存 – 通常是 ASCIIZ 字符串 – 从栈上提供的地址。所以剩下的问题是,如何将这个栈上的地址放到正确的位置上。

    我们的格式化字符串通常位于栈上,所以我们已经距离完全控制这个区域非常近了,格式化字符串就在这里。格式化函数在内部维护一个指针,指向当前格式化参数的栈区域。如果我们能够将这个指针指向一块可控的内存区域,我们就能向%s参数提供一个地址。为了修改栈指针,我们可以仅仅使用假的参数,它会通过打印垃圾来挖掘栈区。

    这里我们假设我们能够完全控制整个字符串。我们稍后会看到,部分控制,字符串过滤,空字节包含的地址,以及类似的问题都会存在,无论何时利用字符串格式化漏洞。

    printf ("AAA0AAA1_%08x.%08x.%08x.%08x.%08x");

    %08x 参数使格式化函数内部的栈指针向栈顶方向增加。将这个参数增加之后,栈指针就指向了我们的内存:格式化字符串本身。格式化函数总是维护最低的栈帧,所以如果我们的缓冲区完全在栈上,它一定会在当前栈指针的上面。如果我们正确选择了%08x的数值,我们就能够展示任意地址的内存,通过向我们的字符串附加%s。在我们的例子中,地址是非法的,它是AAA0。让我们将其换成真实的地址。

    例如:

    address = 0x08480110 
    // address (encoded as 32 bit le string): "\x10\x01\x48\x08"
    printf ("\x10\x01\x48\x08_%08x.%08x.%08x.%08x.%08x|%s|");

    就会转储0x08480110的内存,直到到达了空字符。通过动态增加内存地址,我们可以查看整个进程空间。甚至可以创建远程进程的核心转储,就像映像那样,以及从中重新构建二进制。寻找利用不成功的原因也是很有用的。

    如果我们不能通过使用 4 字节的 POP 来达到精确的格式化字符串的边界,我们需要填充格式化字符串,通过前置一个、两个或三个垃圾字符。这就好比缓冲区溢出利用中的对齐。

    我们不能够按位移动栈指针,反之我们移动格式化字符串本身,以便到达栈指针的四字节边界,并且我们可以使用多个四字节 POP 来到达它。

    3.4 任意内存覆盖

    漏洞利用的圣杯就是控制进程的指令指针。在多数情况下,指令指针(通常命名为 IP,或者 PC)是一个 CPU 中的寄存器,并不能直接修改,因为只有机器指令可以修改它。但是如果我们能够改动机器指令,我们就已经控制了它。所以我们不能直接控制进程。通常,进程比起当前的攻击者拥有更多的权限。

    反之,我们需要寻找修改指令指针的指令,并且影响这些指令修改它的方式。这听起来很复杂,但是多数情况下这非常简单,因为有些指令从内存获取指令指针,并且跳到那里。所以在多数情况下,控制了这部分内存,其中储存了指令指针,就控制了指令指针本身。这就是多数缓冲区溢出的工作方式。

    在两阶段的过程中,首先要覆盖保存的指令指针,之后程序会指令一个合法的指令,它将控制流转移到攻击者提供的地址中。

    我们会检测一些不同的方式,使用格式化字符串漏洞来完成它。

    3.4.1 利用 - 类似于常见的缓冲区溢出

    格式化字符串漏洞有时提供了一个在缓冲区长度周围的方式,并且和常见的缓冲区溢出的利用方式相似。这是出现在 QPOP 2.53 和 bftpd 中的代码:

    char outbuf[512]; 
    char buffer[512];
    
    sprintf (buffer, "ERR Wrong command: %400s", user); 
    sprintf (outbuf, buffer);

    这种例子通常深藏在真实的代码中,并且不会那么明显,就像上面的例子那样。通过提供一个特殊的格式化字符串,我们就能够绕过%400s的限制:

    "%497d\x3c\xd3\xff\xbf<nops><shellcode>"

    任何东西都和常见的缓冲区溢出类似,只是开头 – %497d – 不同。在常见的缓冲区溢出中,我们覆盖了函数帧在栈上的返回地址。在拥有该帧的函数返回值,它会返回到我们提供的地址。地址指向<nop>中的某个地方。有一些不错的文章,描述了这一利用方式,并且如果这个例子对于你来说还不够清楚,你应该考虑首先阅读一篇入门文章,就像 [5] 那样。

    它创建了长度为 497 的字符串。再加上错误信息(ERR Wrong command:),它超出了outbuf缓冲区四个字节。虽然user字符串只允许为 400 字节,我们可以通过不当使用格式化字符串参数来突破这个长度。由于第二个sprintf不检查其长度,它可以用于突破output的边界。现在我们写入一个返回地址0xbfffd33c,并且使用已知的旧办法来利用它,就像我们在任何缓冲区溢出中所做的那样。虽然任何允许拉伸的格式化参数都这样,例如%50d%50f或者%50s,我们还是应该选择一个不会提领指令或者可能导致除零错误的参数。这就排除了%50f%50s。我们只剩下了整数输出参数:%u%d%x

    GNU C 库包含一个 Bug,如果你使用 n 大于 1000 的%nd参数,它会导致崩溃。这是一种判断远程 GNU C 库的方式。如果你使用%.nd,它正产工作,除非你用了很大的值。有关这个长度的深入讨论,请见门户网站的文章 [3]。

    3.4.2 利用 - 只通过格式化字符串

    如果我们不能使用刚刚提到的简单的利用方式,我们仍旧可以利用这个过程。由此,我们可以扩展我们极其有限的控制 – 控制格式化函数的能力 – 到真实的执行流控制,它会执行我们的原始机器码。看看这段代码,它在 wu-ftpd 2.6.0 中发现。

    char buffer[512];
    
    snprintf (buffer, sizeof (buffer), user); 
    buffer[sizeof (buffer) - 1] = ’\0’;

    在上面的代码中,我们不能通过插入某些“拉伸”格式参数来扩大缓冲去,因为程序使用了安全的snprintf函数来确保我们不能突破buffer。最开始它像是,我们不能做很多有用的事情,除了使程序崩溃,并且窥探到一些内存。

    让我们回忆提到过的格式化参数。%n参数将已经打印的字节数,写入到我们所选的变量中。通过将整数指针放置到栈上作为参数,变量地址被提供给格式化函数。

    int i;
    
    printf ("foobar%n\n", (int *) &i); 
    printf ("i = %d\n", i);

    它会打印i = 6。使用我们在上面使用的相同方法来打印任何地址的内存,我们可以写入任意地址:

    "AAA0_%08x.%08x.%08x.%08x.%08x.%n"

    使用%08x参数,我们使格式化函数的内部栈指针增加了四个字节。我们这样做,知道这个指针指向了我们格式化字符串的开头(AAA0)。这可以工作,因为我们的格式化字符串通常位于栈上,在我们的格式化函数栈帧的顶部。%n向地址0x30414141写入,它由字符串AAA0表示。通常这会使程序崩溃,由于地址没有映射。但是如果我们提供了一个正确映射并且可写的地址,这可以工作,并且我们在在该地址覆盖了四个字节:

    "\xc0\xc8\xff\xbf_%08x.%08x.%08x.%08x.%08x.%n"

    上面的格式化字符串会将0xbfffc8c0的四个字节覆盖为一个小型整数。我们已经完成了目标之一,我们可以写入任意地址。但是我们不能控制我们刚才缩写的竖直 – 但是这也会改变的。

    我们所写的竖直 – 由格式化函数写入的字符储量 – 取决于格式化字符串。因为我们控制了格式化字符串,我们至少可以影响这个数量,通过写入或多或少的字节:

    int a; 
    printf ("%10u%n", 7350, &a); 
    /* a == 10 */
    
    int a; 
    printf ("%150u%n", 7350, &a); 
    /* a == 150 */

    通过使用伪造的参数%nu,我们就能控制由%n写入的数量,至少一位。但是对于写入较大数量来说 – 例如地址 – 这还不足够,所以我们需要找到一种方式来写入任意数据。

    x86 架构上的整数以四个字节储存,小端序,最低字节在内存的开始。所以例如0x0000014c的数值在内存中为\x4c\x01\x00\x00。对于格式化函数中的数量,我们可以控制最低字节,也就是内存中首先储存的字节,通过使用伪造的%nu参数来修改它。

    例如:

    unsigned char foo[4];
    
    printf ("%64u%n", 7350, (int *) foo);

    printf函数返回时,foo[0]包含\x40,它等于 64,我们使用这个数值来增加计数器。

    但是对于一个地址,我们需要完全控制四个字节。如果我们不能一次写入四个字节,我们可以尝试在一行中,写入四次,一次写入一个字节。在多数 CISC 架构中,能够写入未对齐的任意地址。这可以用于写入内存的第二个低字节,其中储存了地址,就像:

    unsigned char canary[5]; 
    unsigned char foo[4];
    
    memset (foo, ’\x00’, sizeof (foo)); 
    /* 0 * before */ strcpy (canary, "AAAA");
    
    /* 1 */ printf ("%16u%n", 7350, (int *) &foo[0]); 
    /* 2 */ printf ("%32u%n", 7350, (int *) &foo[1]); 
    /* 3 */ printf ("%64u%n", 7350, (int *) &foo[2]); 
    /* 4 */ printf ("%128u%n", 7350, (int *) &foo[3]);
    
    /* 5 * after */ printf ("%02x%02x%02x%02x\n", foo[0], foo[1], foo[2], foo[3]); 
    printf ("canary: %02x%02x%02x%02x\n", canary[0], canary[1], canary[2], canary[3]);

    返回了输出10204080canary: 00000041。我们将我们所指向的整数的低地址字节覆盖了四次。通过每次增加指针,低地址字节在我们想要写入的内存中移动,并允许我们储存完全任意的数据。

    你可以在图一的第一行看到,所有八个字节都没有被我们的覆盖代码访问。从第二行开始,我们执行了四次覆盖,每一步都向右提升一个字节。最后一行展示了最终的预期状态:我们覆盖了foo数组的所有四个字节,但是这样做的时候,我们破坏了canary的三个字节。我们包含了canary数组,只是为了看到我们覆盖了不想覆盖的内存。

    图一:四阶段的地址覆盖

    虽然这个方式看起来复杂,它也可以用于覆盖任意地址的任意数据。为了解释,我们现在为止只对每个格式化字符串使用了一次写入,但是他可以在一个格式化字符串内执行多次写入。

    strcpy (canary, "AAAA"); 
    printf ("%16u%n%16u%n%32u%n%64u%n", 1, (int *) &foo[0], 1, (int *) &foo[1], 1, (int *) &foo[2], 1, (int *) &foo[3]);
    
    printf ("%02x%02x%02x%02x\n", foo[0], foo[1], foo[2], foo[3]); 
    printf ("canary: %02x%02x%02x%02x\n", canary[0], canary[1], canary[2], canary[3]);

    我们使用参数1作为%u填充的伪造参数。同样,填充发生了改变,因为字符数量在我们写入32的时候已经是16了。所以我们只需要添加16个字符,而不是32个,来获取我们想要的结果。

    这是个特殊案例,其中所有字节在写入过程中递增。但是通过一个微小的修改,我们也可以写入80 40 20 10。由于我们写入整数并且顺序是小端的,在写入过程中只有最低地址字节是重要的。通过使用0x80 0x140 0x220 0x310的计数器,我们就可以构造预期的字符串。计算写入字符预期数量的计数器的代码是这个:

    write_byte += 0x100; 
    already_written %= 0x100; 
    padding = (write_byte - already_written) % 0x100; 
    if (padding < 10) padding += 0x100;

    其中write_byte是我们想要创建的字节,already_written是当前写入数量,由格式化函数维护,padding是我们已经使计数器增加的字节数,例如:

    write_byte = 0x7f; 
    already_written = 30;
    
    write_byte += 0x100; /* write_byte is 0x17f now */ 
    already_written %= 0x100; /* already_written is 30 */
    
    /* afterwards padding is 97 (= 0x61) */ 
    padding = (write_byte - already_written) % 0x100; 
    if (padding < 10) padding += 0x100;

    现在格式化字符串%97u会增加%n计数器,使最低地址字节等于write_byte。最后检查了填充是否低于 10,这非常需要注意。一个简单的整数输出,例如%u最多可以生成十个字符的字符串,取决于所输出的整数值。如果所需长度大于我们指定的填充,假如我们想要使用%2u输出1000,我们的值就会丢弃,以便不会丢失任何有意义的输出。通过确保我们的填充永远大于 10,我们可以使already_written的数值永远保持精确,它是格式化函数维护的计数器,由于我们总是使用格式化参数中的长度选项,写入大量的输出,就像我们指定的那样。

    这取决于格式化函数所运行的操作系统的默认字长,我们假设这里是基于 ILP32 的架构。

    在实践过程中,为了利用这种漏洞,唯一剩下的事情就是将参数以正确的顺序放到栈上,并且使用栈的 POP 序列来增加栈指针。它看起来像:

              A
    <stackpop><dummy-addr-pair * 4><write-code>

    译者注:我更推荐把dummy-addr-pair放在stackpop前面,这样偏移数量更小,stackpop长度更短,而且stackpop的长度就不会影响偏移,也不需要对齐。

    • stackpop:栈的 POP 序列,它会弹出参数,增加栈指针。一旦开始处理stackpop,格式化函数的内部栈指针就会指向dummy-addr-pair字符串。

    • dummy-addr-pair:四对伪造整数值,和要写入的地址。每一对中,地址逐个递增,伪造的整数可以是不含空字符任何东西。

    • write-code:格式化字符串实际写入内存的部分,通过使用%{n}u%n偶对,其中{n}大于 10。第一个部分用于增加或溢出格式化函数内部字节写入计数器的最低地址字节,%n用于将这一数值写入dummy-addr-pair部分中的地址。

    write-code需要修改来匹配由stackpop写入的字节数,因为当格式化函数解析write-code的时候,stackpop已经向输出写入了一些字符 – 格式化函数的计数器已经不是从零开始了,并且这个应该考虑到。

    我们所写入的地址叫做返回地址位置,简写为retloc,我们使用格式化字符串在此处创建的地址叫做返回地址,简写为retaddr

    展开全文
  • python格式化字符串的三种方法(%,format,f-string)

    万次阅读 多人点赞 2019-07-18 23:25:51
    到目前为止,我所知道的,python格式化字符串有三种方法,第一是早期就有的%,其次是2.5之后的format(),还有就是3.6添加的f字符串调试 7.1 %格式化字符串 %格式化字符串是python最早的,也是能兼容所有版本的一种...

    DAY 7. 格式化字符串

    到目前为止,我所知道的,python格式化字符串有三种方法,第一是早期就有的%,其次是2.5之后的format(),还有就是3.6添加的f字符串调试

    7.1 %格式化字符串

    %格式化字符串是python最早的,也是能兼容所有版本的一种字符串格式化方法,在一些python早期的库中,建议使用%格式化方式,他会把字符串中的格式化符按顺序后面参数替换,格式是

    "xxxxxx %s xxxxxx" % (value1, value2)
    
    • 其中 %s就是格式化符,意思是把后面的值格式化为字符类型,类似的格式化符还有%d,%f等,具体参考文章Python字符串格式化
    • 后面的value1,value2就是要格式化的值,不论是字符还是数值,都会被格式化为格式化符对应的类型
    • 当然可以不用以元组的形式传值,你可以直接写这样:"xxxxx %s" % value,不过不建议这样写,一是应为这样只能传递一个参数,二是如果value是元组或列表等类型,这样会触发TypeErrer
    • 如果只传一个参数,并且很确定参数类型不会触发异常,可以使用上面的写法,否则,我建议你提供一个单元素元组,就像"xxxx %s " % (value,)
    value1 = (7, 8)
    value2 = [9, 0]
    print("DAY %s 格式化字符串 %s " % (value1,value2))
    value3 = 1
    s = "xxxix %s" % value3  # 不推荐
    print(s)
    s1 = "xxxx %s " % value1
    print(s1)  # TypeError: not all arguments converted during string formatting
    

    7.2 format()

    %虽然强大,但用起来难免有些麻烦,代码也不是特别美观,因此,在python 2.5 之后,提供了更加优雅的str.format()方法。

        def format(self, *args, **kwargs): # known special case of str.format
            """
            S.format(*args, **kwargs) -> str
    
            Return a formatted version of S, using substitutions from args and kwargs.
            The substitutions are identified by braces ('{' and '}').
            """
            pass
    
    • format()的常用用法
    # 使用名称占位符
    s2 = "xxxx {age} xxxx {name}".format(age=18, name="hangman")
    print(s2)  # xxxx 18 xxxx hangman
    
    # 使用序号占位符,为空默认从左到右01234.。。
    s3 = "xxxx {1} xxx{0}".format(value1,value2)
    print(s3)  # xxxx [9, 0] xxx(7, 8)
    
    # 也可以混合使用
    s4 = "xxxx {} XXX {name} xxx {}".format(value2,value1,name="s4")
    print(s4)  # xxxx [9, 0] XXX s4 xxx (7, 8)
    

    7.3 f-string

    f-string是2015年python 3.6 根据PEP 498新添加的一种字符串格式化方法,f-string实际上是在运行时计算的表达式,而不是常量值。在Python源代码中,f-string是一个文字字符串,前缀为’f’,其中包含大括号内的表达式。表达式会将大括号中的内容替换为其值。例如

    import datetime
    name = "zings"
    age = 17
    date = datetime.date(2019,7,18)
    print(f'my name is {name}, this year is {date:%Y},Next year, I\'m {age+1}')  # my name is zings, this year is 2019,Next year, I'm 18
    

    7.3.2 格式规范迷你语言

    “格式规范”用于格式字符串中包含的替换字段中,以定义各个值的显示方式

    标准格式说明符的一般形式是:

    format_spec     ::=  [[fill]align][sign][#][0][width][grouping_option][.precision][type]
    fill            ::=  <any character>
    align           ::=  "<" | ">" | "=" | "^"
    sign            ::=  "+" | "-" | " "
    width           ::=  digit+
    grouping_option ::=  "_" | ","
    precision       ::=  digit+
    type            ::=  "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
    
    (1) 对齐 align
    align(对齐方式)作用
    <左对齐(字符串默认对齐方式)
    >右对齐(数值默认对齐方式)
    =填充时强制在正负号与数字之间进行填充,只支持对数字的填充
    ^居中
    • 除非定义了最小字段宽度,否则字段宽度将始终与填充它的数据大小相同,因此在这种情况下,对齐选项没有意义。
    • 如果指定了align值,则可以在其前面加上可以是任何字符的填充字符,如果省略则默认为空格。 无法使用文字大括号(“{”或“}”)作为格式化字符串文字中的填充字符或使用str.format()方法。 但是,可以插入带有嵌套替换字段的大括号。
    print(f'{name:^18}')  # |      zings     |
    
    (2) sign

    sign只对数字起作用

    sign作用
    +强制对数字使用正负号
    -仅对负数使用前导负号(默认)
    空格对正数使用一个’ ‘作前导,负数仍以’-'为前导
    print(f'{money:+}')  # +19999999877
    
    (3) #选项

    '#'选项使“替代形式”用于转换。 对于不同类型,替代形式的定义不同。 此选项仅对integer,float,complex和Decimal类型有效。 对于整数,当使用二进制,八进制或十六进制输出时,此选项将前缀“0b”,“0o”或“0x”添加到输出值。 对于浮点数,复数和十进制,替换形式会导致转换结果始终包含小数点字符,即使后面没有数字也是如此。 通常,只有在跟随数字的情况下,这些转换的结果中才会出现小数点字符。 此外,对于“g”和“G”转换,不会从结果中删除尾随零。

    (4) ,选项

    ','被用来对数字整数部分进行千分位分隔

    描述符作用
    ,使用,作为千位分隔符
    _使用_作为千位分隔符
    • , 仅适用于浮点数、复数与十进制整数:对于浮点数和复数,, 只分隔小数点前的数位。
    • _ 适用于浮点数、复数与二、八、十、十六进制整数:对于浮点数和复数,_ 只分隔小数点前的数位;对于二、八、十六进制整数,固定从低位到高位每隔四位插入一个 _(十进制整数是每隔三位插入一个 _)。
    print(f'{money:,}')  # 19,999,999,877
    
    (5) width

    width是定义最小字段宽度的十进制整数。 如果未指定,则字段宽度将由内容确定。

    当然,format还有很多彪悍的特性,还可以看这位大佬的文章:Python字符串格式化

    (6) .precision

    .precision对于数字对象,用来指定数字的小数位数,如果有小数;对于非数字对象,用来指定最终返回的格式化字符的最大长度,即格式化完成后,以这个precision参数对结果进行截取

    (7) type

    [外链图片转存失败(img-AtydwvFj-1563463465332)(…/…/image/python_总结_01.png)]

    **注意:**格式规范迷你语言对format一样适用(本来就是format的)

    7.4 总结

    python最先的格式化字符串方法是%,但他的致命缺点是支持的类型有限,只支持int,str,double,其他所有类型只能转换为这几个类型,还有如果传递的是元组,那么必须还要传入一个单值元组,为此,添加了str.format()以解决%-formatting中的一些问题,特别是,它使用普通函数调用语法(并因此支持多个参数),并且可以通过__format __()方法在被转换为字符串的对象上进行扩展。但str.format()又存在代码冗余的问题,例如

    v = 6*8
    print("the value is {}".format(v))
    

    而使用f-string只需要

    print(f'the value is{6*8}')
    

    F字符串提供了一种简洁易读的方式,可以在字符串中包含Python表达式的值。包括lambda表达式(要放在括号里)

    参考文章

    PEP 498

    python doc

    Python字符串格式化

    Python格式化字符串f-string概览

    GitHub python 面试题

    展开全文
  • Java格式化字符串

    千次阅读 2019-08-02 15:49:12
    String类的静态format()方法用于创建格式化...该方法使用指定的格式字符串和参数返回一个格式化字符串,格式化后的新字符串使用本地默认的语言环境。 format:格式字符串。 args:格式字符串中由格式说明符引用的...

    String类的静态format()方法用于创建格式化的字符串。format()方法有两种重载形式。

    (1)public static String format(String format, Object... args)

    该方法使用指定的格式字符串和参数返回一个格式化字符串,格式化后的新字符串使用本地默认的语言环境。

    format:格式字符串。

    args:格式字符串中由格式说明符引用的参数。如果还用格式说明符以外的参数,则忽略这些额外的参数。此参数的数目是可变的,可以为0。

    (2)public static String format(Locale l, String format, Object... args)

    l:格式化过程中要应用的语言环境。如果l为null,则不进行本地化。

    format:格式字符串。

    args:格式字符串中由格式说明符引用的参数。如果还用格式说明符以外的参数,则忽略这些额外的参数。此参数的数目是可变的,可以为0。

    示例:使用String类的静态format()方法格式化的字符串。

    /**
     * 使用String类的静态format()方法格式化的字符串
     * @author pan_junbiao
     */
    public class StrFormat
    {
        public static void main(String[] args) {
            String msg1 = String.format("您好,欢迎访问 %s","pan_junbiao的博客");
            String msg2 = String.format("博客地址:%s","https://blog.csdn.net/pan_junbiao");
            // 输出信息
            System.out.println(msg1);
            System.out.println(msg2);
        }
    }

    执行结果:

     

    1、日期和时间字符串格式化

    在应用程序设计中,经常需要显示时间和日期。如果想输出满意的日期和时间格式,一般需要编写大量的代码经过各种算法才能实现。format()方法通过给定的特殊转换符作为参数来实现对日期和时间的格式化。

    1.1 日期格式化

    使用format()方法对日期进行格式化时,会用到日期格式化转换符,常用的日期格式化转换符如下所示。

    示例:在项目中创建Eval类,实现日期格式化。

    import java.util.*;
    
    /**
     * 日期格式化
     * @author pan_junbiao
     */
    public class Eval {
        public static void main(String[] args) {
            Date date = new Date(); // 创建Date对象date
            String year = String.format("%tY", date); // 将date进行格式化
            String month = String.format("%tB", date);
            String day = String.format("%td", date);
            String msg = String.format("您好,欢迎访问 %s","pan_junbiao的博客");
            // 输出信息
            System.out.println(msg);
            System.out.println("今年是:" + year + "年");
            System.out.println("现在是:" + month);
            System.out.println("今天是:" + day + "号");
        }
    }

    执行结果:

    1.2 时间格式化

    使用format()方法对时间进行格式化时,会用到时间格式化转换符,时间格式化转换符要比日期转换符更多、更精确,它可以将时间格式化为时、分、秒、毫秒。常用的时间格式化转换符如下表所示。

    示例:在项目中创建GetDate类,实现将时间格式化。

    import java.util.*;
    
    /**
     * 时间格式化
     * @author pan_junbiao
     */
    public class GetDate {
        public static void main(String[] args) {
            Date date = new Date(); // 创建Date对象date
            String hour = String.format("%tH", date); // 将date进行格式化
            String minute = String.format("%tM", date);
            String second = String.format("%tS", date);
            String msg = String.format("您好,欢迎访问 %s","pan_junbiao的博客");
            // 输出的信息
            System.out.println(msg);
            System.out.println("现在是:" + hour + "时" + minute + "分" + second + "秒");
        }
    }

    执行结果:

     

    2、常规类型格式化

    在程序设计过程中,经常需要对常规类型的数据进行格式化,例如格式化为整数,格式化为科学计数表示等,在Java中可以使用常规类型的格式化转换符来实现,下表列出了常规类型的格式化转换符。

    示例:在项目中创建General类,实现常规类型格式化。

    /**
     * 时间格式化
     * @author pan_junbiao
     */
    public class General {
        public static void main(String[] args) {
            String str = String.format("%d", 400 / 2); // 将结果以十进制显示
            String str2 = String.format("%b", 3 > 5); // 将结果以布尔形式显示
            String str3 = String.format("%x", 200); // 将结果以十进制格式显示
            String msg = String.format("您好,欢迎访问 %s","pan_junbiao的博客");
            // 输出格式化字符串
            System.out.println(msg);
            System.out.println("400的一半是:" + str);
            System.out.println("3>5正确吗:" + str2);
            System.out.println("200的十六进制数是:" + str3);
        }
    }

    执行结果:

     

    展开全文
  • LABVIEW格式化字符串

    千次阅读 2019-05-06 15:07:05
    对于一般的应用,使用“编辑格式化字符串”对话框就足够了,但是对话框中包含的格式化字符串只是其中的一部分,要想自如使用该函数,必须了解格式化字符串的语法。 下面我将分类详细介绍各种格式化字符串,最常用的...

    今天我们要详细讨论格式化写入字符串函数的多种用法,格式化写入字符串数最为关键的是“格式字符串”的问题。对于通常的“格式字符串”,函数提供了配置对话框。调用对话框的方法如下图所示:

    对于一般的应用,使用“编辑格式化字符串”对话框就足够了,但是对话框中包含的格式化字符串只是其中的一部分,要想自如使用该函数,必须了解格式化字符串的语法。
    下面我将分类详细介绍各种格式化字符串,最常用的格式化字符串莫过于整型数了。
    一、整数类型转换为字符串
    我们知道整型数有多种数制表示形式,转化为字符串后,也同样存在数制的问题。
    1、整型数转换为10进制字符串。
    整型数转换为10进制字符串的格式化字符串为:%d

    2、整型数转换为16进制字符串。
    整型数转换为16进制字符串的格式化字符串为%x.

    3、整型数转换为8进制数
    整型数转换为8进制字符串的格式化字符串为%o

    4、整型数转换为2进制字符串
    整型数转换为2进制字符串的格式化字符串为%b。

    二、字符串的左右调整与填充
    数值转换为字符串时,默认情况下转换位数的多少是自动适应的。从上面几个程序框图上可以看出显示的字符串长度(宽度)各不相同,大多情况下,都需要规定统一的长度。
    比如我们转换的是一个寄存器当前的二进制值,我们知道寄存器可能是8位、16位、32位、64位等等,对于16位寄存器,转换为16进制后字符串为2个字符,16位寄存器转换成2进制则是16个字符,因此确定转换后的字符串长度是很重要。
    整数转换为字符串确定字符串长度(宽度)的格式化字符串为%4x、%32b等等。百分号后的数字表示宽度。

    上图中的确显示出了我们确定的位数,不过存在问题,应该出现0的位置出现的是空格。空格是LABVIEW默认填充字符,LABVIEW也支持填充0,这才是我们需要的。
    格式化字符串%032b、%04x中,0表示左侧填充0,无0表示左侧填充空格。

    通常情况下,我们一般都会选择在左侧进行调整,填充空格或者无效0。LABVIEW也允许我们在右侧进行调整,也就是左对齐,但是只能默认填充空格,不允许添加0。这是非常合理的,在右侧添加0并非是无效的,它会改变我们要显示的值,会导致错误的结果,因此自然不会允许。
    格式化字符串%-10d中“-”表示进行右侧调整,填充空格。

    三、浮点数转换为字符串
    浮点数转换为字符串的格式化字符串与整数转换为字符串完全不同。
    1、不指定格式字符串时,浮点数转换为字符串默认的格式符为%f,%f以小数形式表示浮点数。
    格式化字符串%f表示浮点数转换为字符串,默认精度(小数点后的位数)为6。

    2、确定小数点后的位数(精度)
    格式化字符串%.3f中,小数点后的3表示3位精度,即小数点后保留3位。

    从上图可以看出,浮点数转换为字符串时,根据指定精度,进行了四舍五入操作。
    3、设定精度为0,浮点数转换为整型字符串,不显示小数点。
    这是浮点数转换为字符串的一种特殊用法。

    4、设定浮点数转换为字符串的宽度
    浮点数转换为字符串的宽度表示的是整个字符串表示的总位数,包括小数点。如果位数不足,则用空格填充。如果设定的宽度不足,LABVIEW保证精度,即小数点后的位数,增加整数部分位数,自动调整。
    格式字符串%10.2f中,10表示数据宽度为10,则整数部分为7位、小数点1位、小数点后2位。

    5、左侧调整填充0
    与整型数转换为字符串一样,当位数不足时,左侧默认填充空格,也可以填充0.
    格式化字符串%010.2f表示宽度为10,不足时左侧填充0.

    6、去除尾部无效0
    浮点数转换为字符串时,由于精度问题,经常会出现字符串尾部有一串无效的0。虽然可以通过设定精度的方法控制小数点的位数,但是会造成四舍五入的问题。
    格式化字符串“%#”用来去除尾部无效0.

    7、浮点数转换为科学计数法表示的浮点数。
    利用小数形式表示浮点数是最为常见的,对于数学计算等应用,利用科学计数法表示浮点数也是非常常见的。
    格式化字符串%e表示使用科学计数法表示浮点数。

    8、LABVIEW自动选择小数方式还是科学计数法方式。
    格式化字符串%g表示LABVIEW自动选择浮点数表示形式。
    根据数字的指数,LabVIEW使用f或e。如指数大于–4或小于指定的精度,LabVIEW使用f。如指数小于–4或大于指定的精度,LabVIEW使用e。

    9、利用SI符号表示浮点数
    利用SI符号表示浮点数在其它编程语言中很少能看到,目前SI符号在日常生活中也经常能见到了,比如我们的USB硬盘是多少M的、多少G的、多少T的等等,这些就是SI符号表示的数。
    格式化字符串%p表示用SI符号表示浮点数

    10、显示数的符号
    上面的例子中,无论是整型数还是浮点数,我们使用的都是正数,转换后的字符串不显示符号。如果使用的是负数,那么在转换后的字符串中会自动显示负号。
    个别情况下,我们需要转换后的字符串必须显示符号,比如正数必须显示“+”,可以使用下面的格式字符串。
    使用格式化字符串%+在正数字符串前显示+号。

    “+”格式符对于负数将直接显示,结果不正确,因此使用时必须确保输入数值为正数。
    四、布尔和枚举类型转换为字符串
    布尔数据类型占用1个字节,非零表示TRUE,0表示FALSE,因此在字符串中可以用多种方式表示布尔,比如真假、开关、ONOFF等等。LABVIEW默认以字符串TRUE表示布尔真,以FALSE表示布尔假。
    通常我们都是通过选择函数把布尔量转换为字符串形式,如下图所示:

    其实用格式化写入函数也是可以实现转换的,但是由于帮助文件中没有提到,因此可能很多人都不清楚。

    布尔量也可以直接转换为数值格式表示的字符串,即0或者1表示的字符串。

    枚举是用字符串表示的整型量,利用格式化写入字符串函数可以直接把枚举转换为字符串。利用不同的格式化字符串,枚举转换后的字符串可以是数值形式的字符串,也可以是枚举的项名。

    除了数值转换为字符串之外,利用格式化写入字符串函数,还可以转换时间类型参数,其中的格式化说明符非常复杂,

    展开全文
  • 格式化字符串漏洞原理详解

    万次阅读 多人点赞 2018-12-08 23:38:02
    再说格式化字符串漏洞之前,先了解一下printf函数和利用该漏洞的重要 格式化字符串%n,利用他可以做到任意内存写入。 函数原型 int printf (“格式化字符串”,参量… ) 函数的返回值是正确输出的字符的个数,...
  • 格式化字符串漏洞

    千次阅读 2018-05-14 11:02:22
    格式化字符串,也是一种比较常见的漏洞类型。会触发该漏洞的函数很有限。主要就是printf还有sprintf,fprintf等等c库中print家族的函数。 我们先来看看printf的函数声明 int printf(const char* format,…) 这...
  • JS 格式化字符串

    千次阅读 2018-06-25 20:13:37
    var xxx = format("a{0}b{1}c", "试一下", "我看行"); console.log(xxx)function format(source, params) { if (arguments.length === 1) { return function () { ...
  • 1 /** 2 * @author 陈维斌 http://www.cnblogs.com/Orange-C/p/4042242.html%20 3 * 如果想将日期字符串格式化,需先将其转换为日
  • 先介绍一下格式化字符串漏洞,这种漏洞在实际中应该很少有了,但仍然需要了解这些基础的漏洞知识。 会触发该漏洞的函数很有限,主要就是printf、sprintf、fprintf等print家族函数,该题就是利用了printf的漏洞,...
  • C++11的一个格式化字符串的黑科技

    万次阅读 2019-07-09 15:33:41
    有时候我们需要按照某种格式写文件,比如我们做一个代码自动生成工具,要在源代码中写入一个函数,为了保证自动生成的代码文件的可读性,需要将缩进格式也写如到文件,如下面的代码 int func() { int i, sum; for...
  • Qt的格式化字符串:QString::arg()

    千次阅读 2019-10-12 16:07:13
    arg() 主要就看arg()原函数,还有第一条重载的arg()函数,往下的重载你应该知道怎么写了吧。 QString::arg(const QString & a, int fieldWidth = 0, QChar fillChar = QLatin1Char( ' ' )...替换%1的字符串 ...
  • 主要是利用格式化字符串漏洞来达到溢出目的,并且借用snprintf的特性,即在遇到格式化参数之前会先将正常的字符复制到指定区域中,最终通过字节大小的计算,利用%n向指定地址写入特定的返回地址
  • python3 格式化字符串 f-string 介绍

    千次阅读 2020-06-13 11:27:37
    f-string,亦称为格式化字符串常量(formatted string literals),是Python3.6新引入的一种字符串格式化方法,该方法源于PEP 498 – Literal String Interpolation,主要目的是使格式化字符串的操作更加简便。...
  • 菜鸟PWN手进阶之格式化字符串

    千次阅读 2021-01-05 21:12:00
    菜鸟PWN手进阶之格式化字符串
  • golang格式化字符串

    千次阅读 2019-06-04 14:24:16
    %v 相应值的默认格式。在打印结构体时,“加号”标记(%+v)会添加字段名 %#v 相应值的 Go 语法表示 %T 相应值的类型的 Go 语法表示 %% 字面上的百分号,并非值的占位符 [布尔] %t 单词 true 或 false。 [整数] %b ...
  • mysql格式化字符串

    千次阅读 2019-07-09 15:22:04
    1.替换字符串中的字符 update `表名` set `列名`=REPLACE(`列名`,‘-’,‘’); 将指定列明中的“-”替换成空字符串
  • 深入解析sprintf格式化字符串漏洞

    万次阅读 多人点赞 2018-11-05 19:33:43
    深入解析sprintf格式化字符串漏洞 0x00 前言 从相遇到相识 从相识到相知 ......... 不过你真的懂ta吗 这次故事的主角是PHP中的格式化函数sprintf 具体详见:http://bey0nd.xyz/2018/11/05/1/ 0x01 sprintf...
  • lua格式化字符串

    千次阅读 2017-11-10 14:58:23
    字符串或串(String)是由数字、字母、下划线组成的一串字符。 Lua 语言中字符串可以使用以下三种方式来表示: 单引号间的一串字符。 双引号间的一串字符。 [[和]]间的一串字符。 以上三种方式的字符串实例如下: ...
  • labview学习18-----格式化字符串

    千次阅读 2018-09-06 14:00:21
    1:格式化扫描字符串 在输入字符串中查找符合特定格式的字符。 扫描字符串&amp;扫描值异同点   扫描字符串 扫描值 输入 格式字符串:右键编辑扫描字符串可根据向导进行编辑 格式字符串...
  • python中format()方法格式化字符串

    千次阅读 多人点赞 2017-10-10 08:56:00
    format()是python2.6新增的一个格式化字符串的方法,功能非常强大,有可能在未来完全替代%格式化方法,相比%,format()的优点有: 1 .格式化时不用关心数据类型的问题,format()会自动转换,而在%方法中,%s用来...
  • 如果希望输出文字信息的同时,一起输出数据,可以使用格式化操作符%,格式化操作符负责处理字符串中的格式,需要注意的是,链接不同的字符时,根据不同类型的数据需要使用不同的格式化字符 常用的格式化字符有四种 %s...
  • 二、格式化函数格式化函数是一类特殊的 ANSI C 函数,接受可变数量的参数,其中的一个就是所谓的格式化字符串。当函数求解格式化字符串时,它会访问向函数提供的额外参数。它是一个转换函数,用于将原始的 C 数据...
  • pwn初识格式化字符串漏洞

    千次阅读 2018-12-02 15:21:04
    格式化字符串漏洞通常情况下是由printf函数产生的; 正常情况下我们用printf函数输出字符串时是这样的: char a[100]="fmtstr"; int b=12,c=666; printf("%s %d %x",a,b,c); 但是有时是出于方便会直接输出字符串...
  • python-格式化字符串(%d %f %s )

    千次阅读 2019-10-30 01:23:27
    在python中,通过使用 % 实现格式化字符串的目的。(这与c语言一致) 其中,在格式化整数和浮点数时可以指定是否补0和整数与小数的位数。 首先,引入一个场宽的概念。 在C语言中场宽代表格式化输出字符的宽度。 ...
  • 学习记录-Qt之QString格式化字符串

    千次阅读 2019-03-12 16:53:41
    1.自动补零 int val = 16; // 0f QString str = QString("%1").arg(val &amp; 0xFF, 2, 16, QLatin1Char('0')); // 0016 QString str = QString("%1").arg(val, 4, 10, QLatin1Char('0')...
  • 格式化字符串溢出

    千次阅读 2018-07-29 12:05:12
    漏洞原理:  printf是c语言中少有的支持可变... 格式化字符串漏洞的产生根源主要源于对用户输入未进行过滤,这些输入数据都作为数据传递给某些执行格式化操作的函数,如printf,sprintf,vprintf,vprintf。恶...
  • c语言中的格式化字符串

    万次阅读 2016-11-10 16:09:05
    C语言中格式字符串的一般形式为: %[标志][输出最小宽度][.精度][长度]类型, 其中方括号[]中的项为可选项。 一、类型 我们用一定的字符用以表示输出数据的类型,其格式符和意义下表所示: 字符 意义 a ...
  • 第一种方法: <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> <script type="text/javascript">...String.format = function() { if (arguments.length == 0) retur...
  • C/C++格式化字符串说明

    千次阅读 2014-04-29 22:44:16
    C/C++格式化字符串说明 1. 格式化规定符 ━━━━━━━━━━━━━━━━━━━━━━━━━━  符号 作用 ──────────────────────────  %d 十进制有符号整数  %u 十...
  • 格式化字符串漏洞打补丁办法 patch

    千次阅读 2018-03-27 21:48:37
    方法 —–&gt; 替换printf 为puts0x1 确定计算方法由于地址是相对的 所以新地址 = 目标地址(这里即为 puts的plt地址) - 当前修改指令地址的下一指令地址0x02 确定目标地址在plt 段 可以看到 puts_plt 地址为 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 964,826
精华内容 385,930
关键字:

格式化字符串