【问题标题】:C function from assembler code as a char string汇编代码中的 C 函数作为 char 字符串
【发布时间】:2020-08-29 20:02:18
【问题描述】:

我只是想知道如何在汇编程序中生成随机数,我发现来自俄罗斯堆栈溢出的问题,人们问的不是如何在汇编程序中生成随机数,而是如何使用_asm{} 在 c 代码中实现它。 The answer 发布到他的问题让我感到惊讶(翻译成英文):

char r[]="!!!!!!!!!!!№№№№№№№№№№№;;;;;;;;;;;;;;;;;;;;;;;;;55555555555555666666666666666666666666666777777777777777777777777777777777777777777777777777777777777";  // String, which length should be calculated

main()
{
    static unsigned long  (__cdecl *lenstr)(char*); // Pointer to function declaration. The method for passing parameters must be defined explicitly - it is different in different compilers

    static int i=0;
        if(!i)
            {
                static char s[]={
                    0x5a,
                    //pop %%edx
                    0x5f,
                    //pop %%edi
                    0xfc,
                    //cld
                    0x31,0xc9,
                    //xor %%ecx,%%ecx
                    0x31,0xc0,
                    //xor %%eax,%%eax
                    0x49,
                    //dec %%ecx
                    0xf2,0xae,
                    //repne scasв
                    0xf7,0xd1,
                    //not %%ecx
                    0x49,
                    //dec %%ecx
                    0x91,
                    //xchg %%eax,%%ecx
                    0x52,
                    //push %%edx
                    0xc3
                    //ret
                    }; // Array with assembler code
                lenstr=(unsigned long ( __cdecl *)(char*))&s; // Linking function pointer to to that array
                i=1;
            }               

            printf("%s%c%d%c%s\n","String length",' ',lenstr(r),' ',"symbols");
}

两个问题:

  1. 将汇编代码作为强制转换的 char 数组放入函数指针的机会存在多长时间?为什么要开发它?
  2. 我不明白:计算字符串长度是一种智能的随机数生成方法,还是只是机器代码转换为指针的示例?

【问题讨论】:

  • 这不适用于大多数 x86-64 操作系统;非常量静态数据应该在没有 exec 权限的读写页面中。您需要额外的技巧,例如适用于 Windows 的 VirtualProtect。此外,这看起来像是一个缓慢但很小的 strlen,而不是 PRNG 步骤。

标签: c++ c assembly random


【解决方案1】:

关于代码示例

将第二个答案的文本从your link 粘贴给翻译给我:

您可以使机器代码位于数组中。下面介绍如何编写程序来计算字符串中的字符数。

所以这只是一个关于如何在 C 程序中使用汇编代码的示例。可以使用__asm,但很多人不喜欢那里的语法。因此,汇编源代码首先在外部进行汇编(例如使用 NASM 或 FASM),然后将生成的机器代码作为 char 数组嵌入到 C 程序中。

使代码可执行

正如 Peter Cordes 已经提到的,在数据​​部分(该 char 数组存储在程序中)内执行代码几乎是不可能的。无论如何,有两种方法可以执行代码:要么必须设置适当的编译器设置(以使数据段可执行),要么必须分配额外的可执行内存。

例如在 Linux 下,你可以使用mmap 请求这样的存储,然后将代码复制过来:

void* executableStorage = mmap(NULL, sizeof(executableCode),
                               PROT_EXEC | PROT_READ | PROT_WRITE,
                               MAP_PRIVATE, 0, 0);
memcpy(executableStorage, executableCode, sizeof(executableCode));

在 Windows 下,可以使用 GlobalAlloc 完成类似的操作,例如,它总是返回一个可执行的内存区域。

随机汇编器

链接问题的第一个答案是关于随机数:

最简单的选择是实现一个线性全等生成器:

R1 = (a * R0 + b) mod M

这里ab是常数系数(被选中),M是模数,最大值为伪随机数(最小值为0),R0是结果之前对生成器的调用(对于第一次调用,您可以替换任何数字)。

Linear-feedback shift registers 是另一种轻松生成伪随机数的方法。

为什么会有这个机会?

它并不是 C 的真正功能。由于 C 非常接近硬件,所以一切都可以解释为数据、指针甚至程序代码。通过使用某种数据类型进行强制转换,您可以在它们之间进行切换。因此,这种可能性可能也会在某处进行描述。反过来,C 代码也可以解释为数据:

#include <stdio.h>

int main() {
    // Interpret the main function (program code) as data
    unsigned char* data = (unsigned char*) main;

    // Print out some machine code of the main-function
    for (int i=0; i<64; i++) {
        printf("%02X ", data[i]);

        if ((i & 15) == 15)
            printf("\n");
    }
}

所以 C 提供了这些可能性。它们是否也被允许不是 C 语言的问题。主要由操作系统提供的安全机制可以使内存区域,从而使这些数据受到写保护或不可执行。

由于这些安全机制,使用 char 数组的方式不再实用。它更像是一种快速而肮脏的解决方案,它是一种糟糕的编程风格并且不切实际:每次更改汇编代码时,都必须手动将其转移到 C 程序中。通常你会在一个单独的文件中编写汇编代码,然后将汇编的目标文件与 C 目标文件链接:

   assembly                 object              executable
 source code                 files                program
               assembler               linker
ASSEMBLY.asm ────────────> ASSEMBLY.o ───┬───>  ./PROGRAM
                                         │
              c-compiler                 │
   PROGRAM.c ────────────> PROGRAM.o  ───┘

c source code

【讨论】:

  • 该示例可以在没有 PAE 页表的古老 32 位 Windows 上运行;在此之前,没有方便的方法将页面标记为可读但不可执行。所以也许它是从非常古老的东西中复制/粘贴的。还值得指出的是,如果您想要的话,可以使用_asm{};它的性能很糟糕,但它并不比手动组装成一个 char 数组差,只要你正在制作 32 位构建(或使用 clang 或 ICC,而不是 MSVC)。
  • 日期为 2014 年 2 月 6 日。
  • 海报可能是从其他地方复制/粘贴的,或者是从旧笔记中挖出来的,而不检查其有效性。还是只在运行 Win2k 或 Win98 的 32 位 VM 中测试内联 asm?不知道。或者,如果他们发布了一个无效的示例并且没有提及如何实际使 static char[] 数组可执行,那么这可能只是一个糟糕的答案。 (我不懂俄语,也没有尝试过自动翻译。)
  • 感谢您的反馈。从您的回答中,我理解了唯一的主要思想 - 将代码放在位于堆栈中的 char 数组中是非常错误的,因为大多数编译器不支持或堆栈溢出。因此,如果我只是制作,例如 char *code = new char[1024] 就可以了。
  • @Artur 区分内存的不同部分(例如堆栈、堆和代码)只是为了让程序员更轻松的约定。对于计算机/CPU 来说,这只是“内存”,因此程序员可以随意滥用约定。这些(ab)使用在历史上更容易,因为保护的成本/收益不那么明显,但现在非执行和 ASLR 等工具很常见,可以防止攻击者
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-09
  • 1970-01-01
  • 2014-11-30
  • 1970-01-01
  • 2012-02-25
相关资源
最近更新 更多