Pwn 学习 格式化字符串

1.测试代码

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int init_func(){
    setvbuf(stdin, 0, 2, 0);
    setvbuf(stdout, 0,2 ,0);
    setvbuf(stderr, 0, 2, 0);
    return 0;
}

int dofunc(){
    char buf2[0x8];
    char buf[0x8] = {};

    int *p;
    buf[0] = 0x61;
    buf[1] = 0x62;
    buf[2] = 0x63;
    buf[3] = 0x64;
    buf[4] = 0x65;
    buf[5] = 0x66;
    buf[6] = 0x67;
    buf[7] = 0x68;
    strcpy(buf2, "deadbeef");

    printf("buf_str is %s\n", buf);
    printf("buf_addr_p is %p\n", buf);
    printf("buf_addr_x is %x\n", buf);
    printf("buf[0]_d is %d\n", buf[0]);
    printf("buf[0]_10d is %10d\n", buf[0]);
    printf("buf[0]_c is %c\n", buf[0]);
    printf("buf[0]_10c is %10c\n", buf[0]);
    printf("buf_str is %s\n", p);
    printf("buf_addr is %p\n", p);
    printf("buf_addr_p is %p,next %p,next %p,next %p,next %p,next %p,next %p,next %p,next %p\n", buf);
    return 0;
}

int main(){
    init_func();
    dofunc();
    return 0;
}

2.格式化字符串

%[parameter][flags][field width][.precision][length]type

每一种 pattern 的含义请具体参考维基百科的格式化字符串 。以下几个 pattern 中的对应选择需要重点关注

parameter

  • n$,获取格式化字符串中的指定参数

flag

  • field width
    输出的最小宽度
  • precision
    输出的最大长度
  • length,输出的长度
    hh,输出一个字节
    h,输出一个双字节
  • type
type符号
d/i有符号整数
u无符号整数
x/X16 进制 unsigned int 。x 使用小写字母;X 使用大写字母。如果指定了精度,则输出的数字不足时在左侧补 0。默认精度为 1。精度为 0 且值为 0,则输出为空。
o8 进制 unsigned int 。如果指定了精度,则输出的数字不足时在左侧补 0。默认精度为 1。精度为 0 且值为 0,则输出为空。
s如果没有用 l 标志,输出 null 结尾字符串直到精度规定的上限;如果没有指定精度,则输出所有字节。如果用了 l 标志,则对应函数参数指向 wchar_t 型的数组,输出时把每个宽字符转化为多字节字符,相当于调用 wcrtomb 函数。
c如果没有用 l 标志,把 int 参数转为 unsigned char 型输出;如果用了 l 标志,把 wint_t 参数转为包含两个元素的 wchart_t 数组,其中第一个元素包含要输出的字符,第二个元素为 null 宽字符。
p,void * 型,输出对应变量的值。printf(“%p”,a) 用地址的格式打印变量 a 的值,printf(“%p”, &a) 打印变量 a 所在的地址。
n不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
%'%'字面值,不接受任何 flags, width。

3.代码演示结果

┌──(kali㉿kali)-[~/Desktop/git/ctf-pwn/fmt_test1]
└─$ ./fmt_test1     
buf_str is abcdefghdeadbeef
buf_addr_p is 0x7ffebf91a7c8
buf_addr_x is bf91a7c8
buf[0]_d is 97
buf[0]_10d is         97
buf[0]_c is a
buf[0]_10c is          a
buf_str is ���==/
buf_addr is 0x56039f67e100
buf_addr_p is 0x7ffebf91a7c8,next (nil),next (nil),next (nil),next 0x7ffebf91852c,next 0x56039f67e060,next 0x6867666564636261,next 0x6665656264616564,next 0x56039f67e100

3.修改源代码

    printf("buf_str is %s\n", buf);
    printf("buf_addr_p is %p\n", buf);
    printf("buf_addr_x is %x\n", buf);
    printf("buf[0]_d is %d\n", buf[0]);
    printf("buf[0]_10d is %10d\n", buf[0]);
    printf("buf[0]_c is %c\n", buf[0]);
    printf("buf[0]_10c is %10c\n", buf[0]);
    printf("buf_str is %s\n", p);
    printf("buf_addr is %p\n", p);
    printf("buf_addr_p is %p,next %10$p,next %p,next %p,next %p,next %p,next %p,next %p,next %p,next %p,next %p\n", buf);

4.测试结果

┌──(kali㉿kali)-[~/Desktop/git/ctf-pwn/fmt_test1]
└─$ ./fmt_test1
buf_str is abcdefghdeadbeef
buf_addr_p is 0x7ffef8963a08
buf_addr_x is f8963a08
buf[0]_d is 97
buf[0]_10d is         97
buf[0]_c is a
buf[0]_10c is          a
buf_str is ���==/
buf_addr is 0x557e18117100
buf_addr_p is 0x7ffef8963a08,next 0x7ffef8963a30,next (nil),next (nil),next (nil),next 0x7ffef896176c,next 0x557e18117060,next 0x6867666564636261,next 0x6665656264616564,next 0x557e18117100,next 0x7ffef8963a30

可以看到%10p的作用是直接打印第十个%p位置上的指针内容

5.gdb调试

在这里插入图片描述
格式化字符串调用参数在x64中的顺序:
rsi -> rdi -> rdx -> rcx -> r8 -> r9 -> stack
通过格式化字符串可以无限泄露栈上的数据

* 回复评论:如有不对欢迎批评

在这里插入图片描述
编译器是谁写的我不知道,但是RSI是不是第一个被print了

* 确实有问题,我重新调了一下,很久没搞二进制了

在这里插入图片描述

按照结果来看
RSI => RDX => RCX => R8 => R9 => RSP(这里开始就是stack了)

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

==Microsoft==

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值