From f19cf67d020fb875da51ae60a1fb4286ed19eb50 Mon Sep 17 00:00:00 2001 From: Theodore Dubois Date: Sat, 27 May 2017 16:34:29 -0700 Subject: [PATCH] Implement uname and add some opcodes --- emu/cpu.c | 73 ++++++++++++++++++++++++++++++++---- emu/instructions.h | 30 +++++++++------ main.c | 6 ++- meson.build | 2 + sys/calls.c | 2 +- sys/calls.h | 16 +++++++- sys/tls.c | 2 +- sys/uname.c | 19 ++++++++++ sys/user.c | 10 +++-- tests/meson.build | 3 +- tools/ptraceomatic.c | 19 +++++++++- tools/ptutil.c | 14 +++++++ tools/ptutil.h | 1 + tools/vdso-transplant-main.c | 4 +- vdso/meson.build | 2 +- vdso/note.S | 14 +++++++ 16 files changed, 183 insertions(+), 34 deletions(-) create mode 100644 sys/uname.c create mode 100644 vdso/note.S diff --git a/emu/cpu.c b/emu/cpu.c index db490264ac..68369f30da 100644 --- a/emu/cpu.c +++ b/emu/cpu.c @@ -111,6 +111,8 @@ int cpu_step(struct cpu_state *cpu) { case 0x05: TRACEI("add imm, eax"); READIMM; ADD(imm, ax); break; + case 0x09: TRACEI("or reg, modrm"); + READMODRM_W; OR(modrm_reg, modrm_val); break; case 0x0d: TRACEI("or imm, eax\t"); READIMM; OR(imm, ax); break; @@ -123,18 +125,45 @@ int cpu_step(struct cpu_state *cpu) { do_cpuid(&cpu->eax, &cpu->ebx, &cpu->ecx, &cpu->edx); break; + case 0x80: TRACEI("jo rel\t"); + READIMM; J_REL(O, imm); break; + case 0x81: TRACEI("jno rel\t"); + READIMM; J_REL(!O, imm); break; + case 0x82: TRACEI("jb rel\t"); + READIMM; J_REL(B, imm); break; + case 0x83: TRACEI("jnb rel\t"); + READIMM; J_REL(!B, imm); break; + case 0x84: TRACEI("je rel\t"); + READIMM; J_REL(E, imm); break; + case 0x85: TRACEI("jne rel\t"); + READIMM; J_REL(!E, imm); break; + case 0x86: TRACEI("jbe rel\t"); + READIMM; J_REL(BE, imm); break; + case 0x87: TRACEI("ja rel\t"); + READIMM; J_REL(!BE, imm); break; + case 0x88: TRACEI("js rel\t"); + READIMM; J_REL(S, imm); break; + case 0x89: TRACEI("jns rel\t"); + READIMM; J_REL(!S, imm); break; + case 0x8a: TRACEI("jp rel\t"); + READIMM; J_REL(P, imm); break; + case 0x8b: TRACEI("jnp rel\t"); + READIMM; J_REL(!P, imm); break; + case 0x8c: TRACEI("jl rel\t"); + READIMM; J_REL(L, imm); break; + case 0x8d: TRACEI("jnl rel\t"); + READIMM; J_REL(!L, imm); break; + case 0x8e: TRACEI("jle rel\t"); + READIMM; J_REL(LE, imm); break; + case 0x8f: TRACEI("jnle rel\t"); + READIMM; J_REL(!LE, imm); break; + // TODO more sets case 0x92: TRACEI("setb\t"); READMODRM_W; SET(B, modrm_val8); break; case 0x94: TRACEI("sete\t"); READMODRM_W; SET(E, modrm_val8); break; - // TODO more jumps - case 0x84: TRACEI("je rel\t"); - READIMM; J_REL(E, imm); break; - case 0x85: TRACEI("jne rel\t"); - READIMM; J_REL(!E, imm); break; - case 0xaf: TRACEI("imul modrm, reg"); READMODRM; IMUL(modrm_reg, modrm_val); break; @@ -142,6 +171,10 @@ int cpu_step(struct cpu_state *cpu) { READMODRM; MOV(modrm_val8, modrm_reg); break; case 0xb7: TRACEI("movz modrm16, reg"); READMODRM; MOV(modrm_val16, modrm_reg); break; + case 0xbe: TRACEI("movs modrm8, reg"); + READMODRM; MOV((int8_t) modrm_val8, modrm_reg); break; + case 0xbf: TRACEI("movs modrm16, reg"); + READMODRM; MOV((int16_t) modrm_val16, modrm_reg); break; case 0xd6: // someone tell intel to get a life @@ -162,6 +195,8 @@ int cpu_step(struct cpu_state *cpu) { case 0x29: TRACEI("sub reg, modrm"); READMODRM_W; SUB(modrm_reg, modrm_val); break; + case 0x2b: TRACEI("sub modrm, reg"); + READMODRM; SUB(modrm_val, modrm_reg); break; case 0x30: TRACEI("xor reg8, modrm8"); READMODRM_W; XOR(modrm_reg8, modrm_val8); break; @@ -172,9 +207,14 @@ int cpu_step(struct cpu_state *cpu) { case 0x39: TRACEI("cmp reg, modrm"); READMODRM; CMP(modrm_reg, modrm_val); break; + case 0x3b: TRACEI("cmp modrm, reg"); + READMODRM; CMP(modrm_val, modrm_reg); break; case 0x3d: TRACEI("cmp imm, eax"); READIMM; CMP(imm, ax); break; + case 0x40: TRACEI("inc eax"); INC(ax); break; + case 0x4a: TRACEI("dec edx"); DEC(dx); break; + case 0x50: TRACEI("push eax"); PUSH(ax); break; case 0x51: TRACEI("push ecx"); @@ -235,6 +275,12 @@ int cpu_step(struct cpu_state *cpu) { case 0x6a: TRACEI("push imm8\t"); READIMM8; PUSH(imm8); break; + case 0x70: TRACEI("jo rel8\t"); + READIMM8; J_REL(O, (int8_t) imm8); break; + case 0x71: TRACEI("jno rel8\t"); + READIMM8; J_REL(!O, (int8_t) imm8); break; + case 0x72: TRACEI("jb rel8\t"); + READIMM8; J_REL(B, (int8_t) imm8); break; case 0x73: TRACEI("jnb rel8\t"); READIMM8; J_REL(!B, (int8_t) imm8); break; case 0x74: TRACEI("je rel8\t"); @@ -249,8 +295,18 @@ int cpu_step(struct cpu_state *cpu) { READIMM8; J_REL(S, (int8_t) imm8); break; case 0x79: TRACEI("jns rel8\t"); READIMM8; J_REL(!S, (int8_t) imm8); break; + case 0x7a: TRACEI("jp rel8\t"); + READIMM8; J_REL(P, (int8_t) imm8); break; + case 0x7b: TRACEI("jnp rel8\t"); + READIMM8; J_REL(!P, (int8_t) imm8); break; + case 0x7c: TRACEI("jl rel8\t"); + READIMM8; J_REL(L, (int8_t) imm8); break; + case 0x7d: TRACEI("jnl rel8\t"); + READIMM8; J_REL(!L, (int8_t) imm8); break; case 0x7e: TRACEI("jle rel8\t"); READIMM8; J_REL(LE, (int8_t) imm8); break; + case 0x7f: TRACEI("jnle rel8\t"); + READIMM8; J_REL(!LE, (int8_t) imm8); break; case 0x80: TRACEI("grp1 imm8, modrm8"); READMODRM; READIMM8; GRP1(imm8, modrm_val8); break; @@ -268,6 +324,7 @@ int cpu_step(struct cpu_state *cpu) { READMODRM_W; MOV(modrm_reg8, modrm_val8); break; case 0x89: TRACEI("mov reg, modrm"); READMODRM_W; MOV(modrm_reg, modrm_val); break; + case 0x90: TRACEI("nop"); break; case 0x8a: TRACEI("mov modrm8, reg8"); READMODRM; MOV(modrm_val8, modrm_reg8); break; case 0x8b: TRACEI("mov modrm, reg"); @@ -290,7 +347,7 @@ int cpu_step(struct cpu_state *cpu) { break; case 0xa1: TRACEI("mov mem, eax\t"); - READADDR_W; MOV(MEM(addr), ax); break; + READADDR; MOV(MEM(addr), ax); break; case 0xa3: TRACEI("mov eax, mem\t"); READADDR_W; MOV(ax, MEM(addr)); break; @@ -333,6 +390,8 @@ int cpu_step(struct cpu_state *cpu) { case 0xe9: TRACEI("jmp rel\t"); READIMM; JMP_REL(imm); break; + case 0xeb: TRACEI("jmp rel8\t"); + READIMM8; JMP_REL((int8_t) imm8); break; case 0xf3: READINSN; diff --git a/emu/instructions.h b/emu/instructions.h index 284c0cdb4b..90a66f414f 100644 --- a/emu/instructions.h +++ b/emu/instructions.h @@ -14,44 +14,52 @@ return code #define SETRES(result) \ - cpu->res = (result); cpu->zf_res = cpu->sf_res = cpu->pf_res = 1; + cpu->res = (result); cpu->zf_res = cpu->sf_res = cpu->pf_res = 1 #define TEST(src, dst) \ cpu->res = (dst) & (src); \ - cpu->cf = cpu->of = 0; + cpu->cf = cpu->of = 0 #define ADD(src, dst) \ cpu->cf = __builtin_add_overflow((uint32_t) (dst), (uint32_t) (src), (uint32_t *) &cpu->res); \ cpu->of = __builtin_add_overflow((int32_t) (dst), (int32_t) (src), (int32_t *) &cpu->res); \ - (dst) = cpu->res; + (dst) = cpu->res #define OR(src, dst) \ (dst) |= (src); \ cpu->cf = cpu->of = 0; \ - SETRES(dst); + SETRES(dst) #define AND(src, dst) \ (dst) &= (src); \ cpu->cf = cpu->of = 0; \ - SETRES(dst); + SETRES(dst) #define SUB(src, dst) \ cpu->cf = __builtin_sub_overflow((uint32_t) (dst), (uint32_t) (src), (uint32_t *) &cpu->res); \ cpu->of = __builtin_sub_overflow((int32_t) (dst), (int32_t) (src), (int32_t *) &cpu->res); \ - (dst) = cpu->res; + (dst) = cpu->res // TODO flags #define XOR(src, dst) dst ^= src; #define CMP(src, dst) \ cpu->cf = __builtin_sub_overflow((uint32_t) (dst), (uint32_t) (src), (uint32_t *) &cpu->res); \ - cpu->of = __builtin_sub_overflow((int32_t) (dst), (int32_t) (src), (int32_t *) &cpu->res); - -#define INC(val) ADD(1, val) -#define DEC(val) SUB(1, val) + cpu->of = __builtin_sub_overflow((int32_t) (dst), (int32_t) (src), (int32_t *) &cpu->res) + +#define INC(val) do { \ + int tmp = cpu->cf; \ + ADD(1, val); \ + cpu->cf = tmp; \ +} while (0) +#define DEC(val) do { \ + int tmp = cpu->cf; \ + SUB(1, val); \ + cpu->cf = tmp; \ +} while (0) #define IMUL(reg, val) \ - reg *= val; + reg *= val // TODO flags #define DIV(reg, val, rem) \ diff --git a/main.c b/main.c index 1c379bbae6..9a3d5ded27 100644 --- a/main.c +++ b/main.c @@ -2,10 +2,12 @@ #include "sys/calls.h" #include "emu/process.h" -int main(int argc, const char *argv[]) { +int main(int argc, char *const args[]) { int err; current = process_create(); - if ((err = sys_execve(argv[1], NULL, NULL)) < 0) { + char *const argv[] = {args[1], NULL}; + char *const envp[] = {NULL}; + if ((err = sys_execve(args[1], argv, envp)) < 0) { return -err; } cpu_run(¤t->cpu); diff --git a/meson.build b/meson.build index 4024f31fd1..0363422371 100644 --- a/meson.build +++ b/meson.build @@ -15,9 +15,11 @@ sys_src = [ 'sys/calls.c', 'sys/user.c', 'sys/exec/exec.c', + 'sys/exit.c', 'sys/write.c', 'sys/brk.c', + 'sys/uname.c', 'sys/tls.c', ] emu_src = [ diff --git a/sys/calls.c b/sys/calls.c index dc6fe8d3c4..e1cf843b69 100644 --- a/sys/calls.c +++ b/sys/calls.c @@ -12,7 +12,7 @@ syscall_t syscall_table[] = { [4] = (syscall_t) _sys_write, // 4 [11] = (syscall_t) _sys_execve, // 11 [45] = (syscall_t) sys_brk, // 45 - + [122] = (syscall_t) _sys_uname, [243] = (syscall_t) sys_set_thread_area, }; diff --git a/sys/calls.h b/sys/calls.h index 2c1936e3fc..01d63ed5a1 100644 --- a/sys/calls.h +++ b/sys/calls.h @@ -10,8 +10,8 @@ void user_put(addr_t addr, dword_t value); void user_put8(addr_t addr, byte_t value); int user_get_string(addr_t addr, char *buf, size_t max); void user_put_string(addr_t addr, const char *buf); -int user_get_count(addr_t addr, char *buf, size_t count); -void user_put_count(addr_t addr, const char *buf, size_t count); +int user_get_count(addr_t addr, void *buf, size_t count); +void user_put_count(addr_t addr, const void *buf, size_t count); int sys_execve(const char *file, char *const argv[], char *const envp[]); int _sys_execve(addr_t file, addr_t argv, addr_t envp); @@ -23,6 +23,18 @@ dword_t _sys_write(dword_t fd, addr_t data, dword_t count); addr_t sys_brk(addr_t new_brk); +#define UNAME_LENGTH 65 +struct uname { + char system[UNAME_LENGTH]; // Linux + char hostname[UNAME_LENGTH]; // my-compotar + char release[UNAME_LENGTH]; // 1.2.3-ish + char version[UNAME_LENGTH]; // SUPER AWESOME + char arch[UNAME_LENGTH]; // i686 + char domain[UNAME_LENGTH]; // lol +}; +int sys_uname(struct uname *uts); +dword_t _sys_uname(addr_t uts_addr); + int sys_set_thread_area(addr_t u_info); typedef int (*syscall_t)(dword_t,dword_t,dword_t,dword_t,dword_t,dword_t); diff --git a/sys/tls.c b/sys/tls.c index a7028375aa..911dbdb9af 100644 --- a/sys/tls.c +++ b/sys/tls.c @@ -15,7 +15,7 @@ struct user_desc { int sys_set_thread_area(addr_t u_info) { struct user_desc info; - user_get_count(u_info, (char *) &info, sizeof(struct user_desc)); + user_get_count(u_info, &info, sizeof(struct user_desc)); // On a real system, TLS works by creating a special segment pointing to // the TLS buffer. Our shitty emulation of that is to ignore attempts to diff --git a/sys/uname.c b/sys/uname.c new file mode 100644 index 0000000000..0443709809 --- /dev/null +++ b/sys/uname.c @@ -0,0 +1,19 @@ +#include +#include "sys/calls.h" + +int sys_uname(struct uname *uts) { + strcpy(uts->system, "Linux"); + strcpy(uts->hostname, "compotar"); + strcpy(uts->release, "2.6.32-ish"); + strcpy(uts->version, "SUPER AWESOME"); + strcpy(uts->arch, "i686"); + strcpy(uts->domain, "compotar.me"); + return 0; +} + +dword_t _sys_uname(addr_t uts_addr) { + struct uname uts; + int res = sys_uname(&uts); + user_put_count(uts_addr, &uts, sizeof(struct uname)); + return res; +} diff --git a/sys/user.c b/sys/user.c index f4ef5120d2..fa9c6d9793 100644 --- a/sys/user.c +++ b/sys/user.c @@ -36,19 +36,21 @@ void user_put_string(addr_t addr, const char *buf) { user_put8(addr + i, '\0'); } -int user_get_count(addr_t addr, char *buf, size_t count) { +int user_get_count(addr_t addr, void *buf, size_t count) { + char *cbuf = (char *) buf; size_t i = 0; while (i < count) { - buf[i] = user_get8(addr + i); + cbuf[i] = user_get8(addr + i); i++; } return i; } -void user_put_count(addr_t addr, const char *buf, size_t count) { +void user_put_count(addr_t addr, const void *buf, size_t count) { + const char *cbuf = (const char *) buf; size_t i = 0; while (i < count) { - user_put8(addr + i, buf[i]); + user_put8(addr + i, cbuf[i]); i++; } } diff --git a/tests/meson.build b/tests/meson.build index 18aaa2fc4b..6395ee8c31 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,2 +1,3 @@ executable('hello', ['hello.c'], c_args: ['-m32'], link_args: ['-m32', '-nostdlib']) -executable('hello-clib-static', ['hello-clib.c'], c_args: ['-m32'], link_args: ['-m32', '-static']) +executable('hello-libc-static', ['hello-clib.c'], c_args: ['-m32'], link_args: ['-m32', '-static']) +executable('hello-libc', ['hello-clib.c'], c_args: ['-m32'], link_args: ['-m32']) diff --git a/tools/ptraceomatic.c b/tools/ptraceomatic.c index d8980025ec..eb0a45f951 100644 --- a/tools/ptraceomatic.c +++ b/tools/ptraceomatic.c @@ -74,17 +74,31 @@ void step_tracing(struct cpu_state *cpu, int pid) { } // step real cpu - // intercept cpuid, though + // intercept cpuid and int $0x80, though struct user_regs_struct regs; errno = 0; trycall(ptrace(PTRACE_GETREGS, pid, NULL, ®s), "ptrace getregs step"); long inst = trycall(ptrace(PTRACE_PEEKTEXT, pid, regs.rip, NULL), "ptrace get inst step"); + if ((inst & 0xff) == 0x0f && ((inst & 0xff00) >> 8) == 0xa2) { // cpuid, handle ourselves and bump ip do_cpuid((dword_t *) ®s.rax, (dword_t *) ®s.rbx, (dword_t *) ®s.rcx, (dword_t *) ®s.rdx); regs.rip += 2; - trycall(ptrace(PTRACE_SETREGS, pid, NULL, ®s), "ptrace setregs step"); + } else if ((inst & 0xff) == 0xcd && ((inst & 0xff00) >> 8) == 0x80) { + // int $0x80, consider intercepting the syscall + dword_t syscall_num = (dword_t) regs.rax; + if (syscall_num == 122) { + // uname + addr_t uname_ptr = (addr_t) regs.rbx; + struct uname un; + regs.rax = sys_uname(&un); + pt_copy(pid, uname_ptr, &un, sizeof(struct uname)); + regs.rip += 2; + } else { + goto do_step; + } } else { +do_step: (void)0; // single step on a repeated string instruction only does one // iteration, so loop until ip changes long ip = regs.rip; @@ -94,6 +108,7 @@ void step_tracing(struct cpu_state *cpu, int pid) { trycall(ptrace(PTRACE_GETREGS, pid, NULL, ®s), "ptrace getregs step"); } } + trycall(ptrace(PTRACE_SETREGS, pid, NULL, ®s), "ptrace setregs step"); } void prepare_tracee(int pid) { diff --git a/tools/ptutil.c b/tools/ptutil.c index 84080f7e75..64419d2d2e 100644 --- a/tools/ptutil.c +++ b/tools/ptutil.c @@ -50,6 +50,20 @@ void pt_write(int pid, addr_t addr, dword_t val) { trycall(ptrace(PTRACE_POKEDATA, pid, addr, out), "memory write"); } +static void pt_write8(int pid, addr_t addr, byte_t val) { + // when a 64-bit process traces a 32-bit process, all writes are 64-bit. gah + uint64_t thingy = trycall(ptrace(PTRACE_PEEKDATA, pid, addr + 1), "memory write read"); + uint64_t out = thingy << 8 | val; + trycall(ptrace(PTRACE_POKEDATA, pid, addr, out), "memory write"); +} + +void pt_copy(int pid, addr_t addr, const void *vdata, size_t len) { + const byte_t *data = (byte_t *) vdata; + for (int i = 0; i < len; i++) { + pt_write8(pid, addr + i, data[i]); + } +} + static addr_t aux_addr(int pid, int type) { struct user_regs_struct regs; trycall(ptrace(PTRACE_GETREGS, pid, NULL, ®s), "ptrace get sp for aux"); diff --git a/tools/ptutil.h b/tools/ptutil.h index c557b87055..14952088c9 100644 --- a/tools/ptutil.h +++ b/tools/ptutil.h @@ -4,5 +4,6 @@ long trycall(long res, const char *msg); int start_tracee(const char *program, char *const argv[], char *const envp[]); dword_t pt_read(int pid, addr_t addr); void pt_write(int pid, addr_t addr, dword_t val); +void pt_copy(int pid, addr_t start, const void *vdata, dword_t len); dword_t aux_read(int pid, int type); void aux_write(int pid, int type, dword_t value); diff --git a/tools/vdso-transplant-main.c b/tools/vdso-transplant-main.c index f24a4bd4ad..b595b13301 100644 --- a/tools/vdso-transplant-main.c +++ b/tools/vdso-transplant-main.c @@ -30,7 +30,7 @@ int main(int argc, char *const args[]) { } transplant_vdso(pid, vdso, vdso_size); - /* trycall(kill(pid, SIGSTOP), "pause process"); */ - /* printf("attach debugger to %d\n", pid); */ + trycall(kill(pid, SIGSTOP), "pause process"); + printf("attach debugger to %d\n", pid); return 0; } diff --git a/vdso/meson.build b/vdso/meson.build index 32d77451d9..40cd038808 100644 --- a/vdso/meson.build +++ b/vdso/meson.build @@ -1,4 +1,4 @@ # the vdso -vdso = shared_library('vdso', ['vdso.S', 'vdso.c'], c_args: ['-m32'], +vdso = shared_library('vdso', ['vdso.S', 'vdso.c', 'note.S'], c_args: ['-m32'], link_args: ['-m32', '-nostdlib', '-Wl,-T,../vdso/vdso.lds'], link_depends: ['vdso.lds']) diff --git a/vdso/note.S b/vdso/note.S new file mode 100644 index 0000000000..44652b41ab --- /dev/null +++ b/vdso/note.S @@ -0,0 +1,14 @@ +.pushsection .note.Linux, "",@note + .balign 4 + .long after_name - name // namesz + .long after_note - note // descz + .long 0 +name: + .asciz "Linux" +after_name: + .balign 4 +note: + .long 0x010000 // let's pretend we're linux 2.0.0 +after_note: + .balign 4 +.popsection