diff options
Diffstat (limited to 'util/statetrace/arch/amd64/tracechild.cc')
-rw-r--r-- | util/statetrace/arch/amd64/tracechild.cc | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/util/statetrace/arch/amd64/tracechild.cc b/util/statetrace/arch/amd64/tracechild.cc new file mode 100644 index 000000000..5d3b660ab --- /dev/null +++ b/util/statetrace/arch/amd64/tracechild.cc @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2007 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#include <iostream> +#include <iomanip> +#include <errno.h> +#include <sys/ptrace.h> +#include <stdint.h> +#include <string.h> + +#include "arch/amd64/tracechild.hh" + +using namespace std; + +bool +AMD64TraceChild::sendState(int socket) +{ + uint64_t regVal64 = 0; + uint32_t regVal32 = 0; + for (int x = 0; x <= R15; x++) { + regVal64 = getRegVal(x); + if (write(socket, ®Val64, sizeof(regVal64)) == -1) { + cerr << "Write failed! " << strerror(errno) << endl; + tracing = false; + return false; + } + } + regVal64 = getRegVal(RIP); + if (write(socket, ®Val64, sizeof(regVal64)) == -1) { + cerr << "Write failed! " << strerror(errno) << endl; + tracing = false; + return false; + } + for (int x = MMX0_0; x <= MMX7_1; x++) { + regVal32 = getRegVal(x); + if (write(socket, ®Val32, sizeof(regVal32)) == -1) { + cerr << "Write failed! " << strerror(errno) << endl; + tracing = false; + return false; + } + } + for (int x = XMM0_0; x <= XMM15_3; x++) { + regVal32 = getRegVal(x); + if (write(socket, ®Val32, sizeof(regVal32)) == -1) { + cerr << "Write failed! " << strerror(errno) << endl; + tracing = false; + return false; + } + } + return true; +} + +int64_t +AMD64TraceChild::getRegs(user_regs_struct & myregs, + user_fpregs_struct & myfpregs, int num) +{ + assert(num < numregs && num >= 0); + switch (num) { + //GPRs + case RAX: return myregs.rax; + case RBX: return myregs.rbx; + case RCX: return myregs.rcx; + case RDX: return myregs.rdx; + //Index registers + case RSI: return myregs.rsi; + case RDI: return myregs.rdi; + //Base pointer and stack pointer + case RBP: return myregs.rbp; + case RSP: return myregs.rsp; + //New 64 bit mode registers + case R8: return myregs.r8; + case R9: return myregs.r9; + case R10: return myregs.r10; + case R11: return myregs.r11; + case R12: return myregs.r12; + case R13: return myregs.r13; + case R14: return myregs.r14; + case R15: return myregs.r15; + //Segmentation registers + case CS: return myregs.cs; + case DS: return myregs.ds; + case ES: return myregs.es; + case FS: return myregs.fs; + case GS: return myregs.gs; + case SS: return myregs.ss; + case FS_BASE: return myregs.fs_base; + case GS_BASE: return myregs.gs_base; + //PC + case RIP: return myregs.rip; + //Flags + case EFLAGS: return myregs.eflags; + //MMX + case MMX0_0: return myfpregs.st_space[0]; + case MMX0_1: return myfpregs.st_space[1]; + case MMX1_0: return myfpregs.st_space[2]; + case MMX1_1: return myfpregs.st_space[3]; + case MMX2_0: return myfpregs.st_space[4]; + case MMX2_1: return myfpregs.st_space[5]; + case MMX3_0: return myfpregs.st_space[6]; + case MMX3_1: return myfpregs.st_space[7]; + case MMX4_0: return myfpregs.st_space[8]; + case MMX4_1: return myfpregs.st_space[9]; + case MMX5_0: return myfpregs.st_space[10]; + case MMX5_1: return myfpregs.st_space[11]; + case MMX6_0: return myfpregs.st_space[12]; + case MMX6_1: return myfpregs.st_space[13]; + case MMX7_0: return myfpregs.st_space[14]; + case MMX7_1: return myfpregs.st_space[15]; + //XMM + case XMM0_0: return myfpregs.xmm_space[0]; + case XMM0_1: return myfpregs.xmm_space[1]; + case XMM0_2: return myfpregs.xmm_space[2]; + case XMM0_3: return myfpregs.xmm_space[3]; + case XMM1_0: return myfpregs.xmm_space[4]; + case XMM1_1: return myfpregs.xmm_space[5]; + case XMM1_2: return myfpregs.xmm_space[6]; + case XMM1_3: return myfpregs.xmm_space[7]; + case XMM2_0: return myfpregs.xmm_space[8]; + case XMM2_1: return myfpregs.xmm_space[9]; + case XMM2_2: return myfpregs.xmm_space[10]; + case XMM2_3: return myfpregs.xmm_space[11]; + case XMM3_0: return myfpregs.xmm_space[12]; + case XMM3_1: return myfpregs.xmm_space[13]; + case XMM3_2: return myfpregs.xmm_space[14]; + case XMM3_3: return myfpregs.xmm_space[15]; + case XMM4_0: return myfpregs.xmm_space[16]; + case XMM4_1: return myfpregs.xmm_space[17]; + case XMM4_2: return myfpregs.xmm_space[18]; + case XMM4_3: return myfpregs.xmm_space[19]; + case XMM5_0: return myfpregs.xmm_space[20]; + case XMM5_1: return myfpregs.xmm_space[21]; + case XMM5_2: return myfpregs.xmm_space[22]; + case XMM5_3: return myfpregs.xmm_space[23]; + case XMM6_0: return myfpregs.xmm_space[24]; + case XMM6_1: return myfpregs.xmm_space[25]; + case XMM6_2: return myfpregs.xmm_space[26]; + case XMM6_3: return myfpregs.xmm_space[27]; + case XMM7_0: return myfpregs.xmm_space[28]; + case XMM7_1: return myfpregs.xmm_space[29]; + case XMM7_2: return myfpregs.xmm_space[30]; + case XMM7_3: return myfpregs.xmm_space[31]; + case XMM8_0: return myfpregs.xmm_space[32]; + case XMM8_1: return myfpregs.xmm_space[33]; + case XMM8_2: return myfpregs.xmm_space[34]; + case XMM8_3: return myfpregs.xmm_space[35]; + case XMM9_0: return myfpregs.xmm_space[36]; + case XMM9_1: return myfpregs.xmm_space[37]; + case XMM9_2: return myfpregs.xmm_space[38]; + case XMM9_3: return myfpregs.xmm_space[39]; + case XMM10_0: return myfpregs.xmm_space[40]; + case XMM10_1: return myfpregs.xmm_space[41]; + case XMM10_2: return myfpregs.xmm_space[42]; + case XMM10_3: return myfpregs.xmm_space[43]; + case XMM11_0: return myfpregs.xmm_space[44]; + case XMM11_1: return myfpregs.xmm_space[45]; + case XMM11_2: return myfpregs.xmm_space[46]; + case XMM11_3: return myfpregs.xmm_space[47]; + case XMM12_0: return myfpregs.xmm_space[48]; + case XMM12_1: return myfpregs.xmm_space[49]; + case XMM12_2: return myfpregs.xmm_space[50]; + case XMM12_3: return myfpregs.xmm_space[51]; + case XMM13_0: return myfpregs.xmm_space[52]; + case XMM13_1: return myfpregs.xmm_space[53]; + case XMM13_2: return myfpregs.xmm_space[54]; + case XMM13_3: return myfpregs.xmm_space[55]; + case XMM14_0: return myfpregs.xmm_space[56]; + case XMM14_1: return myfpregs.xmm_space[57]; + case XMM14_2: return myfpregs.xmm_space[58]; + case XMM14_3: return myfpregs.xmm_space[59]; + case XMM15_0: return myfpregs.xmm_space[60]; + case XMM15_1: return myfpregs.xmm_space[61]; + case XMM15_2: return myfpregs.xmm_space[62]; + case XMM15_3: return myfpregs.xmm_space[63]; + default: + assert(0); + return 0; + } +} + +bool +AMD64TraceChild::update(int pid) +{ + oldregs = regs; + oldfpregs = fpregs; + if (ptrace(PTRACE_GETREGS, pid, 0, ®s) != 0) { + cerr << "update: " << strerror(errno) << endl; + return false; + } + if (ptrace(PTRACE_GETFPREGS, pid, 0, &fpregs) != 0) { + cerr << "update: " << strerror(errno) << endl; + return false; + } + for (unsigned int x = 0; x < numregs; x++) + regDiffSinceUpdate[x] = (getRegVal(x) != getOldRegVal(x)); + return true; +} + +AMD64TraceChild::AMD64TraceChild() +{ + for (unsigned int x = 0; x < numregs; x++) + regDiffSinceUpdate[x] = false; +} + +int64_t +AMD64TraceChild::getRegVal(int num) +{ + return getRegs(regs, fpregs, num); +} + +int64_t +AMD64TraceChild::getOldRegVal(int num) +{ + return getRegs(oldregs, oldfpregs, num); +} + +ostream & +AMD64TraceChild::outputStartState(ostream & os) +{ + uint64_t sp = getSP(); + uint64_t pc = getPC(); + uint64_t highestInfo = 0; + char obuf[1024]; + sprintf(obuf, "Initial stack pointer = 0x%016lx\n", sp); + os << obuf; + sprintf(obuf, "Initial program counter = 0x%016lx\n", pc); + os << obuf; + + //Output the argument count + uint64_t cargc = ptrace(PTRACE_PEEKDATA, pid, sp, 0); + sprintf(obuf, "0x%016lx: Argc = 0x%016lx\n", sp, cargc); + os << obuf; + sp += 8; + + //Output argv pointers + int argCount = 0; + uint64_t cargv; + do { + cargv = ptrace(PTRACE_PEEKDATA, pid, sp, 0); + sprintf(obuf, "0x%016lx: argv[%d] = 0x%016lx\n", + sp, argCount++, cargv); + if (cargv) + if (highestInfo < cargv) + highestInfo = cargv; + os << obuf; + sp += 8; + } while(cargv); + + //Output the envp pointers + int envCount = 0; + uint64_t cenvp; + do { + cenvp = ptrace(PTRACE_PEEKDATA, pid, sp, 0); + sprintf(obuf, "0x%016lx: envp[%d] = 0x%016lx\n", + sp, envCount++, cenvp); + os << obuf; + sp += 8; + } while(cenvp); + uint64_t auxType, auxVal; + do { + auxType = ptrace(PTRACE_PEEKDATA, pid, sp, 0); + sp += 8; + auxVal = ptrace(PTRACE_PEEKDATA, pid, sp, 0); + sp += 8; + sprintf(obuf, "0x%016lx: Auxiliary vector = {0x%016lx, 0x%016lx}\n", + sp - 16, auxType, auxVal); + os << obuf; + } while(auxType != 0 || auxVal != 0); + //Print out the argument strings, environment strings, and file name. + string current; + uint64_t buf; + uint64_t currentStart = sp; + bool clearedInitialPadding = false; + do { + buf = ptrace(PTRACE_PEEKDATA, pid, sp, 0); + char * cbuf = (char *)&buf; + for (int x = 0; x < sizeof(uint64_t); x++) { + if (cbuf[x]) + current += cbuf[x]; + else { + sprintf(obuf, "0x%016lx: \"%s\"\n", + currentStart, current.c_str()); + os << obuf; + current = ""; + currentStart = sp + x + 1; + } + } + sp += 8; + clearedInitialPadding = clearedInitialPadding || buf != 0; + } while (!clearedInitialPadding || buf != 0 || sp <= highestInfo); + return os; +} + +uint64_t +AMD64TraceChild::findSyscall() +{ + uint64_t rip = getPC(); + bool foundOpcode = false; + bool twoByteOpcode = false; + for (;;) { + uint64_t buf = ptrace(PTRACE_PEEKDATA, pid, rip, 0); + for (int i = 0; i < sizeof(uint64_t); i++) { + unsigned char byte = buf & 0xFF; + if (!foundOpcode) { + if(!(byte == 0x66 || //operand override + byte == 0x67 || //address override + byte == 0x2E || //cs + byte == 0x3E || //ds + byte == 0x26 || //es + byte == 0x64 || //fs + byte == 0x65 || //gs + byte == 0x36 || //ss + byte == 0xF0 || //lock + byte == 0xF2 || //repe + byte == 0xF3 || //repne + (byte >= 0x40 && byte <= 0x4F) // REX + )) { + foundOpcode = true; + } + } + if (foundOpcode) { + if (twoByteOpcode) { + //SYSCALL or SYSENTER + if (byte == 0x05 || byte == 0x34) + return rip + 1; + else + return 0; + } + if (!twoByteOpcode) { + if (byte == 0xCC) // INT3 + return rip + 1; + else if (byte == 0xCD) // INT with byte immediate + return rip + 2; + else if (byte == 0x0F) // two byte opcode prefix + twoByteOpcode = true; + else + return 0; + } + } + buf >>= 8; + rip++; + } + } +} + +bool +AMD64TraceChild::step() +{ + uint64_t ripAfterSyscall = findSyscall(); + if (ripAfterSyscall) { + //Get the original contents of memory + uint64_t buf = ptrace(PTRACE_PEEKDATA, pid, ripAfterSyscall, 0); + //Patch the first two bytes of the memory immediately after this with + //jmp -2. Either single stepping will take over before this + //instruction, leaving the rip where it should be, or it will take + //over after this instruction, -still- leaving the rip where it should + //be. + uint64_t newBuf = (buf & ~0xFFFF) | 0xFEEB; + //Write the patched memory to the processes address space + ptrace(PTRACE_POKEDATA, pid, ripAfterSyscall, newBuf); + //Step and hit it + ptraceSingleStep(); + //Put things back to the way they started + ptrace(PTRACE_POKEDATA, pid, ripAfterSyscall, buf); + } else { + //Get all the way past repe and repne string instructions in one shot. + uint64_t newPC, origPC = getPC(); + do { + ptraceSingleStep(); + newPC = getPC(); + } while(newPC == origPC); + } +} + +TraceChild * genTraceChild() +{ + return new AMD64TraceChild; +} |