如何在没有EXE或ELF等容器的情况下手动编写和执行PURE机器代码?

时间:2011-03-11 01:21:44

标签: machine-code

我只需要一个hello world演示来查看机器代码的实际工作方式。

虽然Windows' EXE 和linux' ELF 接近机器代码,但它不是 PURE

如何编写/执行 PURE 机器代码?

10 个答案:

答案 0 :(得分:21)

每个人都知道我们通常编写的应用程序是在操作系统上运行的。由它管理。

这意味着操作系统在机器上运行。所以我认为这就是你所说的PURE机器码。

因此,您需要研究操作系统的工作原理。

以下是一些引导扇区的NASM汇编代码,它可以在PURE中打印“Hello world”。

 org
   xor ax, ax
   mov ds, ax
   mov si, msg
boot_loop:lodsb
   or al, al 
   jz go_flag   
   mov ah, 0x0E
   int 0x10
   jmp boot_loop

go_flag:
   jmp go_flag

msg   db 'hello world', 13, 10, 0

   times 510-($-$$) db 0
   db 0x55
   db 0xAA

您可以在此处找到更多资源:http://wiki.osdev.org/Main_Page

END。

如果你已经安装了nasm并且有软盘,你可以

nasm boot.asm -f bin -o boot.bin
dd if=boot.bin of=/dev/fd0

然后,您可以从此软盘启动,您将看到该消息。 (注意:您应该首先使用软盘启动计算机。)

事实上,我建议您在完整的虚拟机中运行该代码,例如:bochs,virtualbox等。 因为很难找到带软盘的机器。

所以,步骤是 首先,您需要安装完整的虚拟机。 其次,通过推荐:bximage创建一个可视软盘 第三,将bin文件写入该可视软盘。 最后,从该可视软盘启动您的可视化机器。

注意:在https://wiki.osdev.org中,有一些关于该主题的基本信息。

答案 1 :(得分:19)

可以手动编写PURE机器代码没有组装

Linux / ELF:https://github.com/XlogicX/m2elf。这仍然是一项正在进行的工作,我昨天刚开始研究这个问题。

" Hello World"的源文件看起来像这样:

b8    21 0a 00 00   #moving "!\n" into eax
a3    0c 10 00 06   #moving eax into first memory location
b8    6f 72 6c 64   #moving "orld" into eax
a3    08 10 00 06   #moving eax into next memory location
b8    6f 2c 20 57   #moving "o, W" into eax
a3    04 10 00 06   #moving eax into next memory location
b8    48 65 6c 6c   #moving "Hell" into eax
a3    00 10 00 06   #moving eax into next memory location
b9    00 10 00 06   #moving pointer to start of memory location into ecx
ba    10 00 00 00   #moving string size into edx
bb    01 00 00 00   #moving "stdout" number to ebx
b8    04 00 00 00   #moving "print out" syscall number to eax
cd    80            #calling the linux kernel to execute our print to stdout
b8    01 00 00 00   #moving "sys_exit" call number to eax
cd    80            #executing it via linux sys_call

WIN / MZ / PE:

shellcode2exe.py(采用asciihex shellcode并创建一个合法的MZ PE exe文件)脚本位置:

http://zeltser.com/reverse-malware/shellcode2exe.py.txt

依赖性:

corelabs.coresecurity.com/index.php?module=Wiki&action=attachment&type=tool&page=InlineEgg&file=InlineEgg-1.08.tar.gz

提取

python setup.py build




sudo python setup.py install

答案 2 :(得分:14)

听起来你正在寻找旧的16-bit DOS .COM file format.COM文件的字节在程序段中偏移100h处加载(将它们限制为最大大小为64k - 256字节),并且CPU仅在偏移100h处开始执行。没有标题或任何类型的任何必需信息,只是原始CPU指令。

答案 3 :(得分:9)

操作系统没有运行指令,CPU会这样做(除非我们谈论的是虚拟机操作系统,确实存在,我正在考虑Forth或类似的东西)。然而,操作系统确实需要一些元信息才能知道,文件确实包含可执行代码,以及它希望环境看起来如何。 ELF不仅仅是接近机器代码。它机器代码,以及操作系统的一些信息,知道它应该让CPU实际执行那个东西。

如果你想要比ELF更简单的东西,但是* nix,看看a.out格式,这要简单得多。传统上,如果没有指定输出名称,* nix C编译器会(仍)将其可执行文件写入名为a.out的文件中。

答案 4 :(得分:2)

在Windows上 - 至少32位Windows - 您可以使用.com文件执行RAW INSTRUCTIONS。

例如,如果您使用此字符串并将其保存在带有.com扩展名的记事本中:

X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

它将打印一个字符串并启动您的防病毒软件。

答案 5 :(得分:2)

真实机器代码

您需要运行的测试:Linux x86或x64(在我的情况下,我正在使用Ubuntu x64)

开始吧

此程序集(x86)将值666移到eax寄存器中:

movl $666, %eax
ret

让我们对其进行二进制表示:

操作码 movl (movl是操作数大小为32的mov),二进制为1011

二进制指令宽度 = 1

以二进制形式注册 eax = 000

带符号的32位二进制数中的

数字 666 是= 00000000 00000000 00000010 10011010

666 转换为 little endian 是= 10011010 00000010 00000000 00000000

二进制指令 ret (返回)= 11000011

所以最终我们的纯二进制指令将如下所示:

1011(movl)1(width)000(eax)10011010000000100000000000000000(666) 11000011(ret)

将它们放在一起:

1011100010011010000000100000000000000000
11000011

要执行该二进制代码,必须将其置于具有执行特权的内存页中,我们可以使用以下C代码来做到这一点:

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

/* Allocate size bytes of executable memory. */
unsigned char *alloc_exec_mem(size_t size)
{
    void *ptr;

    ptr = mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC,
               MAP_PRIVATE | MAP_ANON, -1, 0);

    if (ptr == MAP_FAILED) {
            perror("mmap");
            exit(1);
    }

    return ptr;
}

/* Read up to buffer_size bytes, encoded as 1's and 0's, into buffer. */
void read_ones_and_zeros(unsigned char *buffer, size_t buffer_size)
{
    unsigned char byte = 0;
    int bit_index = 0;
    int c;

    while ((c = getchar()) != EOF) {
            if (isspace(c)) {
                    continue;
            } else if (c != '0' && c != '1') {
                    fprintf(stderr, "error: expected 1 or 0!\n");
                    exit(1);
            }

            byte = (byte << 1) | (c == '1');
            bit_index++;

            if (bit_index == 8) {
                    if (buffer_size == 0) {
                            fprintf(stderr, "error: buffer full!\n");
                            exit(1);
                    }
                    *buffer++ = byte;
                    --buffer_size;
                    byte = 0;
                    bit_index = 0;
            }
    }

    if (bit_index != 0) {
            fprintf(stderr, "error: left-over bits!\n");
            exit(1);
    }
}

int main()
{
    typedef int (*func_ptr_t)(void);

    func_ptr_t func;
    unsigned char *mem;
    int x;

    mem = alloc_exec_mem(1024);
    func = (func_ptr_t) mem;

    read_ones_and_zeros(mem, 1024);

    x = (*func)();

    printf("function returned %d\n", x);

    return 0;
}

来源:https://www.hanshq.net/files/ones-and-zeros_42.c

我们可以使用以下代码进行编译:

gcc source.c -o binaryexec

要执行它:

./binaryexec

然后我们通过第一组指令:

1011100010011010000000100000000000000000

按Enter

并传递返回指令:

11000011

按Enter

最后按ctrl + d结束程序并获取输出:

  

函数返回666

答案 6 :(得分:1)

当定位嵌入式系统时,您可以制作rom或ram的二进制图像,严格来说是程序中的指令和相关数据。并且经常可以将该二进制文件写入flash / rom并运行它。

操作系统希望了解更多信息,开发人员通常希望在文件中留下更多信息,以便以后可以调试或使用其他操作(使用一些可识别的符号名称进行反汇编)。此外,嵌入式或在操作系统上,您可能需要将.data中的.text与.bata中的.data等分开。文件格式如.elf提供了一种机制,首选用例是加载一些精灵无论是操作系统还是用微控制器的rom和ram编程的东西。

.exe也有一些标题信息。如上所述.com没有将它加载到地址0x100h并在那里分支。

从可执行文件创建原始二进制文件,使用gcc创建的elf文件,例如,您可以执行类似

的操作

objcopy file.elf -O binary file.bin

如果程序是分段的(。text,.data等)并且这些段不是背对背的,那么二进制文件可能会变得非常大。再次使用embedded作为示例,如果rom位于0x00000000且data或bss位于0x20000000,即使您的程序只有4个字节的数据,objcopy将创建一个0x20000004字节文件,填补.text和.data之间的空白(因为它应该因为这就是你要求它做的事情。)

你想做什么?读取elf或intel hex或srec文件非常简单,从中可以看到二进制文件的所有位和字节。或者拆解精灵或其他什么也会以人类可读的形式向您展示。 (objdump -D file.elf&gt; file.list)

答案 7 :(得分:1)

使用纯机器代码,您可以使用任何能够写入文件的语言。 即使是visual basic.net也可以在写入时在int类型之间进行交换时写入8,16,32,64位。

您甚至可以根据需要设置让vb在循环中写出机器代码 对于像setpixel这样的东西,其中x,y会发生变化,你会得到你的argb颜色。

或者,在Windows中定期创建您的vb.net程序,并使用NGEN.exe制作程序的本机代码文件。它创建了特定于ia-32的纯机器代码,一次性抛出JIT调试器。

答案 8 :(得分:0)

这是很好的回答,但为什么有人想这样做可能会更好地指导答案。我认为最重要的原因是要完全控制他们的机器,特别是在其缓存写入时,以获得最大性能,并防止任何操作系统共享处理器或虚拟化代码(从而减慢速度)或特别是在这些天窥探你的代码也是如此。据我所知,汇编程序无法处理这些问题,M $ /英特尔和其他公司将此视为侵权或黑客攻击。&#34;然而,这是非常错误的。如果您的汇编程序代码被移交给操作系统或专有硬件,那么真正的优化(可能是GHz频率)将是遥不可及的。对于科学和技术而言,这是一个非常重要的问题,因为如果没有硬件优化,我们的计算机就无法充分发挥它们的潜力,并且通常比它低几个数量级。可能有一些解决方法或一些开源硬件可以实现这一点,但我还没有找到它。佩妮为任何人的想法。

答案 9 :(得分:0)

下一个程序是我用机器代码16位(intel 8086)编写的Hello World程序,如果您想了解机器代码,建议您首先学习汇编语言,因为汇编语言中的每一行代码都转换为A机器代码中的代码行。众所周知,我来自世界上少数几个人,他们仍然使用机器代码编程,而不是汇编语言。

顺便说一句,要运行它,请以“ .com”扩展名保存文件并在DOSBOX上运行!

So, this is an Hello World Program.