SPOJ中存在一系列问题,即在一行中创建具有一些约束的函数。我已经解决了简单,中等和难的问题,但是对于impossible我已经解决了错误答案。
总结一下,问题请求填写return语句的代码,如果x为1,则返回值应为2.对于其他x值,它应返回3.约束是字母无法使用'x',也无法添加更多代码;一个人只能编写返回语句。显然,要解决这个问题,必须创建一个黑客。
所以我使用gcc的内置方式来获取堆栈帧,然后减少指针以获取指向第一个参数的指针。除此之外,该陈述只是一个正常的比较。
在我的机器上它工作正常,但对于在线裁判使用的集群(Intel Pentinum G860),它不起作用,可能是由于不同的调用约定。我不确定我是否理解处理器的ABI(我不确定堆栈帧指针是保存在堆栈上还是只保存在寄存器上),或者即使我正在读取正确的ABI。
问题是:使用堆栈获取函数的第一个参数的正确方法是什么?
我的代码是(它必须以这种方式格式化,否则不被接受):
#include <stdio.h>
int count(int x){
return (*(((int*)__builtin_frame_address(0))-1) == 1) ? 2 : 3;
}
int main(i){
for(i=1;i%1000001;i++)
printf("%d %d\n",i,count(i));
return 0;
}
答案 0 :(得分:2)
问题是:获得第一个的正确方法是什么 使用堆栈的函数的参数?
没有办法以便携的方式。您必须假定特定的编译器,其设置和ABI以及调用约定。
gcc
编译器可能到&#34;放下&#34; int
局部变量-0x4
偏移量(假设sizeof(int) == 4
)。您可能会观察count
的最基本定义:
4 {
0x00000000004004c4 <+0>: push %rbp
0x00000000004004c5 <+1>: mov %rsp,%rbp
0x00000000004004c8 <+4>: mov %edi,-0x4(%rbp)
5 return x == 1 ? 2 : 3;
0x00000000004004cb <+7>: cmpl $0x1,-0x4(%rbp)
0x00000000004004cf <+11>: jne 0x4004d8 <count+20>
0x00000000004004d1 <+13>: mov $0x2,%eax
0x00000000004004d6 <+18>: jmp 0x4004dd <count+25>
0x00000000004004d8 <+20>: mov $0x3,%eax
6 }
0x00000000004004dd <+25>: leaveq
0x00000000004004de <+26>: retq
您可能还会看到%edi
寄存器包含第一个参数。这是AMD64 ABI的情况(调用之间也不保留%edi
。)
现在,凭借这些知识,您可能会写出类似的内容:
int count(int x)
{
return *((int*)(__builtin_frame_address(0) - sizeof(int))) == 1 ? 2 : 3;
}
可以混淆为:
return *((int*)(__builtin_frame_address(0)-sizeof(int)))==1?2:3;
然而,诀窍是这样的optimizing compiler可能会热情地假设x
中没有引用count
,它可能只是跳过进入堆栈。例如,它使用-O
标志生成以下程序集:
4 {
0x00000000004004c4 <+0>: push %rbp
0x00000000004004c5 <+1>: mov %rsp,%rbp
5 return *((int*)(__builtin_frame_address(0)-sizeof(int)))==1?2:3;
0x00000000004004c8 <+4>: cmpl $0x1,-0x4(%rbp)
0x00000000004004cc <+8>: setne %al
0x00000000004004cf <+11>: movzbl %al,%eax
0x00000000004004d2 <+14>: add $0x2,%eax
6 }
0x00000000004004d5 <+17>: leaveq
0x00000000004004d6 <+18>: retq
正如您所看到的mov %edi,-0x4(%rbp)
指令现在缺失,因此 1 唯一的方法是从x
寄存器访问%edi
的值:
int count(int x)
{
return ({register int edi asm("edi");edi==1?2:3;});
}
但是这种方法缺乏混淆&#34;的能力,因为变量声明需要空格,其值为%edi
。
1)不一定。即使编译器决定从寄存器到堆栈跳过mov
操作,仍然有可能强制&#34;强制&#34;它是通过asm("mov %edi,-0x4(%rbp)");
内联汇编来实现的。但要注意,编译器迟早会报复。
功能
答案 1 :(得分:0)
C标准在任何实现中都不需要堆栈,所以你的问题确实没有任何意义。
在gcc的上下文中,x86和x86-64(以及其他任何内容)的行为不同。
在x86中,参数驻留在堆栈中,但在x86-64中,前6个参数(包括隐式参数)驻留在寄存器中。所以基本上你不能像你说的那样做黑客攻击。
如果你想破解代码,你需要指定你想要运行的平台,否则,没有必要回答你的问题。