16 July 2022
Some months ago I was trying to build a very simple JIT compiler in Zig. I eventually gave it up since building a decent one is quite a challenging task, even though I was glad that I learned a bit more about how a JIT really works.
Today I found out this little piece of code for executing a shellcode in Zig: I remember I had to ask how to do it since I couldn't figure it out from the stdlib documentation, so I will share it here in the hope it is useful to others.
// shellcode.zig
const std = @import("std");
const shellcode: []const u8 = &.{
0x6a, 0x42, 0x58, 0xfe, 0xc4, 0x48, 0x99, 0x52, 0x48, 0xbf,
0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, 0x73, 0x68, 0x57, 0x54,
0x5e, 0x49, 0x89, 0xd0, 0x49, 0x89, 0xd2, 0x0f, 0x05,
};
pub fn main() !void {
const aligned_length = std.mem.alignForward(shellcode.len, std.mem.page_size);
const pcode = try std.heap.page_allocator.allocAdvanced(u8, std.mem.page_size, aligned_length, .exact);
std.mem.copy(u8, pcode, shellcode);
try std.os.mprotect(pcode, 7); // 7 means READ + WRITE + EXEC
const ptr = @ptrCast(fn () void, pcode.ptr);
ptr();
}
You can just try it out by executing
zig run shellcode.zig
Just remember that the shellcode is for linux only (it should be execve /bin/sh
).