% 字符串格式中字符的含义(printf 样式格式)

Meaning of characters in % string formatting (printf-style formatting)

我目前正在研究格式字符串漏洞 (writeUp config console picoctf) 并遇到了奇怪的语法(作为用于 pwn 的 python 脚本的一部分):

payload += ("%%%du|%%17$hn|" % 2493).rjust(16)

我知道作者的意图是用上面的值(2493)覆盖一个内存地址。我可以使用以下语法实现相同的目标:

payload += "%2493x%17$hn".rjust(16)

所以我知道第二部分中 $ 和 hn 的含义。

我主要对上面第一个版本中的多个“%”感到困惑,并且“% 2493”出现在连字符之外。 我试过 google 但它只会导致标准格式字符串解释。

有人可以解释上面的第一个版本,或者可能有一个 link 其中有相同的解释。

$hm 没有什么特别的意思,它们只是字符串中的字符。只需 运行 字符串格式化代码,您就会看到:

>>> "%%%du|%%17$hn|" % 2493
'%2493u|%17$hn|'

在上面的字符串中,%d 被您的数字 2493 替换,而 %% 用于在您的字符串中将 % 显示为单个 %% 字符串格式 中具有特殊含义。其余为普通字符串字符。

%-formatting(也称为 printf-style string formatting) is a old style of Python's string formatting which is these days is generally done by using str.format function. PEP 3101 建议用新的高级字符串格式替换 % 运算符。

两者之间的非常好的比较可在以下位置找到:"Python string formatting: % vs. .format"


"printf-style String Formatting" document 开始,它允许以下 转换类型:

Conversion   Meaning
'd'          Signed integer decimal.     
'i'          Signed integer decimal.     
'o'          Signed octal value.
'u'          Obsolete type – it is identical to 'd'.    
'x'          Signed hexadecimal (lowercase).    
'X'          Signed hexadecimal (uppercase).    
'e'          Floating point exponential format (lowercase).
'E'          Floating point exponential format (uppercase).
'f'          Floating point decimal format.
'F'          Floating point decimal format. 
'g'          Floating point format. Uses lowercase exponential format if exponent is less than -4 or not less than precision, decimal format otherwise.
'G'          Floating point format. Uses uppercase exponential format if exponent is less than -4 or not less than precision, decimal format otherwise.
'c'          Single character (accepts integer or single character string).  
'r'          String (converts any Python object using repr()).  
's'          String (converts any Python object using str()).   
'a'          String (converts any Python object using ascii()). 
'%'          No argument is converted, results in a '%' character in the result.

并且具有 转换标志 个字符:

+------+--------------------------------------------------------------------------------------------------------------+
| Flag | Meaning                                                                                                      |
+------+--------------------------------------------------------------------------------------------------------------+
| '#'  | The value conversion will use the “alternate form” (where defined below).                                    |
+------+--------------------------------------------------------------------------------------------------------------+
| '0'  | The conversion will be zero padded for numeric values.                                                       |
+------+--------------------------------------------------------------------------------------------------------------+
| '-'  | The converted value is left adjusted (overrides the '0' conversion if both are given).                       |
+------+--------------------------------------------------------------------------------------------------------------+
| ' '  | (a space) A blank should be left before a positive number (or empty string) produced by a signed conversion. |
+------+--------------------------------------------------------------------------------------------------------------+
| '+'  | A sign character ('+' or '-') will precede the conversion (overrides a “space” flag).                        |
+------+--------------------------------------------------------------------------------------------------------------+

您在引号外看到的 % 是一个运算符(与对数字取模的运算符相同)。当它的左参数是一个字符串时,它会进行 "old style" 字符串格式化。在 Python 2.6 引入 str.format 之前,这是主要的字符串格式,因此您会在旧代码中看到很多。 % 运算符使用的格式化语言非常接近于 C 的 printf,因此一些更习惯 C 的程序员可能也更喜欢它而不是更新的,也许更多的 "Pythonic" 格式化方法.

在您的示例中,格式字符串中最相关的部分是 %d,它将参数格式化为整数。 % 是用于在格式字符串中引入替换的字符,这就是为什么在其他两个地方的格式字符串中还有另一个有趣的功能。如果要在输出中使用单个百分号,则需要在格式字符串中放入两个:%%。你有两次,一次在 %d 之前,一次在之后。

最后得到的字符串 "%2493x%17$hn" 对 Python 没有任何特殊意义,但它可能在其他语言或某些特殊上下文中。它不会再次用作格式字符串,因为 %17$ 不是有效的格式说明符(%2493x 是有效的,尽管可能只是巧合)。