Initial commit

This commit is contained in:
Yann Weber 2023-02-10 14:12:58 +01:00
commit 6dfc10797f
7 changed files with 572 additions and 0 deletions

40
Makefile Normal file
View file

@ -0,0 +1,40 @@
GCC=gcc
AS=as
LD=ld
ASFLAGS=--64
LDFLAGS=-s -melf_x86_64
CFLAGS=-Wall
CLDFLAGS=-s
# To build with debugging symbols
ifeq ($(DEBUG), 1)
ASFLAGS += -DDEBUG=1 --gdwarf-5 -g
LDFLAGS = -g -melf_x86_64
LIB_FILE = src/lib$(LIBNAME)_dbg.a
CFLAGS += -g
CLDFLAGS = -g
endif
C_SRCS=$(wildcard *.c)
C_OBJ=$(C_SRCS:.c=.o)
all: child asmsh
asmsh: $(C_OBJ)
$(GCC) $(CLDFLAGS) -o $@ $<
$(C_OBJ): %.o: %.c Makefile
$(GCC) $(CFLAGS) -o $@ -c $<
child: child.o
$(LD) $(LDFLAGS) -o $@ $<
child.o: child.s Makefile
$(AS) $(ASFLAGS) -o $@ $<
.PHONY: clean
clean:
-rm -f $(C_OBJ) child.o

7
README Normal file
View file

@ -0,0 +1,7 @@
asmsh
An assembly shell.
Interactively run assembly into a terminal emulator.
Uses ptrace syscall to achieve this.

204
asmsh.c Normal file
View file

@ -0,0 +1,204 @@
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
#include<sys/ptrace.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/user.h>
#include<sys/wait.h>
#define CHILD_EXEC_PATH "./child"
void child_process();
int main(int argc, char *argv[], char *envp[])
{
int cpid, wstatus;
unsigned long pret;
pid_t wpid;
int sigpipe[2];
char sigbuf;
struct user_regs_struct regs;
if(pipe(sigpipe) < 0)
{
perror("Unable to open pipe");
return 1;
}
cpid = fork();
if(cpid < 0)
{
perror("Unable to fork :");
return 1;
}
else if(cpid == 0)
{
child_process(argv, envp, sigpipe);
}
printf("Child started %d\n", cpid);
if(ptrace(PTRACE_ATTACH, cpid, 0, 0) == -1)
{
perror("Unable to attach to child process");
goto err_cleanup;
}
if(waitpid(cpid, &wstatus, 0) < 0)
{
perror("Unable to wait for child process to stop");
goto err_cleanup;
}
if(!WIFSTOPPED(wstatus))
{
dprintf(2, "Wstatus = %d\n", wstatus);
goto err_cleanup;
}
//read(sigpipe[0], &sigbuf, 1); // sync message
printf("Attached to %d\n", cpid);
if(ptrace(PTRACE_SETOPTIONS, cpid, NULL, PTRACE_O_TRACEEXEC) < 0)
{
perror("Setoptions error");
goto err_cleanup;
}
if(ptrace(PTRACE_CONT, cpid, NULL, 0) < 0)
{
perror("CONT error");
goto err_cleanup;
}
if(waitpid(cpid, &wstatus, 0) < 0)
{
perror("Unable to wait for child process to stop");
goto err_cleanup;
}
if(wstatus >> 8 != (SIGTRAP | (PTRACE_EVENT_EXEC<<8)))
{
dprintf(2, "Wstatus = %d\n", wstatus);
goto err_cleanup;
}
// first syscall is execve ret
// then mmap in & out
for(int i=0; i<3; i++)
{
if(ptrace(PTRACE_SYSCALL, cpid, NULL, 0) < 0)
{
perror("CONT error");
goto err_cleanup;
}
printf("syscall sent, waiting child to stop\n");
if(waitpid(cpid, &wstatus, 0) < 0)
{
perror("Unable to wait for child process to stop");
goto err_cleanup;
}
if(wstatus != 1407)
{
dprintf(2, "Wstatus = %d\n", wstatus);
goto err_cleanup;
}
}
printf("mmap done ?\n");
bzero(&regs, sizeof(regs));
if(ptrace(PTRACE_GETREGS, cpid, NULL, &regs) < 0)
{
perror("GETREGS error");
goto err_cleanup;
}
printf("rax = %016llX rsp=%016llX r15=%016llX rip=%016llX\n",
regs.rax, regs.rsp, regs.r15, regs.rip);
/*
if((pret = ptrace(PTRACE_PEEKTEXT, cpid, regs.rsp, 0)) < 0)
{
perror("PEEKTEXT fails");
goto err_cleanup;
}
printf("mmap_addr : %08lx", pret);
if((pret = ptrace(PTRACE_PEEKTEXT, cpid, regs.rsp+sizeof(long), 0)) < 0)
{
perror("PEEKTEXT fails");
goto err_cleanup;
}
printf(" %08lx\n", pret);
*/
char mapfile[256];
snprintf(mapfile, 256, "/proc/%d/maps", cpid);
int mapfd = open(mapfile, O_RDONLY);
char buff[256];
int read_ret;
do
{
read_ret = read(mapfd, buff, 255);
buff[read_ret] = '\0';
if(read_ret)
{
printf("%s", buff);
}
}while(read_ret > 0);
printf("Exiting...\n");
kill(cpid, SIGKILL);
exit(0);
err_cleanup:
/*
if(waitpid(cpid, &wstatus, 0) < 0)
{
perror("Unable to wait for child process to stop");
goto err_cleanup;
}
if(WIFEXITED(wstatus))
{
dprintf(2, "child exited with status %d\n", WEXITSTATUS(wstatus));
} else if(WCOREDUMP(wstatus)) {
dprintf(2, "child segfault\n");
} else if(WIFSTOPPED(wstatus)) {
dprintf(2, "child stopped\n");
} else {
dprintf(2, "Wstatus = %d\n", wstatus);
}
*/
dprintf(2, "Hardkill %d\n", cpid);
kill(cpid, SIGKILL);
exit(2);
}
void child_process(char * argv[], char *envp[], int sigpipe[2])
{
char sigbuf = '\0';
//char* const argv[] = {"child"};
//char* const envp[] = {};
/*
if(ptrace(PTRACE_TRACEME, 0,0,0) < 0)
{
perror("Unable to TRACEME");
exit(5);
}
printf("TRACEME %d [OK]\n", getpid());
*/
//write(sigpipe[1], &sigbuf, 1); // sync message
printf("%d execve\n", getpid());
execve(CHILD_EXEC_PATH, argv, envp);
perror("Unable to execve");
exit(5);
}

60
asmsh.s Normal file
View file

@ -0,0 +1,60 @@
.file "asmsh.c"
.text
.section .rodata
.LC0:
.string "Unable to fork :"
.text
.globl main
.type main, @function
main:
.LFB6:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
call fork@PLT
movl %eax, -4(%rbp)
cmpl $0, -4(%rbp)
jns .L2
leaq .LC0(%rip), %rdi
call perror@PLT
movl $1, %eax
jmp .L3
.L2:
cmpl $0, -4(%rbp)
jne .L4
movl $0, %eax
call child_process
.L4:
movl $0, %eax
.L3:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size main, .-main
.globl child_process
.type child_process, @function
child_process:
.LFB7:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE7:
.size child_process, .-child_process
.ident "GCC: (Debian 10.2.1-6) 10.2.1 20210110"
.section .note.GNU-stack,"",@progbits

37
child.s Normal file
View file

@ -0,0 +1,37 @@
.file "child64.s"
.section .text
.global _start
_start:
mov $0x9, %rax # MMAP
xor %rdi, %rdi
mov $0x1000, %rsi # 1 page map
mov $(0x1|0x2), %rdx # PROT_READ | PROT_WRITE
mov $(0x20 | 0x1), %r10 # MAP_ANONYMOUS | MAP_SHARED
mov $-1, %r8 # fd
xor %r9, %r9
syscall
cmp $0, %rax
jle .errmap
push %rax
xor %rax, %rax
push %rax
mov $-1, %r15
mov $34, %rax # sys_pause
syscall
jmp *(%rsp)
mov $60, %rax
xor %rdi, %rdi
syscall
.errmap:
mov $60, %rax # sys_exit
mov $1, %rdi
syscall

175
mmap_parse.c Normal file
View file

@ -0,0 +1,175 @@
#include "mmap_parse.h"
int child_mmap_get(pid_t child_pid, child_mmap_l *maps)
{
const int RDBUF_SZ = 4095;
char procfs_path[PATH_MAX+1];
char rdbuf[RDBUF_SZ+1];
char *line, *endline;
int ret, maps_fd;
size_t curmap;
void *tmp;
if(child_pid == -1)
{
errno = EINVAL;
return -1;
}
ret = snprintf(procfs_path, PATH_MAX, "/proc/%d/maps", child_pid);
if(ret < 0) { return -1; }
procfs_path[ret] = '\0';
if((maps_fd = open(procfs_path, O_RDONLY)) < 0)
{
return -1;
}
curmap = 0;
do
{
ret = read(maps_fd, rdbuf, RDBUF_SZ);
if(ret < 0) { goto err; }
rdbuf[ret] = '\0';
line = endline = rdbuf;
while(*endline)
{
if(*endline == '\n')
{
if(maps->size <= curmap)
{
if(_child_mmap_alloc(curmap, maps) < 0)
{
goto err;
}
}
*endline = '\0';
ret = child_mmap_parseline(line, &maps->maps[curmap]);
if(ret < 0) { goto err; }
line = endline + 1;
}
endline++;
}
}while(1);
close(maps_fd);
if(curmap < maps->size)
{
tmp = realloc(maps->maps, sizeof(child_mmap_t) * curmap);
if(!tmp)
{
goto err;
}
maps->maps = tmp;
maps->size = curmap;
}
return 0;
err:
ret = errno;
close(maps_fd);
errno = ret;
return -1;
}
int child_mmap_parseline(char *line, child_mmap_t *maps)
{
char *ptr, *orig, *endptr;
unsigned long long parsed;
int errno_bck, cperm;
size_t i;
void **addr_ptr[2] = {&maps->start, &maps->stop};
const char addr_sep[2] = "- ";
const char perms[3] = "rwx";
const int perms_val[3] = {PROT_READ, PROT_WRITE, PROT_EXEC};
#define parsefield(line, sep, base, mapfield) {\
unsigned long long p; char *endptr;\
errno = 0;\
p = strtoull(line, &endptr, base);\
if(errno || *endptr != sep) { goto err_inval; }\
*mapfield=p;
ptr = orig = line;
for(i=0; i<2; i++)
{
errno = 0;
parsed = strtoull(line, &endptr, 16);
if(errno || *endptr != addr_sep[i])
{
goto err_inval;
}
*addr_ptr[i] = (void*)parsed;
line = endptr+1;
}
cperm = 1;
maps->perm = 0;
for(i=0; i<3; i++)
{
if(*line == perms[i])
{
maps->perm |= perms_val[i];
}
else if (*line != '-')
{
goto err_inval;
}
line++;
cperm <<= 1;
}
switch(*line)
{
case 'p':
maps->perm |= MAP_PRIVATE;
break;
case 's':
maps->perm |= MAP_SHARED;
break;
default:
goto err_inval;
}
line++;
return 0;
err_inval:
errno_bck = errno;
dprintf(2, "Invalid procfs/[pid]/maps content '%s'", orig);
err:
return -1;
}
int _child_mmap_alloc(size_t curmap, child_mmap_l *maps)
{
void *tmp;
maps->size++;
if(curmap == 0)
{
tmp = malloc(sizeof(child_mmap_t));
}
else
{
tmp = realloc(maps->maps,
sizeof(child_mmap_t) * maps->size);
}
if(!tmp)
{
perror("Unable to allocate maps description");
return -1;
}
maps->maps = tmp;
return 0;
}

49
mmap_parse.h Normal file
View file

@ -0,0 +1,49 @@
#ifndef ASMSH_MMAP_PARSE_H
#define ASMSH_MMAP_PARSE_H
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
extern int errno;
typedef struct child_mmap_s child_mmap_t;
typedef struct child_mmap_list child_mmap_l;
struct child_mmap_s
{
void *start;
void *stop;
int perm;
size_t offset;
dev_t device;
ino_t inode;
char *pathname;
};
struct child_mmap_list
{
child_mmap_t *maps;
size_t size;
};
/** Parse /proc/[pid]/map file
*
* @param pid_t The pid of the child process we ptrace attached
* @param child_mmap_l A child map that will be filled with child's mmaps
* @return 0 if no error else -1
*/
int child_mmap_get(pid_t child_pid, child_mmap_l *maps);
int child_mmap_parseline(char *line, child_mmap_t *map);
int _child_mmap_alloc(size_t curmap, child_mmap_l *maps);
#endif