内联汇编和函数覆盖导致段错误

发布于 2024-08-07 01:27:25 字数 829 浏览 8 评论 0原文

SO 的有人发布了一个问题,询问他如何“隐藏”一个函数。这是我的答案:

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

int encrypt(void)
{
  char *text="Hello World";
  asm("push text");
  asm("call printf");
  return 0;
}

int main(int argc, char *argv[])
{
  volatile unsigned char *i=encrypt;
  while(*i!=0x00)
    *i++^=0xBE;
  return EXIT_SUCCESS;
}

但是,存在问题:

encode.c: In function `main':
encode.c:13: warning: initialization from incompatible pointer type
C:\DOCUME~1\Aviral\LOCALS~1\Temp/ccYaOZhn.o:encode.c:(.text+0xf): undefined reference to `text'
C:\DOCUME~1\Aviral\LOCALS~1\Temp/ccYaOZhn.o:encode.c:(.text+0x14): undefined reference to `printf'
collect2: ld returned 1 exit status

我的第一个问题是为什么内联汇编失败......正确的方法是什么?其他的事情——“ret”或“retn”的代码是 0x00 ,对吧...我的代码异或的东西直到它到达返回...那么为什么它会SEGFAULTing?

Somebody over at SO posted a question asking how he could "hide" a function. This was my answer:

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

int encrypt(void)
{
  char *text="Hello World";
  asm("push text");
  asm("call printf");
  return 0;
}

int main(int argc, char *argv[])
{
  volatile unsigned char *i=encrypt;
  while(*i!=0x00)
    *i++^=0xBE;
  return EXIT_SUCCESS;
}

but, there are problems:

encode.c: In function `main':
encode.c:13: warning: initialization from incompatible pointer type
C:\DOCUME~1\Aviral\LOCALS~1\Temp/ccYaOZhn.o:encode.c:(.text+0xf): undefined reference to `text'
C:\DOCUME~1\Aviral\LOCALS~1\Temp/ccYaOZhn.o:encode.c:(.text+0x14): undefined reference to `printf'
collect2: ld returned 1 exit status

My first question is why is the inline assembly failing ... what would be the right way to do it? Other thing -- the code for "ret" or "retn" is 0x00 , right... my code xor's stuff until it reaches a return ... so why is it SEGFAULTing?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

转瞬即逝 2024-08-14 01:27:25

作为一个高层次的观点,我不太确定为什么您要尝试使用内联汇编对 printf 进行简单的调用,因为您所做的只是创建函数调用的错误版本(您的内联将某些内容推送到堆栈,但永远不会将其弹出,很可能会导致问题,因为 GCC 不知道您已在函数中间修改了堆栈指针,这在一个简单的示例中很好,但可能会导致不明显的错误。在一个更复杂的函数中)

这是您的 top 函数的正确实现:

int encrypt(void)
{
  char *text="Hello World";
  char *formatString = "%s\n";
  // volatile really isn't necessary but I just use it by habit
  asm volatile("pushl %0;\n\t"
               "pushl %1;\n\t"
            "call printf;\n\t"
               "addl $0x8, %%esp\n\t"          
               : 
               : "r"(text), "r"(formatString)
               );

  return 0;
}

至于您的最后一个问题,RET 的常用操作码是“C3”,但有很多变体,请查看 http://pdos.csail.mit.edu/6.828/2009/readings/i386/RET.htm
您搜索 RET 的想法也是错误的,因为当您在随机指令集中看到字节 0xC3 时,并不意味着您遇到了 ret。由于 0xC3 可能只是另一条指令的数据/属性(作为旁注,尝试解析 x86 指令特别困难,因为 x86 是 CISC 架构,指令长度在 1-16 字节之间)

另一个注意事项是,并非所有操作系统都允许修改文本/代码段(存储可执行指令的位置),因此您在 main 中的代码可能无论如何都无法工作。

As a high level point, I'm not quite sure why you're trying to use inline assembly to do a simple call into printf, as all you've done is create an incorrect version of a function call (your inline pushes something onto the stack, but never pop it off, most likely causing problems cause GCC isn't aware that you've modified the stack pointer in the middle of the function. This is fine in a trivial example, but could lead to non-obvious errors in a more complicated function)

Here's a correct implementation of your top function:

int encrypt(void)
{
  char *text="Hello World";
  char *formatString = "%s\n";
  // volatile really isn't necessary but I just use it by habit
  asm volatile("pushl %0;\n\t"
               "pushl %1;\n\t"
            "call printf;\n\t"
               "addl $0x8, %%esp\n\t"          
               : 
               : "r"(text), "r"(formatString)
               );

  return 0;
}

As for your last question, the usual opcode for RET is "C3", but there are many variations, have a look at http://pdos.csail.mit.edu/6.828/2009/readings/i386/RET.htm
Your idea of searching for RET is also faulty as due to the fact that when you see the byte 0xC3 in a random set of instructions, it does NOT mean you've encountered a ret. As the 0xC3 may simply be the data/attributes of another instruction (as a side note, it's particularly hard to try and parse x86 instructions as you're doing due to the fact x86 is a CISC architecture with instruction lengths between 1-16 bytes)

As another note, not all OS's allow modification to the text/code segment (Where executable instructions are stored), so the the code you have in main may not work regardless.

狠疯拽 2024-08-14 01:27:25

GCC 内联汇编使用 AT&T 语法(如果没有选择使用 Intel 语法的特定选项)。

这是一个示例:

 int a=10, b;
 asm ("movl %1, %%eax; 
       movl %%eax, %0;"
      :"=r"(b)        /* output */
      :"r"(a)         /* input */
      :"%eax"         /* clobbered register */
      );       

因此,您的问题是无法从您的呼叫(以及遵循的指令)中识别“文本”。

请参阅此处以供参考。

此外,您的代码不可在 32 位和 64 位环境之间移植。使用 -m32 标志编译它以确保正确分析(如果出错,GCC 无论如何都会抱怨)。

您的问题的完整解决方案位于帖子在海湾合作委员会邮件列表上。
下面是一个片段:

for ( i = method->args_size - 1; i >= 0; i-- ) {
    asm( "pushl %0": /* no outputs */: \
         "g" (stack_frame->op_stack[i]) );
}

asm( "call *%0" : /* no outputs */ : "g" (fp) :
     "%eax", "%ecx", "%edx", "%cc", "memory" );

asm ( "movl %%eax, %0" : "=g" (ret_value) : /* No inputs */ );

在 Windows 系统上,还有一个附加的 asm ( "addl %0, %%esp" : /* 无输出 */ : "g" (method->args_size * 4) ); 去做。谷歌了解更好的细节。

GCC inline asm uses AT&T syntax (if no specific options are selected for using Intel's one).

Here's an example:

 int a=10, b;
 asm ("movl %1, %%eax; 
       movl %%eax, %0;"
      :"=r"(b)        /* output */
      :"r"(a)         /* input */
      :"%eax"         /* clobbered register */
      );       

Thus, your problem is that "text" is not identifiable from your call (and following instruction too).

See here for reference.

Moreover your code is not portable between 32 and 64 bit environments. Compile it with -m32 flag to ensure proper analysis (GCC will complain anyway if you fall in error).

A complete solution to your problem is on this post on GCC Mailing list.
Here's a snippet:

for ( i = method->args_size - 1; i >= 0; i-- ) {
    asm( "pushl %0": /* no outputs */: \
         "g" (stack_frame->op_stack[i]) );
}

asm( "call *%0" : /* no outputs */ : "g" (fp) :
     "%eax", "%ecx", "%edx", "%cc", "memory" );

asm ( "movl %%eax, %0" : "=g" (ret_value) : /* No inputs */ );

On windows systems there's also an additional asm ( "addl %0, %%esp" : /* No outputs */ : "g" (method->args_size * 4) ); to do. Google for better details.

缱倦旧时光 2024-08-14 01:27:25

它不是 printf 而是 _printf

It is not printf but _printf

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文