Initial commit
This commit is contained in:
commit
6dfc10797f
7 changed files with 572 additions and 0 deletions
40
Makefile
Normal file
40
Makefile
Normal 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
7
README
Normal 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
204
asmsh.c
Normal 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(®s, sizeof(regs));
|
||||
if(ptrace(PTRACE_GETREGS, cpid, NULL, ®s) < 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
60
asmsh.s
Normal 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
37
child.s
Normal 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
175
mmap_parse.c
Normal 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
49
mmap_parse.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue