/// <summary> /// 计算大于等于 size 字节的最少指令的长度 /// </summary> /// <param name="code"></param> /// <returns></returns> public static uint SizeofMinNumByte(void *code, int size) { if (NativeAPI.IsAndroidARM()) { return((uint)((size + 3) / 4) * 4); // 此为 jit 模式下的长度 } UInt32 Length; byte * pOpcode; UInt32 Result = 0; ldasm_data data = new ldasm_data(); bool is64 = IntPtr.Size == 8; do { Length = ldasm(code, data, is64); pOpcode = (byte *)code + data.opcd_offset; Result += Length; if (Result >= size) { break; } if ((Length == 1) && (*pOpcode == 0xCC)) { break; } code = (void *)((ulong)code + Length); } while (Length > 0); return(Result); }
/// <summary> /// 计算大于等于5字节的最少指令的长度 /// </summary> /// <param name="code"></param> /// <returns></returns> public static uint SizeofMin5Byte(void *code) { UInt32 Length; byte * pOpcode; UInt32 Result = 0; ldasm_data data = new ldasm_data(); bool is64 = IntPtr.Size == 8; do { Length = ldasm(code, data, is64); pOpcode = (byte *)code + data.opcd_offset; Result += Length; if (Result >= 5) { break; } if ((Length == 1) && (*pOpcode == 0xCC)) { break; } code = (void *)((ulong)code + Length); } while (Length > 0); return(Result); }
public static int GetASMLength(byte[] b, int index, bool x64) { unsafe { IntPtr addr = Marshal.AllocHGlobal(b.Length); Marshal.Copy(b, 0, addr, b.Length); ldasm_data data = new ldasm_data(); uint i = ldasm((byte *)addr, ref data, x64); Marshal.FreeHGlobal(addr); return((int)i); } }
public static bool CheckShortCall(void *code, int size, out int index) { bool result = false; index = -1; if (NativeAPI.IsAndroidARM()) { return(result); } UInt32 oneLength; byte * pOpcode; UInt32 calledLength = 0; ldasm_data data = new ldasm_data(); bool is64 = IntPtr.Size == 8; do { oneLength = ldasm(code, data, is64); pOpcode = (byte *)code + data.opcd_offset; calledLength += oneLength; if (calledLength >= size) { break; } if ((oneLength == 1) && (*pOpcode == 0xCC)) { break; } byte instruct = ((byte *)code)[calledLength]; if (instruct == 0xE8 || instruct == 0xE9) { index = (int)calledLength; result = true; break; } code = (void *)((ulong)code + oneLength); } while (oneLength > 0); return(result); }
static uint ldasm(void *code, ldasm_data ld, bool is64) { byte *p = (byte *)code; byte s, op, f; byte rexw, pr_66, pr_67; s = rexw = pr_66 = pr_67 = 0; /* dummy check */ if ((int)code == 0) { return(0); } /* init output data */ //memset(ld, 0, sizeof(ldasm_data)); /* phase 1: parse prefixies */ while ((cflags(*p) & OP_PREFIX) != 0) { if (*p == 0x66) { pr_66 = 1; } if (*p == 0x67) { pr_67 = 1; } p++; s++; ld.flags |= F_PREFIX; if (s == 15) { ld.flags |= F_INVALID; return(s); } } /* parse REX prefix */ if (is64 && *p >> 4 == 4) { ld.rex = *p; rexw = (byte)((ld.rex >> 3) & 1); ld.flags |= F_REX; p++; s++; } /* can be only one REX prefix */ if (is64 && *p >> 4 == 4) { ld.flags |= F_INVALID; s++; return(s); } /* phase 2: parse opcode */ ld.opcd_offset = (byte)(p - (byte *)code); ld.opcd_size = 1; op = *p++; s++; /* is 2 byte opcode? */ if (op == 0x0F) { op = *p++; s++; ld.opcd_size++; f = cflags_ex(op); if ((f & OP_INVALID) != 0) { ld.flags |= F_INVALID; return(s); } /* for SSE instructions */ if ((f & OP_EXTENDED) != 0) { op = *p++; s++; ld.opcd_size++; } } else { f = cflags(op); /* pr_66 = pr_67 for opcodes A0-A3 */ if (op >= 0xA0 && op <= 0xA3) { pr_66 = pr_67; } } /* phase 3: parse ModR/M, SIB and DISP */ if ((f & OP_MODRM) != 0) { byte mod = (byte)(*p >> 6); byte ro = (byte)((*p & 0x38) >> 3); byte rm = (byte)(*p & 7); ld.modrm = *p++; s++; ld.flags |= F_MODRM; /* in F6,F7 opcodes immediate data present if R/O == 0 */ if (op == 0xF6 && (ro == 0 || ro == 1)) { f |= OP_DATA_I8; } if (op == 0xF7 && (ro == 0 || ro == 1)) { f |= OP_DATA_I16_I32_I64; } /* is SIB byte exist? */ if (mod != 3 && rm == 4 && !(!is64 && pr_67 != 0)) { ld.sib = *p++; s++; ld.flags |= F_SIB; /* if base == 5 and mod == 0 */ if ((ld.sib & 7) == 5 && mod == 0) { ld.disp_size = 4; } } switch (mod) { case 0: if (is64) { if (rm == 5) { ld.disp_size = 4; if (is64) { ld.flags |= F_RELATIVE; } } } else if (pr_67 != 0) { if (rm == 6) { ld.disp_size = 2; } } else { if (rm == 5) { ld.disp_size = 4; } } break; case 1: ld.disp_size = 1; break; case 2: if (is64) { ld.disp_size = 4; } else if (pr_67 != 0) { ld.disp_size = 2; } else { ld.disp_size = 4; } break; } if (ld.disp_size > 0) { ld.disp_offset = (byte)(p - (byte *)code); p += ld.disp_size; s += ld.disp_size; ld.flags |= F_DISP; } } /* phase 4: parse immediate data */ if (rexw != 0 && (f & OP_DATA_I16_I32_I64) != 0) { ld.imm_size = 8; } else if ((f & OP_DATA_I16_I32) != 0 || (f & OP_DATA_I16_I32_I64) != 0) { ld.imm_size = (byte)(4 - (pr_66 << 1)); } /* if exist, add OP_DATA_I16 and OP_DATA_I8 size */ ld.imm_size += (byte)(f & 3); if ((ld.imm_size) != 0) { s += ld.imm_size; ld.imm_offset = (byte)(p - (byte *)code); ld.flags |= F_IMM; if ((f & OP_RELATIVE) != 0) { ld.flags |= F_RELATIVE; } } /* instruction is too long */ if (s > 15) { ld.flags |= F_INVALID; } return(s); }
/// <summary> /// Performs a SingleStep operation, that is to stay it resumes the process and pauses at the very next instruction. /// Jumps are followed, conditional jumps are evaluated, Calls are either stepped into or over depending on StepIntoCalls value. /// </summary> /// <returns></returns> public NIDebugger64 SingleStep() { getContext(getCurrentThreadId()); ulong address = Context.Rip; if (address == 0x7ffa79b6660e) { int i = 0; } byte[] data; ReadData(address, 16, out data); if (breakpoints.ContainsKey(address) == true) { Array.Copy(breakpoints[address].originalBytes, data, 2); ClearBreakpoint(address); } ldasm_data ldata = lde.ldasm(data, 0, false); uint size = ldata.size; ulong nextAddress = Context.Rip + size; if (ldata.opcd_size == 1 && (data[ldata.opcd_offset] == 0xEB)) { // we have a 1 byte JMP here sbyte offset = (sbyte)data[ldata.imm_offset]; nextAddress = (ulong)((long)Context.Rip + offset) + ldata.size; } if ((data[ldata.opcd_offset] == 0xE2)) { // LOOP if (Context.Rcx == 1) { // this instruction will make ECX 0, so we fall thru the jump now nextAddress = (ulong)((long)Context.Rip + ldata.size); } else if (Context.Rcx > 1) { // this instruction will decrement ECX but it wont be 0 yet, so jump! sbyte disp = (sbyte)data[1]; nextAddress = (uint)((long)Context.Rip + disp) + ldata.size; } } if ((data[ldata.opcd_offset] == 0xE0)) { //LOOPNZ LOOPNE if (Context.Rcx == 1 && Context.GetFlag(NIContextFlag.ZERO) == false) { nextAddress = (ulong)((long)Context.Rip + ldata.size); } else if (Context.Rcx > 1 || Context.GetFlag(NIContextFlag.ZERO) != false) { sbyte disp = (sbyte)data[1]; nextAddress = (ulong)((long)Context.Rip + disp) + ldata.size; } } if ((data[ldata.opcd_offset] == 0xE1)) { //LOOPZ LOOPE if (Context.Rcx == 1 && Context.GetFlag(NIContextFlag.ZERO) == true) { nextAddress = (ulong)((long)Context.Rip + ldata.size); } else if (Context.Rcx > 1 || Context.GetFlag(NIContextFlag.ZERO) != true) { sbyte disp = (sbyte)data[1]; nextAddress = (ulong)((long)Context.Rip + disp) + ldata.size; } } if (ldata.opcd_size == 1 && ((data[ldata.opcd_offset] == 0xE9) || (data[ldata.opcd_offset] == 0xE8))) { // we have a long JMP or CALL here int offset = BitConverter.ToInt32(data, ldata.imm_offset); if ((data[ldata.opcd_offset] == 0xE9) || (StepIntoCalls && (data[ldata.opcd_offset] == 0xE8))) { nextAddress = (ulong)((long)Context.Rip + offset) + ldata.size; } } if (ldata.opcd_size == 1 && ((data[ldata.opcd_offset] >= 0x70 && (data[ldata.opcd_offset] <= 0x7F)) || (data[ldata.opcd_offset] == 0xE3))) { // we have a 1byte jcc here bool willJump = evalJcc(data[ldata.opcd_offset]); if (willJump) { sbyte offset = (sbyte)data[ldata.imm_offset]; nextAddress = (ulong)((long)Context.Rip + offset) + ldata.size; } } if (ldata.opcd_size == 2 && ((data[ldata.opcd_offset] == 0x0F) || (data[ldata.opcd_offset + 1] == 0x80))) { // we have a 2 byte jcc here bool willJump = evalJcc(data[ldata.opcd_offset + 1]); if (willJump) { int offset = BitConverter.ToInt32(data, ldata.imm_offset); nextAddress = (ulong)(((long)Context.Rip + offset) + ldata.size); } } if (data[ldata.opcd_offset] == 0xC3 || data[ldata.opcd_offset] == 0xC2) { ReadStackValue(0, out nextAddress); } if (data[ldata.opcd_offset] == 0xFF && ldata.opcd_size == 1 && ldata.modrm != 0x00) { // let's parse ModRM! var reg2 = (ldata.modrm & 0x38) >> 3; var mod = (ldata.modrm & 0xC0) >> 6; var reg1 = (ldata.modrm & 0x7); bool addressSet = false; if (reg2 == 2) { if (StepIntoCalls == false) { nextAddress = (uint)Context.Rip + ldata.size; addressSet = true; } Console.Write("RegOp tells me this is a CALL\r\n"); } else if (reg2 == 4) { Console.Write("RegOp tells me this is a JMP\r\n"); } else { nextAddress = (uint)Context.Rip + ldata.size; addressSet = true; } if (addressSet == false) { if (reg1 == 4) { //txtFacts.Text += "Reg1 is a 4 which means there is a SIB byte\r\n"; var ss = (ldata.sib & 0xC0) >> 6; var index = (ldata.sib & 0x38) >> 3; var Base = (ldata.sib & 0x07); int scale = (int)Math.Pow(2, ss); nextAddress = (uint)GetRegisterByNumber(index) * (uint)scale; if (Base == 5) { if (mod == 0) { nextAddress = (uint)((int)nextAddress + BitConverter.ToInt32(data, ldata.disp_offset)); } else if (mod == 1) { nextAddress += GetRegisterByNumber(Base); nextAddress = (uint)((int)(nextAddress) + (sbyte)data[ldata.disp_offset]); } else if (mod == 2) { nextAddress += GetRegisterByNumber(Base); nextAddress = (uint)((int)nextAddress + BitConverter.ToInt32(data, ldata.disp_offset)); } } } else { if (mod == 0) { if (reg1 != 5) { nextAddress = GetRegisterByNumber(reg1); } else { nextAddress = (uint)BitConverter.ToInt32(data, ldata.disp_offset); } } else if (mod == 1) { nextAddress = GetRegisterByNumber(reg1); nextAddress = (uint)((int)(nextAddress) + (sbyte)data[ldata.disp_offset]); } else if (mod == 2) { nextAddress = GetRegisterByNumber(reg1); nextAddress = (uint)((int)nextAddress + BitConverter.ToInt32(data, ldata.disp_offset)); } } if (mod != 3) { //ReadDWORD(nextAddress, out nextAddress); } Console.WriteLine("Next Address: " + nextAddress.ToString("X8")); } } updateContext(getCurrentThreadId()); SetBreakpoint(nextAddress); Continue(); ClearBreakpoint(nextAddress); return(this); }