之前做一道关于格式化字符串泄漏canary的题的时候,对%n$p的n不知道怎么计算。

在看了大佬的wp后大概总结出三种。

这里画了一个栈内存的图:

format_string

要求的偏移就是图中两个偏移相加。

这里以bjdctf2020_babyrop2为例

存在格式化字符串的函数的伪代码:

unsigned __int64 gift()
{
  char format[8]; // [rsp+0h] [rbp-10h] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("I'll give u some gift to help u!");
  __isoc99_scanf("%6s", format);
  printf(format);
  puts(byte_400A05);
  fflush(0LL);
  return __readfsqword(0x28u) ^ v2;
}

1.直接IDA,看栈的结构

stack

var_8就是canary的位置,

format与canary之间差了一个字节,1字节就是canary相对格式化字符串的偏移,再加上寄存器中的6个字节的参数,得出n = 7

test0

成功泄漏出canary

2.利用aa%n$p测出n的值,或者在aa后面加上若干个%p找到aa也就是\x6161的位置。由于这道题限制了format string的大小为6个字节,所以只能用前者。

test1

当n=6时发现了6161,所以再加上格式化字符串到canary的偏移,即n=6+1,n为7就能泄漏出canary

test2

3.用gdb调试

在printf函数设下断点,输入aaaaaa,此时rsp即栈顶就是aaaaaa的地址

test3

查看内存

test4

在函数结束时会将canary与fs/gs寄存器中的值进行亦或(xor)操作,在这里下一个断点test6

查看rax寄存器test5

可以发现与之前有一个值相同,就是canary,然后就可以直接把偏移算出来了。