I've been stuck with this problem for some days, and still haven't manage to fix it. Basically, I want to do a remote syscall from an attacker program to the target. But before showing the code, I think it'd be a good idea to present my thought process, as the problem could be anything at this point. I am doing this remote syscall through the following steps:
I am using my memory lib to parse the mmap files, get process id and process information, etc. As far as I am concerned, it is working properly. In any case, here's the source: https://github.com/rdbo/libmem
And the code I am using to do the call:
mem_voidptr_t allocate_ex(mem_process_t process, mem_size_t size, mem_alloc_t allocation)
{
mem_voidptr_t alloc_addr = (mem_voidptr_t)MEM_BAD_RETURN;
if(!mem_process_is_valid(&process)) return alloc_addr;
int status;
int mmap_syscall = __NR_mmap;
struct user_regs_struct old_regs, regs;
mem_byte_t injection_buf[] =
{
0x0f, 0x05, //syscall
0xcc //int3
};
//Parse /proc/<process.pid>/maps to get executable region
char path_buffer[64];
snprintf(path_buffer, sizeof(path_buffer), "/proc/%i/maps", process.pid);
int fd = open(path_buffer, O_RDONLY);
if(fd == -1) return alloc_addr;
int read_check = 0;
mem_size_t file_size = 0;
mem_string_t file_buffer = mem_string_init();
for(char c; (read_check = read(fd, &c, 1)) != -1 && read_check != 0; file_size++)
{
mem_string_resize(&file_buffer, file_size);
mem_string_c_set(&file_buffer, file_size, c);
}
mem_size_t injection_address_pos, injection_address_end;
mem_string_t injection_address_str = mem_string_init();
mem_voidptr_t injection_address = (mem_voidptr_t)MEM_BAD_RETURN;
injection_address_pos = mem_string_find(&file_buffer, "r-xp", 0);
injection_address_pos = mem_string_rfind(&file_buffer, "\n", injection_address_pos);
if(injection_address_pos == file_buffer.npos) return alloc_addr;
injection_address_end = mem_string_find(&file_buffer, "-", injection_address_pos);
injection_address_str = mem_string_substr(&file_buffer, injection_address_pos, injection_address_end);
injection_address = (mem_voidptr_t)strtoull(mem_string_c_str(&injection_address_str), NULL, 16);
if(injection_address == (mem_voidptr_t)MEM_BAD_RETURN || injection_address == (mem_voidptr_t)0)
return alloc_addr;
printf("Injection address: %p\n", injection_address);
//Store the old data at 'injection_address' and write the injection buffer to it
mem_byte_t old_data[sizeof(injection_buf)];
mem_ex_read(process, injection_address, (mem_voidptr_t)old_data, sizeof(old_data));
mem_ex_write(process, injection_address, (mem_voidptr_t)injection_buf, sizeof(injection_buf));
//Attach to process and store current registers
ptrace(PTRACE_ATTACH, process.pid, NULL, NULL);
ptrace(PTRACE_GETREGS, process.pid, NULL, &old_regs);
memcpy(®s, &old_regs, sizeof(regs));
//Setup syscall registers
regs.rax = mmap_syscall; //syscall number
regs.rdi = 0; //address (arg0)
regs.rsi = size; //length (arg1)
regs.rdx = allocation.protection; //protection (arg2)
regs.r10 = allocation.type; //flags (arg3)
regs.r8 = -1; //fd (arg4)
regs.r9 = 0; //offset (arg5)
regs.rip = (unsigned long long)injection_address; //next instruction to execute
//Call mmap on external process
ptrace(PTRACE_SETREGS, process.pid, NULL, ®s);
ptrace(PTRACE_CONT, process.pid, NULL, NULL);
waitpid(process.pid, &status, WSTOPPED);
//Get the registers after syscall to store the return of mmap
ptrace(PTRACE_GETREGS, process.pid, NULL, ®s);
alloc_addr = (mem_voidptr_t)regs.rax; //store the return of mmap
//Restore the original buffer at 'injection_address'
mem_ex_write(process, injection_address, (mem_voidptr_t)old_data, sizeof(old_data));
//Continue the original execution
ptrace(PTRACE_SETREGS, process.pid, NULL, &old_regs);
ptrace(PTRACE_CONT, process.pid, NULL, NULL);
//Return allocation address, if valid
if((mem_uintptr_t)alloc_addr >= (mem_uintptr_t)-2048)
alloc_addr = (mem_voidptr_t)MEM_BAD_RETURN;
return alloc_addr;
}
and the main function of the attacker program:
int main()
{
mem_pid_t pid = mem_ex_get_pid(mem_string_new("target"));
mem_process_t process = mem_ex_get_process(pid);
int buffer = 10;
mem_alloc_t allocation = mem_alloc_init();
allocation.protection = PROT_READ | PROT_WRITE;
allocation.type = MAP_ANON | MAP_PRIVATE;
mem_voidptr_t alloc_addr = allocate_ex(process, sizeof(buffer), allocation);
printf("Allocation Address: %p\n", alloc_addr);
if(alloc_addr == (mem_voidptr_t)MEM_BAD_RETURN)
{
printf("Invalid allocation\n");
return -1;
}
//Check if worked by reading/writing to that buffer
int read_buffer = 0;
mem_ex_write(process, alloc_addr, &buffer, sizeof(buffer));
mem_ex_read(process, alloc_addr, &read_buffer, sizeof(read_buffer));
printf("Read buffer: %i\n", read_buffer);
if(read_buffer == buffer)
printf("Success!\n");
return 0;
}
The target program:
int main()
{
printf("Waiting for injection\n");
while(1);
}
The output of the attacker program is:
Injection address: 0x55f6e104a000
Allocation Address: (nil)
Read buffer: 0
and a Segmentation Fault is raised on the target program. The executable region is valid (I manually checked) and the process is valid too. Also, I am having some trouble debugging the target program, apparently GDB wouldn't let ptrace do its job from the attacker program. Running Arch Linux. Both programs are compiled with clang (x64). Any ideas?
Turns out the problem was that I was reading/writing the memory using process_vm_read and process_vm_write. I got it to work by changing the read/write method to ptrace PEEK/POKE data. Fixed code (included on my memory lib):
mem_voidptr_t injection_address;
struct user_regs_struct old_regs, regs;
int status;
const mem_byte_t injection_buffer[] =
{
0x0f, 0x05, //syscall
0xcc //int3 (SIGTRAP)
};
mem_byte_t old_data[sizeof(injection_buffer)];
//Find injection address
char path_buffer[64];
snprintf(path_buffer, sizeof(path_buffer), "/proc/%i/maps", process.pid);
int fd = open(path_buffer, O_RDONLY);
if(fd == -1) return alloc_addr;
int read_check = 0;
mem_size_t file_size = 0;
mem_string_t file_buffer = mem_string_init();
for(char c; (read_check = read(fd, &c, 1)) != -1 && read_check != 0; file_size++)
{
mem_string_resize(&file_buffer, file_size);
mem_string_c_set(&file_buffer, file_size, c);
}
mem_size_t injection_address_pos, injection_address_end;
mem_string_t injection_address_str = mem_string_init();
injection_address = (mem_voidptr_t)MEM_BAD_RETURN;
injection_address_pos = mem_string_find(&file_buffer, "r-xp", 0);
injection_address_pos = mem_string_rfind(&file_buffer, "\n", injection_address_pos);
if(injection_address_pos == file_buffer.npos) return alloc_addr;
injection_address_end = mem_string_find(&file_buffer, "-", injection_address_pos);
injection_address_str = mem_string_substr(&file_buffer, injection_address_pos, injection_address_end);
injection_address = (mem_voidptr_t)strtoull(mem_string_c_str(&injection_address_str), NULL, 16);
if(injection_address == (mem_voidptr_t)MEM_BAD_RETURN || injection_address == (mem_voidptr_t)0) return alloc_addr;
//Inject
ptrace(PTRACE_ATTACH, process.pid, NULL, NULL);
//Store data at injection_address
for(mem_size_t i = 0; i < sizeof(injection_buffer); i++)
((mem_byte_t*)old_data)[i] = (mem_byte_t)ptrace(PTRACE_PEEKDATA, process.pid, injection_address + i, NULL);
//Write injection buffer to injection address
for(mem_size_t i = 0; i < sizeof(injection_buffer); i++)
ptrace(PTRACE_POKEDATA, process.pid, injection_address + i, ((mem_byte_t*)injection_buffer)[i]);
ptrace(PTRACE_GETREGS, process.pid, NULL, &old_regs);
regs = old_regs;
regs.rax = __NR_mmap; //syscall number
regs.rdi = (mem_uintptr_t)0; //arg0 (void* address)
regs.rsi = (mem_uintptr_t)size; //arg1 (size_t size)
regs.rdx = (mem_uintptr_t)protection; //arg2 (int protection)
regs.r10 = MAP_PRIVATE | MAP_ANON; //arg3 (int flags)
regs.r8 = -1; //arg4 (int fd)
regs.r9 = 0; //arg5 (off_t offset)
regs.rip = (mem_uintptr_t)injection_address; //next instruction
ptrace(PTRACE_SETREGS, process.pid, NULL, ®s);
ptrace(PTRACE_CONT, process.pid, NULL, NULL);
waitpid(process.pid, &status, WSTOPPED);
ptrace(PTRACE_GETREGS, process.pid, NULL, ®s);
alloc_addr = (mem_voidptr_t)regs.rax;
//Restore old execution
ptrace(PTRACE_SETREGS, process.pid, NULL, &old_regs);
for(mem_size_t i = 0; i < sizeof(injection_buffer); i++)
ptrace(PTRACE_POKEDATA, process.pid, injection_address + i, ((mem_byte_t*)old_data)[i]);
//ptrace(PTRACE_CONT, process.pid, NULL, NULL);
ptrace(PTRACE_DETACH, process.pid, NULL, NULL);
if(alloc_addr == (mem_voidptr_t)__NR_mmap)
alloc_addr = (mem_voidptr_t)MEM_BAD_RETURN;
return alloc_addr;