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
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
#include
#include
#include
/* 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