Ejemplo n.º 1
0
 public ByteStream(ByteStream byteStream, int offset)
 {
     this.bytes = byteStream.bytes;
     this.offset = byteStream.offset + offset;
 }
Ejemplo n.º 2
0
        public t_disasm Disasm(ByteStream src, ulong srcip, int disasmmode)
        {
            da = new t_disasm();

            int i, j, operand, mnemosize, arg;
            ulong u;
            int cxsize;

            // Prepare disassembler variables and initialize structure disasm.
            datasize = addrsize = 4; // 32-bit code and data segments only!
            segprefix = SEG_UNDEF;
            hasrm = false;
            hassib = false;
            dispsize = immsize = 0;
            bool lockprefix = false; // Non-zero if lock prefix present
            byte repprefix = 0; // REPxxx prefix or 0

            cmd = new ByteStream(src, 0);

            pfixup = -1;
            softerror = 0;
            bool is3dnow = false;

            da.ip = srcip;
            da.comment = string.Empty;
            da.cmdtype = C_BAD; da.nprefix = 0;
            da.memtype = DEC_UNKNOWN; da.indexed = 0;
            da.jmpconst = 0; da.jmptable = 0;
            da.adrconst = 0; da.immconst = 0;
            da.zeroconst = 0;
            da.fixupoffset = 0;
            da.fixupsize = 0;
            da.warnings = 0;
            da.error = DAE_NOERR;
            mode = disasmmode; // No need to use register contents

            // Correct 80x86 command may theoretically contain up to 4 prefixes belonging
            // to different prefix groups. This limits maximal possible size of the
            // command to MAXCMDSIZE=16 bytes. In order to maintain this limit, if
            // Disasm() detects second prefix from the same group, it flushes first
            // prefix in the sequence as a pseudocommand.
            u = 0;
            bool repeated = false;
            while (size > 0)
            {
                bool isprefix = true; // Assume that there is some prefix
                switch (cmd[0])
                {
                    case 0x26: if (segprefix == SEG_UNDEF) segprefix = SEG_ES;
                        else repeated = true; break;
                    case 0x2E: if (segprefix == SEG_UNDEF) segprefix = SEG_CS;
                        else repeated = true; break;
                    case 0x36: if (segprefix == SEG_UNDEF) segprefix = SEG_SS;
                        else repeated = true; break;
                    case 0x3E: if (segprefix == SEG_UNDEF) segprefix = SEG_DS;
                        else repeated = true; break;
                    case 0x64: if (segprefix == SEG_UNDEF) segprefix = SEG_FS;
                        else repeated = true; break;
                    case 0x65: if (segprefix == SEG_UNDEF) segprefix = SEG_GS;
                        else repeated = true; break;
                    case 0x66: if (datasize == 4) datasize = 2;
                        else repeated = true; break;
                    case 0x67: if (addrsize == 4) addrsize = 2;
                        else repeated = true; break;
                    case 0xF0: if (!lockprefix) repprefix = 0xF0; // lockprefix = 0xF0;
                        else repeated = true; break;
                    case 0xF2: if (repprefix == 0) repprefix = 0xF2;
                        else repeated = true; break;
                    case 0xF3: if (repprefix == 0) repprefix = 0xF3;
                        else repeated = true; break;
                    default: isprefix = false; break;
                };
                if (!isprefix || repeated)
                    break; // No more prefixes or duplicated prefix

                if (mode >= DISASM_FILE)
                    da.dump.AppendFormat("{0:X}:", cmd[0]);

                da.nprefix++;
                cmd.AdjustOffset(1);
                srcip++;
                u++;
            };

            // We do have repeated prefix. Flush first prefix from the sequence.
            if (repeated)
            {
                if (mode >= DISASM_FILE)
                {
                    //da.dump[3] = '\0'; // Leave only first dumped prefix
                    string pname;
                    da.nprefix = 1;
                    switch (cmd[(int)-(long)u])
                    {
                        case 0x26: pname = segname[SEG_ES]; break;
                        case 0x2E: pname = segname[SEG_CS]; break;
                        case 0x36: pname = segname[SEG_SS]; break;
                        case 0x3E: pname = segname[SEG_DS]; break;
                        case 0x64: pname = segname[SEG_FS]; break;
                        case 0x65: pname = segname[SEG_GS]; break;
                        case 0x66: pname = "DATASIZE"; break;
                        case 0x67: pname = "ADDRSIZE"; break;
                        case 0xF0: pname = "LOCK"; break;
                        case 0xF2: pname = "REPNE"; break;
                        case 0xF3: pname = "REPE"; break;
                        default: pname = "?"; break;
                    };
                    da.result.AppendFormat("PREFIX {0}:", pname);

                    if (!extraprefix)
                        da.comment = "Superfluous prefix";
                };
                da.warnings |= DAW_PREFIX;
                if (lockprefix)
                    da.warnings |= DAW_LOCK;
                da.cmdtype = C_RARE;

                return da; // 1; // Any prefix is 1 byte long
            };
            // If lock prefix available, display it and forget, because it has no
            // influence on decoding of rest of the command.
            if (lockprefix)
            {
                if (mode >= DISASM_FILE)
                    da.result.Append("LOCK ");
                da.warnings |= DAW_LOCK;
            };
            // Fetch (if available) first 3 bytes of the command, add repeat prefix and
            // find command in the command table.
            ulong code = 0;
            if (cmd.SizeLeft > 0) code = cmd[0];
            if (cmd.SizeLeft > 1) code = code + (ulong)(cmd[1] << 8);
            if (cmd.SizeLeft > 2) code = code + (ulong)(cmd[2] << 16);
            if (repprefix != 0) // RER/REPE/REPNE is considered to be
                code = (code << 8) | repprefix; // part of command.

            t_cmddata pd = cmddata[0]; // Assignment to first entry is to avoid compiler error
            int pd_index = 0;

            if (decodevxd && (code & 0xFFFF) == 0x20CD)
                pd = vxdcmd; // Decode VxD call (Win95/98)
            else
            {
                for (pd_index = 0; pd_index < cmddata.Length; pd_index++)
                {
                    pd = cmddata[pd_index];
                    if (((code ^ pd.code) & pd.mask) != 0)
                        continue;

                    if (mode >= DISASM_FILE && shortstringcmds && (pd.arg1 == MSO || pd.arg1 == MDE || pd.arg2 == MSO || pd.arg2 == MDE))
                        continue; // Search short form of string command

                    break;
                };
            };

            if ((pd.type & C_TYPEMASK) == C_NOW)
            {
                // 3DNow! commands require additional search.
                is3dnow = true;
                j = Get3dnowsuffix();
                if (j < 0)
                    da.error = DAE_CROSS;
                else
                {
                    //for (; pd.mask != 0; pd++)
                    for (; pd_index < cmddata.Length; pd_index++)
                    {
                        pd = cmddata[pd_index];
                        if (((code ^ pd.code) & pd.mask) != 0)
                            continue;

                        //if (((uchar*)&(pd.code))[2] == j)
                        if ((int)((pd.code & 0xFF0000) >> 24) == j)
                            break;
                    };
                };
            };
            if (pd.mask == 0)
            { // Command not found
                da.cmdtype = C_BAD;
                if (cmd.SizeLeft < 2) da.error = DAE_CROSS;
                else da.error = DAE_BADCMD;
            }
            else
            { // Command recognized, decode it
                da.cmdtype = pd.type;
                cxsize = datasize; // Default size of ECX used as counter
                if (segprefix == SEG_FS || segprefix == SEG_GS || lockprefix)
                    da.cmdtype |= C_RARE; // These prefixes are rare
                if (pd.bits == PR)
                    da.warnings |= DAW_PRIV; // Privileged command (ring 0)
                else if (pd.bits == WP)
                    da.warnings |= DAW_IO; // I/O command
                // Win32 programs usually try to keep stack dword-aligned, so INC ESP
                // (44) and DEC ESP (4C) usually don't appear in real code. Also check for
                // ADD ESP,imm and SUB ESP,imm (81,C4,imm32; 83,C4,imm8; 81,EC,imm32;
                // 83,EC,imm8).
                if (cmd[0] == 0x44 || cmd[0] == 0x4C ||
                  (cmd.SizeLeft >= 3 && (cmd[0] == 0x81 || cmd[0] == 0x83) &&
                  (cmd[1] == 0xC4 || cmd[1] == 0xEC) && (cmd[2] & 0x03) != 0)
                )
                {
                    da.warnings |= DAW_STACK;
                    da.cmdtype |= C_RARE;
                };
                // Warn also on MOV SEG,... (8E...). Win32 works in flat mode.
                if (cmd[0] == 0x8E)
                    da.warnings |= DAW_SEGMENT;
                // If opcode is 2-byte, adjust command.
                if (pd.len == 2)
                {
                    if (cmd.SizeLeft == 0) da.error = DAE_CROSS;
                    else
                    {
                        if (mode >= DISASM_FILE)
                            da.dump.AppendFormat("{0:X}", cmd[0]);

                        cmd.AdjustOffset(1);

                        srcip++;
                    };
                };
                if (cmd.SizeLeft == 0) da.error = DAE_CROSS;
                // Some commands either feature non-standard data size or have bit which
                // allows to select data size.
                if ((pd.bits & WW) != 0 && (cmd[0] & WW) == 0)
                    datasize = 1; // Bit W in command set to 0
                else if ((pd.bits & W3) != 0 && (cmd[0] & W3) == 0)
                    datasize = 1; // Another position of bit W
                else if ((pd.bits & FF) != 0)
                    datasize = 2; // Forced word (2-byte) size
                // Some commands either have mnemonics which depend on data size (8/16 bits
                // or 32 bits, like CWD/CDQ), or have several different mnemonics (like
                // JNZ/JNE). First case is marked by either '&' (mnemonic depends on
                // operand size) or '$' (depends on address size). In the second case,
                // there is no special marker and disassembler selects main mnemonic.
                if (mode >= DISASM_FILE)
                {
                    string name = string.Empty;

                    if (pd.name[0] == '&')
                        mnemosize = datasize;
                    else if (pd.name[0] == '$')
                        mnemosize = addrsize;
                    else mnemosize = 0;

                    if (mnemosize != 0)
                    {
                        for (i = 0, j = 1; pd.name[j] != '\0'; j++)
                        {
                            if (pd.name[j] == ':')
                            { // Separator between 16/32 mnemonics
                                if (mnemosize == 4) i = 0;
                                else break;
                            }
                            else if (pd.name[j] == '*')
                            { // Substitute by 'W', 'D' or none
                                if (mnemosize == 4 && sizesens != 2)
                                    name = name + 'D';
                                else if (mnemosize != 4 && sizesens != 0)
                                    name = name + 'W';
                            }
                            else
                                name = name + pd.name[j];
                        };
                        //name[i] = '\0';
                    }
                    else
                    {
                        name = pd.name;

                        int comma_index = name.IndexOf(',');
                        if (comma_index >= 0)
                            name = name.Substring(0, comma_index - 1);
                    };

                    da.result.Append(name);
                };
                // Decode operands (explicit - encoded in command, implicit - present in
                // mmemonic or assumed - used or modified by command). Assumed operands
                // must stay after all explicit and implicit operands. Up to 3 operands
                // are allowed.
                for (operand = 0; operand < 3; operand++)
                {
                    if (da.error != 0) break; // Error - no sense to continue
                    // If command contains both source and destination, one usually must not
                    // decode destination to comment because it will be overwritten on the
                    // next step. Global addcomment takes care of this. Decoding routines,
                    // however, may ignore this flag.
                    if (operand == 0 && pd.arg2 != NNN && pd.arg2 < PSEUDOOP)
                        addcomment = false;
                    else
                        addcomment = true;

                    // Get type of next argument.
                    if (operand == 0) arg = pd.arg1;
                    else if (operand == 1) arg = pd.arg2;
                    else arg = pd.arg3;

                    if (arg == NNN) break; // No more operands

                    // Arguments with arg>=PSEUDOOP are assumed operands and are not
                    // displayed in disassembled result, so they require no delimiter.
                    if ((mode >= DISASM_FILE) && arg < PSEUDOOP)
                    {
                        if (operand == 0)
                            da.result.Append(' ');
                        else
                            da.result.Append(',');
                    };

                    // Decode, analyse and comment next operand of the command.
                    switch (arg)
                    {
                        case REG: // Integer register in Reg field
                            if (cmd.SizeLeft < 2) da.error = DAE_CROSS;
                            else DecodeRG(cmd[1] >> 3, datasize, REG);
                            hasrm = true; break;
                        case RCM: // Integer register in command byte
                            DecodeRG(cmd[0], datasize, RCM); break;
                        case RG4: // Integer 4-byte register in Reg field
                            if (cmd.SizeLeft < 2) da.error = DAE_CROSS;
                            else DecodeRG(cmd[1] >> 3, 4, RG4);
                            hasrm = true; break;
                        case RAC: // Accumulator (AL/AX/EAX, implicit)
                            DecodeRG(REG_EAX, datasize, RAC); break;
                        case RAX: // AX (2-byte, implicit)
                            DecodeRG(REG_EAX, 2, RAX); break;
                        case RDX: // DX (16-bit implicit port address)
                            DecodeRG(REG_EDX, 2, RDX); break;
                        case RCL: // Implicit CL register (for shifts)
                            DecodeRG(REG_ECX, 1, RCL); break;
                        case RS0: // Top of FPU stack (ST(0))
                            DecodeST(0, 0); break;
                        case RST: // FPU register (ST(i)) in command byte
                            DecodeST(cmd[0], 0); break;
                        case RMX: // MMX register MMx
                            if (cmd.SizeLeft < 2) da.error = DAE_CROSS;
                            else DecodeMX(cmd[1] >> 3);
                            hasrm = true; break;
                        case R3D: // 3DNow! register MMx
                            if (cmd.SizeLeft < 2) da.error = DAE_CROSS;
                            else DecodeNR(cmd[1] >> 3);
                            hasrm = true; break;
                        case MRG: // Memory/register in ModRM byte
                        case MRJ: // Memory/reg in ModRM as JUMP target
                        case MR1: // 1-byte memory/register in ModRM byte
                        case MR2: // 2-byte memory/register in ModRM byte
                        case MR4: // 4-byte memory/register in ModRM byte
                        case MR8: // 8-byte memory/MMX register in ModRM
                        case MRD: // 8-byte memory/3DNow! register in ModRM
                        case MMA: // Memory address in ModRM byte for LEA
                        case MML: // Memory in ModRM byte (for LES)
                        case MM6: // Memory in ModRm (6-byte descriptor)
                        case MMB: // Two adjacent memory locations (BOUND)
                        case MD2: // Memory in ModRM byte (16-bit integer)
                        case MB2: // Memory in ModRM byte (16-bit binary)
                        case MD4: // Memory in ModRM byte (32-bit integer)
                        case MD8: // Memory in ModRM byte (64-bit integer)
                        case MDA: // Memory in ModRM byte (80-bit BCD)
                        case MF4: // Memory in ModRM byte (32-bit float)
                        case MF8: // Memory in ModRM byte (64-bit float)
                        case MFA: // Memory in ModRM byte (80-bit float)
                        case MFE: // Memory in ModRM byte (FPU environment)
                        case MFS: // Memory in ModRM byte (FPU state)
                        case MFX: // Memory in ModRM byte (ext. FPU state)
                            DecodeMR(arg); break;
                        case MMS: // Memory in ModRM byte (as SEG:OFFS)
                            DecodeMR(arg);
                            da.warnings |= DAW_FARADDR; break;
                        case RR4: // 4-byte memory/register (register only)
                        case RR8: // 8-byte MMX register only in ModRM
                        case RRD: // 8-byte memory/3DNow! (register only)
                            if ((cmd[1] & 0xC0) != 0xC0) softerror = DAE_REGISTER;
                            DecodeMR(arg); break;
                        case MSO: // Source in string op's ([ESI])
                            DecodeSO(); break;
                        case MDE: // Destination in string op's ([EDI])
                            DecodeDE(); break;
                        case MXL: // XLAT operand ([EBX+AL])
                            DecodeXL(); break;
                        case IMM: // Immediate data (8 or 16/32)
                        case IMU: // Immediate unsigned data (8 or 16/32)
                            if ((pd.bits & SS) != 0 && (cmd[0] & 0x02) != 0)
                                DecodeIM(1, datasize, arg);
                            else
                                DecodeIM(datasize, 0, arg);
                            break;
                        case VXD: // VxD service (32-bit only)
                            DecodeVX(); break;
                        case IMX: // Immediate sign-extendable byte
                            DecodeIM(1, datasize, arg); break;
                        case C01: // Implicit constant 1 (for shifts)
                            DecodeC1(); break;
                        case IMS: // Immediate byte (for shifts)
                        case IM1: // Immediate byte
                            DecodeIM(1, 0, arg); break;
                        case IM2: // Immediate word (ENTER/RET)
                            DecodeIM(2, 0, arg);
                            if ((da.immconst & 0x03) != 0) da.warnings |= DAW_STACK;
                            break;
                        case IMA: // Immediate absolute near data address
                            DecodeIA(); break;
                        case JOB: // Immediate byte offset (for jumps)
                            DecodeRJ(1, srcip + 2); break;
                        case JOW: // Immediate full offset (for jumps)
                            DecodeRJ((ulong)datasize, (ulong)((int)srcip + datasize + 1)); break;
                        case JMF: // Immediate absolute far jump/call addr
                            DecodeJF();
                            da.warnings |= DAW_FARADDR; break;
                        case SGM: // Segment register in ModRM byte
                            if (cmd.SizeLeft < 2) da.error = DAE_CROSS;
                            DecodeSG(cmd[1] >> 3); hasrm = true; break;
                        case SCM: // Segment register in command byte
                            DecodeSG(cmd[0] >> 3);
                            if ((da.cmdtype & C_TYPEMASK) == C_POP) da.warnings |= DAW_SEGMENT;
                            break;
                        case CRX: // Control register CRx
                            if ((cmd[1] & 0xC0) != 0xC0) da.error = DAE_REGISTER;
                            DecodeCR(cmd[1]); break;
                        case DRX: // Debug register DRx
                            if ((cmd[1] & 0xC0) != 0xC0) da.error = DAE_REGISTER;
                            DecodeDR(cmd[1]); break;
                        case PRN: // Near return address (pseudooperand)
                            break;
                        case PRF: // Far return address (pseudooperand)
                            da.warnings |= DAW_FARADDR; break;
                        case PAC: // Accumulator (AL/AX/EAX, pseudooperand)
                            DecodeRG(REG_EAX, datasize, PAC); break;
                        case PAH: // AH (in LAHF/SAHF, pseudooperand)
                        case PFL: // Lower byte of flags (pseudooperand)
                            break;
                        case PS0: // Top of FPU stack (pseudooperand)
                            DecodeST(0, 1); break;
                        case PS1: // ST(1) (pseudooperand)
                            DecodeST(1, 1); break;
                        case PCX: // CX/ECX (pseudooperand)
                            DecodeRG(REG_ECX, cxsize, PCX); break;
                        case PDI: // EDI (pseudooperand in MMX extentions)
                            DecodeRG(REG_EDI, 4, PDI); break;
                        default:
                            da.error = DAE_INTERN; // Unknown argument type
                            break;
                    };
                };

                // Check whether command may possibly contain fixups.
                if (pfixup != -1 && da.fixupsize > 0)
                    da.fixupoffset = pfixup;

                // Segment prefix and address size prefix are superfluous for command which
                // does not access memory. If this the case, mark command as rare to help
                // in analysis.
                if (da.memtype == DEC_UNKNOWN && (segprefix != SEG_UNDEF || (addrsize != 4 && pd.name[0] != '$')))
                {
                    da.warnings |= DAW_PREFIX;
                    da.cmdtype |= C_RARE;
                };

                // 16-bit addressing is rare in 32-bit programs. If this is the case,
                // mark command as rare to help in analysis.
                if (addrsize != 4) da.cmdtype |= C_RARE;
            };

            // Suffix of 3DNow! command is accounted best by assuming it immediate byte
            // constant.
            if (is3dnow)
            {
                if (immsize != 0) da.error = DAE_BADCMD;
                else immsize = 1;
            };

            // Right or wrong, command decoded. Now dump it.
            if (da.error != 0)
            { // Hard error in command detected
                if (mode >= DISASM_FILE)
                    da.result.Append("???");

                if (da.error == DAE_BADCMD &&
                  (cmd[0] == 0x0F || cmd[0] == 0xFF) && size > 0
                )
                {
                    if (mode >= DISASM_FILE)
                        da.dump.AppendFormat("{0:X}", cmd[0]);

                    cmd.AdjustOffset(1);
                };
                if (size > 0)
                {
                    if (mode >= DISASM_FILE)
                        da.dump.AppendFormat("{0:X}", cmd[0]);

                    cmd.AdjustOffset(1);
                };
            }
            else
            { // No hard error, dump command
                if (mode >= DISASM_FILE)
                {
                    da.dump.AppendFormat("{0:X}", cmd[0]);
                    cmd.AdjustOffset(1);

                    if (hasrm)
                    {
                        da.dump.AppendFormat("{0:X}", cmd[0]);
                    }

                    if (hassib)
                    {
                        da.dump.AppendFormat("{0:X}", cmd[0]);
                        cmd.AdjustOffset(1);
                    }

                    if (dispsize != 0)
                    {
                        da.dump.Append(' ');
                        for (i = 0; i < dispsize; i++)
                        {
                            da.dump.AppendFormat("{0:X}", cmd[0]);
                            cmd.AdjustOffset(1);
                        };
                    };
                    if (immsize != 0)
                    {
                        da.dump.Append(' ');
                        for (i = 0; i < immsize; i++)
                        {
                            da.dump.AppendFormat("{0:X}", cmd[0]);
                            cmd.AdjustOffset(1);
                        };
                    };
                }
                else
                    cmd.AdjustOffset(1 + (hasrm ? 1 : 0) + (hassib ? 1 : 0) + dispsize + immsize);
            };
            // Check that command is not a dangerous one.
            if (mode >= DISASM_DATA)
            {
                //t_cmddata pdan;
                //for (pdan = dangerous; pdan.mask != 0; pdan++)
                foreach (t_cmddata pdan in dangerous)
                {
                    if (((code ^ pdan.code) & pdan.mask) != 0)
                        continue;
                    if (pdan.type == C_DANGERLOCK && !lockprefix)
                        break; // Command harmless without LOCK prefix
                    if (iswindowsnt && pdan.type == C_DANGER95)
                        break; // Command harmless under Windows NT
                    // Dangerous command!
                    if (pdan.type == C_DANGER95) da.warnings |= DAW_DANGER95;
                    else da.warnings |= DAW_DANGEROUS;
                    break;
                };
            };
            if (da.error == 0 && softerror != 0)
                da.error = softerror; // Error, but still display command
            if (mode >= DISASM_FILE)
            {
                if (da.error != DAE_NOERR)
                {
                    switch (da.error)
                    {
                        case DAE_CROSS:
                            da.comment = "Command crosses end of memory block"; break;
                        case DAE_BADCMD:
                            da.comment = "Unknown command"; break;
                        case DAE_BADSEG:
                            da.comment = "Undefined segment register"; break;
                        case DAE_MEMORY:
                            da.comment = "Illegal use of register"; break;
                        case DAE_REGISTER:
                            da.comment = "Memory address not allowed"; break;
                        case DAE_INTERN:
                            da.comment = "Internal OLLYDBG error"; break;
                        default:
                            da.comment = "Unknown error";
                            break;
                    }
                }
                else if ((da.warnings & DAW_PRIV) != 0 && !privileged)
                    da.comment = "Privileged command";
                else if ((da.warnings & DAW_IO) != 0 && !iocommand)
                    da.comment = "I/O command";
                else if ((da.warnings & DAW_FARADDR) != 0 && !farcalls)
                {
                    if ((da.cmdtype & C_TYPEMASK) == C_JMP)
                        da.comment = "Far jump";
                    else if ((da.cmdtype & C_TYPEMASK) == C_CAL)
                        da.comment = "Far call";
                    else if ((da.cmdtype & C_TYPEMASK) == C_RET)
                        da.comment = "Far return";
                }
                else if ((da.warnings & DAW_SEGMENT) != 0 && !farcalls)
                    da.comment = "Modification of segment register";
                else if ((da.warnings & DAW_SHIFT) != 0 && !badshift)
                    da.comment = "Shift constant out of range 1..31";
                else if ((da.warnings & DAW_PREFIX) != 0 && !extraprefix)
                    da.comment = "Superfluous prefix";
                else if ((da.warnings & DAW_LOCK) != 0 && !lockedbus)
                    da.comment = "LOCK prefix";
                else if ((da.warnings & DAW_STACK) != 0 && !stackalign)
                    da.comment = "Unaligned stack operation";
            };

            return da;
        }