How to write and execute PURE machine code manually without containers like EXE or ELF?

后端 未结 10 1638
天涯浪人
天涯浪人 2020-11-30 18:38

I just need a hello world demo to see how machine code actually works.

Though windows\' EXE and linux\' ELF is near machine code,bu

相关标签:
10条回答
  • 2020-11-30 19:25

    It sounds like you're looking for the old 16-bit DOS .COM file format. The bytes of a .COM file are loaded at offset 100h in the program segment (limiting them to a maximum size of 64k - 256 bytes), and the CPU simply started executing at offset 100h. There are no headers or any required information of any kind, just raw CPU instructions.

    0 讨论(0)
  • 2020-11-30 19:30

    You can write in PURE machine code manually WITHOUT ASSEMBLY

    Linux/ELF: https://github.com/XlogicX/m2elf. This is still a work in progress, I just started working on this yesterday.

    Source file for "Hello World" would look like this:

    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 (takes asciihex shellcode and creates a legit MZ PE exe file) script location:

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

    dependency:

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

    extract

    python setup.py build
    
    
    
    
    sudo python setup.py install
    
    0 讨论(0)
  • 2020-11-30 19:33

    The OS is not running the instructions, the CPU does (except if we're talking about a virtual machine OS, which do exist, I'm thinking about Forth or such things). The OS however does require some metainformation to know, that a file does in fact contain executable code, and how it expects its environment to look like. ELF is not just near machine code. It is machine code, together with some information for the OS to know that it's supposed to put the CPU to actually execute that thing.

    If you want something simpler than ELF but *nix, have a look at the a.out format, which is much simpler. Traditionally *nix C compilers do (still) write their executable to a file called a.out, if no output name is specified.

    0 讨论(0)
  • 2020-11-30 19:34

    Real Machine Code

    What you need to run the test: Linux x86 or x64 (in my case I am using Ubuntu x64)

    Let's Start

    This Assembly (x86) moves the value 666 into the eax register:

    movl $666, %eax
    ret
    

    Let's make the binary representation of it:

    Opcode movl (movl is a mov with operand size 32) in binary is = 1011

    Instruction width in binary is = 1

    Register eax in binary is = 000

    Number 666 in signed 32 bits binary is = 00000000 00000000 00000010 10011010

    666 converted to little endian is = 10011010 00000010 00000000 00000000

    Instruction ret (return) in binary is = 11000011

    So finally our pure binary instructions will look like this:

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

    Putting it all together:

    1011100010011010000000100000000000000000
    11000011
    

    For executing it the binary code has to be placed in a memory page with execution privileges, we can do that using the following C code:

    #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;
    }
    

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

    We can compile it using:

    gcc source.c -o binaryexec

    To execute it:

    ./binaryexec

    Then we pass the first sets of instructions:

    1011100010011010000000100000000000000000

    press enter

    and pass the return instruction:

    11000011

    press enter

    finally ctrl+d to end the program and get the output:

    function returned 666

    0 讨论(0)
提交回复
热议问题