【问题标题】:Inline assembly and function overwriting resulting in a segfault内联汇编和函数覆盖导致段错误
【发布时间】:2010-12-03 13:01:39
【问题描述】:

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:在函数“main”中: encode.c:13:警告:从不兼容的指针类型初始化 C:\DOCUME~1\Aviral\LOCALS~1\Temp/ccYaOZhn.o:encode.c:(.text+0xf): 未定义对“文本”的引用 C:\DOCUME~1\Aviral\LOCALS~1\Temp/ccYaOZhn.o:encode.c:(.text+0x14): undefined reference to `printf' collect2: ld 返回 1 个退出状态

我的第一个问题是为什么内联汇编失败了......正确的方法是什么?另一件事——“ret”或“retn”的代码是 0x00 ,对……我的代码 xor 的东西直到它返回……那为什么它是 SEGFAULTing?

【问题讨论】:

    标签: c obfuscation inline-assembly


    【解决方案1】:

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

    这是您的顶级功能的正确实现:

    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 是指令长度在 1-16 字节之间的 CISC 架构,因此尝试解析 x86 指令特别困难) )

    另外说明,并非所有操作系统都允许修改文本/代码段(存储可执行指令的位置),因此您在 main 中的代码可能无法正常工作。

    【讨论】:

      【解决方案2】:

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

      这是一个例子:

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

      因此,您的问题是无法从您的电话中识别“文本”(并且也遵循说明)。

      参考here

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

      您的问题的完整解决方案在 GCC 邮件列表上的 this 帖子上。 这是一个sn-p:

      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" : /* No outputs */ : "g" (method-&gt;args_size * 4) ); 要做。谷歌了解更多详情。

      【讨论】:

      • 该示例是一个巨大的 hack,并且取决于优化器行为(不引用任何与 ESP 相关的本地变量),甚至两者都没有为 call 使用相同的 asm 语句并获得返回值。它应该只在执行调用的 asm 语句中使用 "=a"(ret_value)` 输出。 (当然它必须是asm volatile,因为只有没有输出的 asm 语句是隐式易失的)。这也无法在调用后修复 ESP,因此它完全被破坏,除非被调用者是 Windows stdcall 或其他一些被调用者-pops 约定。
      【解决方案3】:

      不是 printf 而是 _printf

      【讨论】:

        猜你喜欢
        • 2019-08-06
        • 2010-10-29
        • 1970-01-01
        • 1970-01-01
        • 2020-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多