问题描述
我正在使用C中的格式字符串漏洞,并且尝试通过终端中提供的printf命令打印“ argc”整数的值。
我当前的代码是:
int main (int argc,char **argv) {
char buffer[32];
*More variables*
strncpy(buffer,argv[1],sizeof(buffer));
printf(buffer);
*More printf's*
}
我可能需要使用格式说明符将整数argc的内容打印到终端中,但是我似乎找不到解决方案。我所有的猜测都让我获得了所有argv堆栈寄存器(%rsi,%rdx,%rcx,%r8d,%r9d)。
应在终端中给出格式字符串,如以下示例所示:
./format-string %d_%s
是否可以获取argc值?如果是,该怎么办?
解决方法
我试图在评论中要求一些澄清,但是您没有回答,所以我假设您在符合SYS V x86-64 ABI的环境中工作。
调用main
时,argc
位于rdi
中,但很快被调用strncpy
和printf
本身覆盖:
main:
sub rsp,40
mov rsi,QWORD PTR [rsi+8]
mov edx,32
mov rdi,rsp ;OOOPS
call strncpy
mov rdi,rsp ;OOOPS
xor eax,eax
call printf
xor eax,eax
add rsp,40
ret
上面的代码是compiled output of your sample program(一旦清除)。
但是,SYS V x86-64 ABI上的glibc
本身并不合成argc
(就像Windows的副本一样,请参见GetCommandLine等),此值创建程序时将其作为堆栈上的第一个值传递(请参见ABI规范的图3.9)。
因此,您可以使用printf
格式,通过使用%d
格式来跳过第一个 k -1个参数,即%k$d
,其中 k 是要找到的号码。
要找到k
,您只需找到调用rsp
时printf
与地址argc
之间的偏移量。
但是,由于argc
在创建进程时位于堆栈的底部,所以这等于在调用点rsp
的{{1}}与的初始值之间找到偏移量。 printf
。
因此使用gdb:
rsp
现在0x7fffffffdfa0-(0x7fffffffd9d8 + 8)= 0x110
0x110字节是34个参数(0x110 / 8 = 0x22),由于前四个参数在寄存器中,因此我们也需要跳过它们,加4。 最后,计数是基于1的,且包含差值,因此我们需要在计数上加2。 最终值为对于我的示例环境,为34 + 4 + 2 = 40,从而导致命令:
gdb --args format-string test
b _start
r
i r rsp
0x7fffffffdfa0 The initial value of RSP
b printf
c
i r rsp
0x7fffffffd9d8 The value AFTER printf is called. Add 8 to find it BEFORE the call
q
,
printf
使用的是系统v x86-64位ABI,该状态指出要传递给函数的所有参数要在寄存器rdi,rsi,rdx,rcx,r8,r9中传递,然后再传递其他值参数(如果存在)以相反的顺序堆栈,因此在您的情况下,您将需要传递多个%p
(取决于堆栈上已存在多少数据),并且我们使用%p
,因为我们希望将数据打印为64位值。简而言之,将多个%p
传递给printf将首先查看寄存器,然后查看存储在堆栈中的参数(从内存中读取)。
所以
%p%p%p%p%p%p%p%p%p /* will print registers values first extra %p will start to read up from stack (feel free to add as you want but keep in mind it will result in segmentation fault eventually if reached a specific area in memory but not sure when)*/
,
将%d
用于整数,将%s
用于字符串
#include <stdio.h>
int main (int argc,char **argv)
{
char buffer[32] = {0};
strncpy(buffer,argv[1],sizeof(buffer));
printf("argc = %d and argv[1] = %s\n",argc,buffer);
return 0;
}