真实机器码
运行测试需要什么:Linux x86 或 x64(在我的情况下,我使用的是 Ubuntu x64)
开始吧
这个程序集 (x86) 将值 666 移动到 eax 寄存器中:
movl $666, %eax
ret
让我们对其进行二进制表示:
操作码 movl(movl 是一个操作数大小为 32 的 mov)在二进制中是 = 1011
指令 width 二进制是 = 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
按回车
并传递返回指令:
11000011
按回车
最后ctrl+d结束程序并得到输出:
函数返回 666