BSOne.SFC/Tocsg.Lib/VCL/MadCodeHook/madDisAsm.pas

3541 lines
190 KiB
Plaintext

// ***************************************************************
// madDisAsm.pas version: 2.2.6 · date: 2017-07-12
// -------------------------------------------------------------
// mini mini x86 disassembler
// -------------------------------------------------------------
// Copyright (C) 1999 - 2017 www.madshi.net, All Rights Reserved
// ***************************************************************
// 2017-07-12 2.2.6 fixed: "mov r8b, 1" was reported as "mov al, 1"
// 2017-03-13 2.2.5 fixed bug handling "JMP/CALL +0" instructions
// 2014-05-05 2.2.4 fixed rare crash in Delphi exception block parsing
// 2013-10-01 2.2.3 added detection for "_CxxThrowException"
// 2012-09-05 2.2.2 added support for "TryRead"ing PAGE_EXECUTE memory
// 2012-07-03 2.2.1 improved TryRead performance and reliability
// 2012-04-03 2.2.0 completed x64 support
// 2010-07-26 2.1i (1) fixed: x64 disasm output "push rax" instead of "push r8"
// (2) added support for 3 byte nop instruction (0f 1f)
// 2010-01-07 2.1h some changes for improved Windows 7 support
// 2009-09-14 2.1g fixed: cleartext instruction labels were sometimes wrong
// 2009-07-14 2.1f fixed: cleartext disassembly missed instruction labels
// 2009-02-09 2.1e (1) Delphi 2009 support
// (2) made CopyFunction more robust against mixture mode hooks
// (3) "is code interceptable" security check was too strict
// 2006-09-11 2.1d (1) another little bug in ParseFunction fixed
// (2) several little bugs in cleartext disasm fixed
// (3) support for SSE3 added
// (4) limited support for 64bit modules added
// (5) some preparation for 64bit disassembling
// (6) minimal debug info only: function names were missing
// 2005-06-19 2.1c (1) little bug in ParseFunction fixed
// (2) line numbers were not shown for project initialization
// 2004-10-22 2.1b support for BCB try..whatever blocks added
// 2004-07-11 2.1a (1) line numbers are added to disassembling (Delphi only)
// (2) some disassembling cleartext tweaking
// 2004-04-25 2.1 (1) structured exception handling is detected + parsed now
// (2) special support for Delphi try..except blocks
// (3) special support for Delphi try..finally blocks
// (4) special support for Delphi safecall handling blocks
// (5) Delphi @Halt call is interpreted as "end of function"
// 2004-01-01 2.0b (1) ParseFunction "not interceptable" false alarm fixed
// (2) TryRead improved
// 2003-11-10 2.0a (1) jumps/calls to the very next instruction are ignored now
// (2) text output of function parts speeded up (for madExcept)
// 2003-06-09 2.0 (1) rewritten from scratch, full support for mmx/sse2/3dnow!
// (2) now we have a full disassembler including text output
// (3) the disassembler keeps track of the register contents
// -> should improve the detection of call/jmp targets
// (4) TryRead gets rid of unwanted debugger exception warnings
// 2002-11-26 1.2c ParseFunction stops at the end of module's code section
// 2002-11-07 1.2b (1) GetImageNtHeaders + PImageExportDirectory -> madTools
// (2) ParseFunction: case/switch statements are interpreted
// (3) ParseFunction: little gaps between code areas are parsed
// 2001-07-22 1.2a all remote stuff was moved to the new package "madRemote"
// 2001-07-08 1.2 (1) CreateRemoteThread added (works also in win9x)
// (2) Alloc/FreeMemEx added
// 2001-06-04 1.1e (1) some changes in "TFunctionInfo"
// (2) ParseFunction parameter "acceptOutsideCode" added
// (3) "TCodeInfo.Call" added
// 2001-05-25 1.1d (1) only targets with 4 byte length are accepted as far calls
// (2) CopyFunction works better now inside of the IDE in win9x
// 2001-04-16 1.1c bug (relocating absolute targets) in CopyFunction fixed
// 2001-02-23 1.1b little bug in ParseFunction fixed
// 2001-01-07 1.1a FreeCopiedFunction added
// 2000-12-22 1.1 CopyFunction added and some minor changes
// 2000-11-23 1.0e minor bug fixes in ParseCode + ParseFunction
unit madDisAsm;
{$I mad.inc}
interface
uses Windows, madTypes, madTools, madStrings;
// ***************************************************************
{ $define cstyle}
type
// result type for ParseCode
TCodeInfo = record
IsValid : boolean; // was the specified code pointer valid?
Opcode : word; // Opcode, one byte ($00xx) or two byte ($0fxx)
ModRm : byte; // ModRm byte, if available, otherwise 0
{$ifdef win64}
RipRelative : boolean; // is the displacement data rip relative?
DisplDword : NativeUInt; // displacement data in dword form
PDispl : pointer; // at which address is the displacement data stored?
{$endif}
Call : boolean; // is this instruction a call?
Jmp : boolean; // is this instruction a jmp?
RelTarget : boolean; // is this target relative (or absolute)?
Target : pointer; // absolute target address
PTarget : pointer; // pointer to the target information in the code
PPTarget : TPPointer; // pointer to pointer to the target information
TargetSize : integer; // size of the target information in bytes (1/2/4)
Enlargeable : boolean; // can the target size of this opcode be extended?
This : pointer; // where does this instruction begin?
Next : pointer; // next code location
end;
// disassembles the specified "code"
// you can loop through code blocks by using "result.Next"
function ParseCode (code: pointer ) : TCodeInfo; overload;
function ParseCode (code: pointer; var disAsm: AnsiString) : TCodeInfo; overload;
type
// result type for ParseFunction
TCodeArea = record
AreaBegin : pointer;
AreaEnd : pointer;
CaseBlock : boolean;
OnExceptBlock : boolean;
CalledFrom : pointer;
Registers : array [0..{$ifdef win64}15{$else}7{$endif}] of pointer;
end;
TFarCall = record
Call : boolean; // is it a CALL or a JMP?
CodeAddr1 : pointer; // beginning of call instruction
CodeAddr2 : pointer; // beginning of next instruction
Target : pointer;
RelTarget : boolean;
PTarget : pointer;
PPTarget : TPPointer;
{$ifdef win64}
RipRelative : boolean;
{$endif}
end;
TUnknownTarget = record
Call : boolean;
CodeAddr1 : pointer;
CodeAddr2 : pointer;
end;
TFunctionInfo = record
IsValid : boolean;
EntryPoint : pointer;
CodeBegin : pointer;
CodeLen : integer;
LastErrorAddr : pointer;
LastErrorNo : cardinal;
LastErrorStr : AnsiString;
CodeAreas : array of TCodeArea;
FarCalls : array of TFarCall;
UnknownTargets : array of TUnknownTarget;
Interceptable : boolean;
Copy : record
IsValid : boolean;
BufferLen : integer;
LastErrorAddr : pointer;
LastErrorNo : cardinal;
LastErrorStr : AnsiString;
end;
end;
TPFunctionInfo = ^TFunctionInfo;
// disassembles the complete function beginning at "func"
// the result tells you whether you can copy this function to another process
// (and which call targets you have to correct for this purpose)
// and whether you can intercept this function by overwriting the code
function ParseFunction (func: pointer ) : TFunctionInfo; overload;
function ParseFunction (func: pointer; var disAsm: AnsiString) : TFunctionInfo; overload;
// ***************************************************************
const
// error codes
CErrorBase_DisAsm = $770000;
CErrorNo_UnknownTarget = CErrorBase_DisAsm + 0;
CErrorNo_InvalidCode = CErrorBase_DisAsm + 1;
CErrorNo_CodeNotInterceptable = CErrorBase_DisAsm + 2;
CErrorNo_BadFunction = CErrorBase_DisAsm + 3;
CErrorNo_DoubleHook = CErrorBase_DisAsm + 4;
CErrorStr_UnknownTarget : PAnsiChar = 'This target can''t be seen in the assembler code.';
CErrorStr_InvalidCode : PAnsiChar = 'Invalid code!';
CErrorStr_CodeNotInterceptable : PAnsiChar = 'This code is not interceptable due to its design.';
CErrorStr_BadFunction : PAnsiChar = 'The specified function is bad.';
CErrorStr_DoubleHook : PAnsiChar = 'This code was already hooked by another hooking library.';
// ***************************************************************
// internal stuff
function kernel32handle : HMODULE;
function ntdllhandle : HMODULE;
function KernelProc (const api: AnsiString; doubleCheck: boolean = false) : pointer;
function NtProc (const api: AnsiString; doubleCheck: boolean = false) : pointer;
function GetExportDirectory (code: pointer; out module: HMODULE; out pexp: PImageExportDirectory) : boolean;
function SolveW9xDebugMode (code: pointer) : pointer;
function Magic : cardinal;
function Magic95 : boolean;
function ParseCode_ (code: pointer; tryRead_: THandle) : TCodeInfo;
function ParseFunction_ (func : pointer;
tryRead_ : THandle;
HandleAnyExceptionAddr : pointer;
HandleOnExceptionAddr : pointer;
HandleAutoExceptionAddr : pointer;
HandleFinallyAddr : pointer;
Halt0Addr : pointer) : TFunctionInfo;
function ParseFunctionEx (func: pointer; var disAsm: AnsiString; exceptAddr: pointer;
maxLines: integer; autoDelimiters: boolean) : TFunctionInfo;
var GetProcNameFromMapFile : function (proc: pointer) : UnicodeString = nil;
GetLineNumber : procedure (proc: pointer; var line: integer; var minAddr, maxAddr: pointer) = nil;
BcbInitExceptBlockLDTC : pointer = nil;
const
CKernel32 : AnsiString = (* kernel32.dll *) #$3E#$30#$27#$3B#$30#$39#$66#$67#$7B#$31#$39#$39;
CKernelbase : AnsiString = (* kernelbase.dll *) #$3E#$30#$27#$3B#$30#$39#$37#$34#$26#$30#$7B#$31#$39#$39;
CReadProcessMemory : AnsiString = (* ReadProcessMemory *) #$07#$30#$34#$31#$05#$27#$3A#$36#$30#$26#$26#$18#$30#$38#$3A#$27#$2C;
CWriteProcessMemory : AnsiString = (* WriteProcessMemory *) #$02#$27#$3C#$21#$30#$05#$27#$3A#$36#$30#$26#$26#$18#$30#$38#$3A#$27#$2C;
function StartTryRead : THandle;
procedure EndTryRead (tryRead: THandle);
function TryRead (src, dst: pointer; count: dword; tryRead: THandle = 0) : boolean;
function TryWrite (src, dst: pointer; count: dword) : boolean;
function IsBadReadPtrEx (src: pointer; count: dword; tryRead: THandle = 0) : boolean;
// ***************************************************************
implementation
{$ifdef cstyle}
function IntToHexExA(value: int64; minLen: integer = 1; fillChar: AnsiChar = '0') : AnsiString;
begin
result := madStrings.IntToHexExA(value);
Delete(result, 1, 1);
if (minLen < 0) or (fillChar in ['0'..'9','A'..'F','a'..'f']) then begin
result := FillStrA(result, minLen, fillChar);
result := UpStrA(result) + 'h';
end else begin
result := UpStrA(result) + 'h';
result := FillStrA(result, minLen, fillChar);
end;
end;
{$endif}
// ***************************************************************
function GetHandleAnyExceptionAddr : pointer;
asm
{$ifdef win64}
.NOFRAME
mov rax, 0
{$else}
mov eax, offset System.@HandleAnyException
{$endif}
end;
function GetHandleOnExceptionAddr : pointer;
asm
{$ifdef win64}
.NOFRAME
mov rax, 0
{$else}
mov eax, offset System.@HandleOnException
{$endif}
end;
function GetHandleAutoExceptionAddr : pointer;
asm
{$ifdef win64}
.NOFRAME
mov rax, 0
{$else}
mov eax, offset System.@HandleAutoException
{$endif}
end;
function GetHandleFinallyAddr : pointer;
asm
{$ifdef win64}
.NOFRAME
mov rax, 0
{$else}
mov eax, offset System.@HandleFinally
{$endif}
end;
function GetHalt0Addr : pointer;
asm
{$ifdef win64}
.NOFRAME
mov rax, offset System.@Halt0
{$else}
mov eax, offset System.@Halt0
{$endif}
end;
// ***************************************************************
const
fInvalid = $ffff; // invalid opcode
fReg = $0007; // bit mask
fNoReg = $0000; // no register information available for this opcode
fRegAl = $0001; // no modrm byte: al register
fRegEax = $0002; // no modrm byte: (e)ax register
fRegO8 = $0004; // no modrm byte: byte register depending on opcode
fRegO32 = $0005; // no modrm byte: (d)word register depending on opcode
fRegEaxO = $0006; // no modrm byte: fRegEax + fRegO32
fRegDxA = $0007; // no modrm byte: dx register + (e)ax/al register
fReg8 = $0001; // byte register specified by modrm byte
fReg16 = $0002; // word register specified by modrm byte
fRegxx = $0003; // segment/cr/dr register specified by modrm byte
fReg32 = $0004; // (d)word register specified by modrm byte
fReg64 = $0005; // qword register specified by modrm byte
fRegSt = $0006; // st floating point register specified by modrm byte
fReg128 = $0007; // oword register specified by modrm byte
fMod = $0038; // bit mask
fModOpc = $0008; // real flags are stored in COpcodeFlagsEx
fMod8 = $0010; // byte register/memory
fMod16 = $0018; // word register/memory
fMod32 = $0020; // (d)word register/memory
fMod64 = $0028; // qword register/memory
fMod80 = $0030; // st floating point register/memory
fMod128 = $0038; // oword register/memory
f66 = $00C0; // bit mask
f66R = $0040; // 66 prefix changes size of register -> 16 (sse: 128)
f66M = $0080; // 66 prefix changes size of modrm -> 16 (sse: 128)
f66RM = $00C0; // 66 prefix changes size of reg+modrm -> 16 (sse: 128)
fPtr = $0100; // disassembler shows "xword/byte ptr" or "[$xxx]"
fOrder = $0200; // swapped order -> modrm or immediate data comes first
fI = $0C00; // bit mask
fI8 = $0400; // immediate byte available
fI16 = $0800; // immediate word available
fI32 = $0C00; // immediate (d)word available
fJmpRel = $1000; // this opcode is a relative jump/call
fClr = $e000; // bit mask
fClrR = $2000; // clear modrm register/memory specified
fClrM = $4000; // clear register specified by modrm byte
fClrO = $6000; // clear register depending on opcode
fClrA = $8000; // clear eax
fClrRM = $a000; // fClrR + fClrM
fClrMA = $c000; // fClrM + fClrA
fClrOA = $e000; // fClrO + fClrA
// flags for one byte opcodes
COpcodeFlags : array [$00..$ff] of word =
((fReg8 + fMod8 + fOrder + fClrM ), // 00 /r add r/mb, rb (r)
(fReg32 + fMod32 + f66RM + fOrder + fClrM ), // 01 /r add r/m?, r? (r)
(fReg8 + fMod8 + fClrR ), // 02 /r add rb, r/mb r
(fReg32 + fMod32 + f66RM + fClrR ), // 03 /r add r?, r/m? r
(fRegAl + fI8 + fClrA ), // 04 ib add al, ib eax
(fRegEax + f66R + fI32 + fClrA ), // 05 i? add (e)ax, i? eax
{$ifdef win64}
(fInvalid ), // -----
(fInvalid ), // -----
{$else}
(fNoReg ), // 06 push es
(fNoReg ), // 07 pop es
{$endif}
(fReg8 + fMod8 + fOrder + fClrM ), // 08 /r or r/mb, rb (r)
(fReg32 + fMod32 + f66RM + fOrder + fClrM ), // 09 /r or r/m?, r? (r)
(fReg8 + fMod8 + fClrR ), // 0a /r or rb, r/mb r
(fReg32 + fMod32 + f66RM + fClrR ), // 0b /r or r?, r/m? r
(fRegAl + fI8 + fClrA ), // 0c ib or al, ib eax
(fRegEax + f66R + fI32 + fClrA ), // 0d i? or (e)ax, i? eax
{$ifdef win64}
(fInvalid ), // -----
{$else}
(fNoReg ), // 0e push cs
{$endif}
(fNoReg ), // 0f < extra table below >
(fReg8 + fMod8 + fOrder + fClrM ), // 10 /r adc r/mb, rb (r)
(fReg32 + fMod32 + f66RM + fOrder + fClrM ), // 11 /r adc r/m?, r? (r)
(fReg8 + fMod8 + fClrR ), // 12 /r adc rb, r/mb r
(fReg32 + fMod32 + f66RM + fClrR ), // 13 /r adc r?, r/m? r
(fRegAl + fI8 + fClrA ), // 14 ib adc al, ib eax
(fRegEax + f66R + fI32 + fClrA ), // 15 i? adc (e)ax, i? eax
{$ifdef win64}
(fInvalid ), // -----
(fInvalid ), // -----
{$else}
(fNoReg ), // 16 push ss
(fNoReg ), // 17 pop ss
{$endif}
(fReg8 + fMod8 + fOrder + fClrM ), // 18 /r sbb r/mb, rb (r)
(fReg32 + fMod32 + f66RM + fOrder + fClrM ), // 19 /r sbb r/m?, i? (r)
(fReg8 + fMod8 + fClrR ), // 1a /r sbb rb, r/mb r
(fReg32 + fMod32 + f66RM + fClrR ), // 1b /r sbb r?, r/m? r
(fRegAl + fI8 + fClrA ), // 1c ib sbb al, ib eax
(fRegEax + f66R + fI32 + fClrA ), // 1d i? sbb (e)ax, i? eax
{$ifdef win64}
(fInvalid ), // -----
(fInvalid ), // -----
{$else}
(fNoReg ), // 1e push ds
(fNoReg ), // 1f pop ds
{$endif}
(fReg8 + fMod8 + fOrder + fClrM ), // 20 /r and r/mb, rb (r)
(fReg32 + fMod32 + f66RM + fOrder + fClrM ), // 21 /r and r/m?, r? (r)
(fReg8 + fMod8 + fClrR ), // 22 /r and rb, r/mb r
(fReg32 + fMod32 + f66RM + fClrR ), // 23 /r and r?, r/m? r
(fRegAl + fI8 + fClrA ), // 24 ib and al, ib eax
(fRegEax + f66R + fI32 + fClrA ), // 25 i? and (e)ax, i? eax
(fNoReg ), // 26 PREFIX: es segment override
{$ifdef win64}
(fInvalid ), // -----
{$else}
(fNoReg + fClrA ), // 27 daa eax
{$endif}
(fReg8 + fMod8 + fOrder + fClrM ), // 28 /r sub r/mb, rb (r)
(fReg32 + fMod32 + f66RM + fOrder + fClrM ), // 29 /r sub r/m?, r? (r)
(fReg8 + fMod8 + fClrR ), // 2a /r sub rb, r/mb r
(fReg32 + fMod32 + f66RM + fClrR ), // 2b /r sub r?, r/m? r
(fRegAl + fI8 + fClrA ), // 2c ib sub al, ib eax
(fRegEax + f66R + fI32 + fClrA ), // 2d i? sub (e)ax, i? eax
(fNoReg ), // 2e PREFIX: cs segment override / branch not taken hint
{$ifdef win64}
(fInvalid ), // -----
{$else}
(fNoReg + fClrA ), // 2f das eax
{$endif}
(fReg8 + fMod8 + fOrder + fClrM ), // 30 /r xor r/mb, rb (r)
(fReg32 + fMod32 + f66RM + fOrder + fClrM ), // 31 /r xor r/m?, r? (r)
(fReg8 + fMod8 + fClrR ), // 32 /r xor rb, r/mb r
(fReg32 + fMod32 + f66RM + fClrR ), // 33 /r xor r?, r/m? r
(fRegAl + fI8 + fClrA ), // 34 ib xor al, ib eax
(fRegEax + f66R + fI32 + fClrA ), // 35 i? xor (e)ax, i? eax
(fNoReg ), // 36 PREFIX: SS segment override
{$ifdef win64}
(fInvalid ), // -----
{$else}
(fNoReg + fClrA ), // 37 aaa eax
{$endif}
(fReg8 + fMod8 + fOrder ), // 38 /r cmp r/mb, rb
(fReg32 + fMod32 + f66RM + fOrder ), // 39 /r cmp r/m?, r?
(fReg8 + fMod8 ), // 3a /r cmp rb, r/mb
(fReg32 + fMod32 + f66RM ), // 3b /r cmp r?, r/m?
(fRegAl + fI8 ), // 3c ib cmp al, ib
(fRegEax + f66R + fI32 ), // 3d i? cmp (e)ax, i?
(fNoReg ), // 3e PREFIX: DS segment override / branch taken hint
{$ifdef win64}
(fInvalid ), // -----
{$else}
(fNoReg + fClrA ), // 3f aas eax
{$endif}
{$ifdef win64}
(fNoReg ), // 40 PREFIX: REX
(fNoReg ), // 41
(fNoReg ), // 42
(fNoReg ), // 43
(fNoReg ), // 44
(fNoReg ), // 45
(fNoReg ), // 46
(fNoReg ), // 47
(fNoReg ), // 48
(fNoReg ), // 49
(fNoReg ), // 4a
(fNoReg ), // 4b
(fNoReg ), // 4c
(fNoReg ), // 4d
(fNoReg ), // 4e
(fNoReg ), // 4f
{$else}
(fRegO32 + f66R + fClrO ), // 40 inc (e)ax r
(fRegO32 + f66R + fClrO ), // 41 inc (e)cx r
(fRegO32 + f66R + fClrO ), // 42 inc (e)dx r
(fRegO32 + f66R + fClrO ), // 43 inc (e)bx r
(fRegO32 + f66R + fClrO ), // 44 inc (e)sp r
(fRegO32 + f66R + fClrO ), // 45 inc (e)bp r
(fRegO32 + f66R + fClrO ), // 46 inc (e)si r
(fRegO32 + f66R + fClrO ), // 47 inc (e)di r
(fRegO32 + f66R + fClrO ), // 48 dec (e)ax r
(fRegO32 + f66R + fClrO ), // 49 dec (e)cx r
(fRegO32 + f66R + fClrO ), // 4a dec (e)dx r
(fRegO32 + f66R + fClrO ), // 4b dec (e)bx r
(fRegO32 + f66R + fClrO ), // 4c dec (e)sp r
(fRegO32 + f66R + fClrO ), // 4d dec (e)bp r
(fRegO32 + f66R + fClrO ), // 4e dec (e)si r
(fRegO32 + f66R + fClrO ), // 4f dec (e)di r
{$endif}
(fRegO32 + f66R ), // 50 push (e)ax
(fRegO32 + f66R ), // 51 push (e)cx
(fRegO32 + f66R ), // 52 push (e)dx
(fRegO32 + f66R ), // 53 push (e)bx
(fRegO32 + f66R ), // 54 push (e)sp
(fRegO32 + f66R ), // 55 push (e)bp
(fRegO32 + f66R ), // 56 push (e)si
(fRegO32 + f66R ), // 57 push (e)di
(fRegO32 + f66R + fClrO ), // 58 pop (e)ax r
(fRegO32 + f66R + fClrO ), // 59 pop (e)cx r
(fRegO32 + f66R + fClrO ), // 5a pop (e)dx r
(fRegO32 + f66R + fClrO ), // 5b pop (e)bx r
(fRegO32 + f66R + fClrO ), // 5c pop (e)sp r
(fRegO32 + f66R + fClrO ), // 5d pop (e)bp r
(fRegO32 + f66R + fClrO ), // 5e pop (e)si r
(fRegO32 + f66R + fClrO ), // 5f pop (e)di r
{$ifdef win64}
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fReg32 + fMod32 + f66R + fPtr + fClrR ), // 63 /r movsxd rq, r/md r
{$else}
(fNoReg ), // 60 pusha(d)
(fNoReg + fClrA ), // 61 popa(d) edi esi ebp ebx edx ecx eax
(fReg32 + fMod32 + f66RM ), // 62 /r bound r?, m?&?
(fReg16 + fMod16 + fOrder + fClrM ), // 63 /r arpl r/mw, rw (r)
{$endif}
(fNoReg ), // 64 PREFIX: fs segment override
(fNoReg ), // 65 PREFIX: gs segment override
(fNoReg ), // 66 PREFIX: operand size override
(fNoReg ), // 67 PREFIX: address size override
(fNoReg + fI32 ), // 68 i? push i?
(fReg32 + fMod32 + f66RM + fI32 + fClrR ), // 69 /r i? imul r?, [r/m?,] i? r
(fNoReg + fI8 ), // 6a ib push ib
(fReg32 + fMod32 + f66RM + fI8 + fClrR ), // 6b /r ib imul r?, [r/m?,] ib r
(fNoReg ), // 6c insb edi
(fNoReg ), // 6d insw/insd edi
(fNoReg ), // 6e outsb esi
(fNoReg ), // 6f outsw/d esi
(fNoReg + fI8 + fJmpRel ), // 70 cb jo relb
(fNoReg + fI8 + fJmpRel ), // 71 cb jno relb
(fNoReg + fI8 + fJmpRel ), // 72 cb jb relb
(fNoReg + fI8 + fJmpRel ), // 73 cb jnb relb
(fNoReg + fI8 + fJmpRel ), // 74 cb jz relb
(fNoReg + fI8 + fJmpRel ), // 75 cb jnz relb
(fNoReg + fI8 + fJmpRel ), // 76 cb jbe relb
(fNoReg + fI8 + fJmpRel ), // 77 cb ja relb
(fNoReg + fI8 + fJmpRel ), // 78 cb js relb
(fNoReg + fI8 + fJmpRel ), // 79 cb jns relb
(fNoReg + fI8 + fJmpRel ), // 7a cb jp relb
(fNoReg + fI8 + fJmpRel ), // 7b cb jnp relb
(fNoReg + fI8 + fJmpRel ), // 7c cb jl relb
(fNoReg + fI8 + fJmpRel ), // 7d cb jge relb
(fNoReg + fI8 + fJmpRel ), // 7e cb jle relb
(fNoReg + fI8 + fJmpRel ), // 7f cb jg relb
(fNoReg + fMod8 + fPtr + fI8 ), // 80 /x ib xxx r/mb, ib (r) - add/or/adc/sbb/and/sub/xor/cmp
(fNoReg + fMod32 + f66M + fPtr + fI32 ), // 81 /x i? xxx r/m?, i? (r)
{$ifdef win64}
(fInvalid ), // -----
{$else}
(fNoReg + fMod8 + fPtr + fI8 ), // 82 /x ib xxx r/mb, ib (r)
{$endif}
(fNoReg + fMod32 + f66M + fPtr + fI8 ), // 83 /x ib xxx r/m?, ib (r)
(fReg8 + fMod8 + fOrder ), // 84 /r test r/mb, rb
(fReg32 + fMod32 + f66RM + fOrder ), // 85 /r test r/m?, r?
(fReg8 + fMod8 + fClrRM), // 86 /r xchg rb, r/mb (r) r
(fReg32 + fMod32 + f66RM + fClrRM), // 87 /r xchg r?, r/m? (r) r
(fReg8 + fMod8 + fOrder + fClrM ), // 88 /r mov r/mb, rb (r)
(fReg32 + fMod32 + f66RM + fOrder + fClrM ), // 89 /r mov r/m?, r? (r)
(fReg8 + fMod8 + fClrR ), // 8a /r mov rb, r/mb r
(fReg32 + fMod32 + f66RM + fClrR ), // 8b /r mov r?, r/m? r
(fRegxx + fMod32 + f66RM + fOrder + fClrM ), // 8c /r mov r/m?, sreg (r)
(fReg32 + fMod32 + f66RM + fClrR ), // 8d /r lea r?, m r
(fRegxx + fMod16 + f66RM + fPtr ), // 8e /r mov sreg, r/m?
(fNoReg + fMod32 + f66M + fPtr ), // 8f /0 pop m?
(fNoReg ), // 90 nop
(fRegEaxO+ f66R + fClrOA), // 91 xchg (e)ax, (e)cx r eax
(fRegEaxO+ f66R + fClrOA), // 92 xchg (e)ax, (e)dx r eax
(fRegEaxO+ f66R + fClrOA), // 93 xchg (e)ax, (e)bx r eax
(fRegEaxO+ f66R + fClrOA), // 94 xchg (e)ax, (e)sp r eax
(fRegEaxO+ f66R + fClrOA), // 95 xchg (e)ax, (e)bp r eax
(fRegEaxO+ f66R + fClrOA), // 96 xchg (e)ax, (e)si r eax
(fRegEaxO+ f66R + fClrOA), // 97 xchg (e)ax, (e)di r eax
(fNoReg + fClrA ), // 98 cbw/cwde eax
(fNoReg ), // 99 cwd/cdq edx
{$ifdef win64}
(fInvalid ), // -----
{$else}
(fNoReg ), // 9a c? cw call cw:c?
{$endif}
(fNoReg ), // 9b wait
(fNoReg ), // 9c pushf(d)
(fNoReg ), // 9d popf(d)
(fNoReg ), // 9e sahf
(fNoReg + fClrA ), // 9f lahf eax
(fRegAl + fPtr + fClrA ), // a0 mov al, moffsb eax
(fRegEax + f66R + fPtr + fClrA ), // a1 mov (e)ax, moffs? eax
(fRegAl + fPtr + fOrder ), // a2 mov moffsb, al
(fRegEax + f66R + fPtr + fOrder ), // a3 mov moffs?, (e)ax
(fNoReg ), // a4 movsb esi edi
(fNoReg ), // a5 movsw/d esi edi
(fNoReg ), // a6 cmpsb esi edi
(fNoReg ), // a7 cmpsd/w esi edi
(fRegAl + fI8 ), // a8 ib test al, ib
(fRegEax + f66R + fI32 ), // a9 i? test (e)ax, i?
(fNoReg ), // aa stosb edi
(fNoReg ), // ab stosw/d edi
(fNoReg + fClrA ), // ac lodsb eax esi
(fNoReg + fClrA ), // ad lodsw/d eax esi
(fNoReg ), // ae scasb edi
(fNoReg ), // af scasw/d edi
(fRegO8 + fI8 + fClrO ), // b0 ib mov rb, ib r
(fRegO8 + fI8 + fClrO ), // b1 ib mov rb, ib r
(fRegO8 + fI8 + fClrO ), // b2 ib mov rb, ib r
(fRegO8 + fI8 + fClrO ), // b3 ib mov rb, ib r
(fRegO8 + fI8 + fClrO ), // b4 ib mov rb, ib r
(fRegO8 + fI8 + fClrO ), // b5 ib mov rb, ib r
(fRegO8 + fI8 + fClrO ), // b6 ib mov rb, ib r
(fRegO8 + fI8 + fClrO ), // b7 ib mov rb, ib r
(fRegO32 + f66R + fI32 + fClrO ), // b8 i? mov (e)ax, i? r
(fRegO32 + f66R + fI32 + fClrO ), // b9 i? mov (e)cx, i? r
(fRegO32 + f66R + fI32 + fClrO ), // ba i? mov (e)dx, i? r
(fRegO32 + f66R + fI32 + fClrO ), // bb i? mov (e)bx, i? r
(fRegO32 + f66R + fI32 + fClrO ), // bc i? mov (e)sp, i? r
(fRegO32 + f66R + fI32 + fClrO ), // bd i? mov (e)bp, i? r
(fRegO32 + f66R + fI32 + fClrO ), // be i? mov (e)si, i? r
(fRegO32 + f66R + fI32 + fClrO ), // bf i? mov (e)di, i? r
(fNoReg + fMod8 + fPtr + fI8 + fClrM ), // c0 /x ib xxx r/mb, ib (r) - rol/ror/rcl/rcr/shl/shr/sar
(fNoReg + fMod32 + f66M + fPtr + fI8 + fClrM ), // c1 /x ib xxx r/m?, ib (r)
(fNoReg + fI16 ), // c2 iw ret iw
(fNoReg ), // c3 ret
{$ifdef win64}
(fInvalid ), // -----
(fInvalid ), // -----
{$else}
(fReg32 + fMod32 + f66RM + fClrR ), // c4 /r les r?, m16:? r
(fReg32 + fMod32 + f66RM + fClrR ), // c5 /r lds r?, m16:? r
{$endif}
(fNoReg + fMod8 + fPtr + fI8 + fClrM ), // c6 /0 ib mov r/mb, ib (r)
(fNoReg + fMod32 + f66M + fPtr + fI32 + fClrM ), // c7 /0 i? mov r/m?, i? (r)
(fNoReg ), // c8 iw ib enter iw, ib ebp
(fNoReg ), // c9 leave ebp
(fNoReg + fI16 ), // ca iw ret iw
(fNoReg ), // cb ret
(fNoReg ), // cc int 3
(fNoReg + fI8 ), // cd ib int ib
{$ifdef win64}
(fInvalid ), // -----
{$else}
(fNoReg ), // ce into
{$endif}
(fNoReg ), // cf iret(d)
(fNoReg + fMod8 + fPtr + fClrM ), // d0 /x xxx r/mb, 1 (r) - rol/ror/rcl/rcr/shl/shr/sar
(fNoReg + fMod32 + f66M + fPtr + fClrM ), // d1 /x xxx r/m?, 1 (r)
(fNoReg + fMod8 + fPtr + fClrM ), // d2 /x xxx r/mb, cl (r)
(fNoReg + fMod32 + f66M + fPtr + fClrM ), // d3 /x xxx r/m?, cl (r)
{$ifdef win64}
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
{$else}
(fNoReg + fI8 + fClrA ), // d4 ib aam eax
(fNoReg + fI8 + fClrA ), // d5 ib aad eax
(fNoReg ), // d6 salc
{$endif}
(fNoReg + fClrA ), // d7 xlatb eax
(fModOpc ), // d8 /r xxx mdreal/st, st(1)
(fModOpc ), // d9 /x/r xxx
(fModOpc ), // da /x/r xxx
(fModOpc ), // db /x/r xxx
(fModOpc ), // dc /r xxx mdreal/st(1), st
(fModOpc ), // dd /x/r xxx
(fModOpc ), // de /x/r xxx
(fModOpc ), // df /x/r xxx
(fNoReg + fI8 + fJmpRel ), // e0 cb loopne relb ecx
(fNoReg + fI8 + fJmpRel ), // e1 cb loope relb ecx
(fNoReg + fI8 + fJmpRel ), // e2 cb loop relb ecx
(fNoReg + fI8 + fJmpRel ), // e3 cb jcxz relb
(fRegAl + fI8 + fClrA ), // e4 ib in al, ib eax
(fRegEax + f66R + fI8 + fClrA ), // e5 ib in (e)ax, ib eax
(fRegAl + fOrder + fI8 ), // e6 ib out ib, al
(fRegEax + f66R + fOrder + fI8 ), // e7 ib out ib, (e)ax
(fNoReg + fI32 + fJmpRel ), // e8 c? call rel?
(fNoReg + fI32 + fJmpRel ), // e9 c? jmp rel?
{$ifdef win64}
(fInvalid ), // -----
{$else}
(fNoReg ), // ea c? cw jmp ptr16:?
{$endif}
(fNoReg + fI8 + fJmpRel ), // eb cb jmp relb
(fRegDxA + fClrA ), // ec in al, dx eax
(fRegDxA + f66R + fClrA ), // ed in (e)ax, dx eax
(fRegDxA + fOrder ), // ee out dx, al
(fRegDxA + f66R + fOrder ), // ef out dx, (e)ax
(fNoReg ), // f0 PREFIX: lock
(fNoReg ), // f1 int01
(fNoReg ), // f2 PREFIX: repne +ecx
(fNoReg ), // f3 PREFIX: rep(e) +ecx
(fNoReg ), // f4 hlt
(fNoReg ), // f5 cmc
(fModOpc ), // f6 /x (ib) xxx r/mb (,ib) (r) (eax) - test/not/neg/mul/imul/div/idiv
(fModOpc ), // f7 /x (i?) xxx r/m? (,i?) (r) (eax)
(fNoReg ), // f8 clc
(fNoReg ), // f9 stc
(fNoReg ), // fa cli
(fNoReg ), // fb sti
(fNoReg ), // fc cld
(fNoReg ), // fd std
(fNoReg + fMod8 + fPtr + fClrM ), // fe /x xxx r/mb (r) - inc/dec
(fNoReg + fMod32 + f66M + fPtr )); // ff /x xxx r/m? (r) - inc/dec/call/call/jmp/jmp/push
// flags for two byte opcodes ($0f $xx)
COpcodeFlags0f : array [$00..$ff] of word =
((fNoReg + fMod16 + fPtr ), // 0f 00 /x xxx r/mw (r) - sldt/str/lldt/ltr/verr/verw
(fNoReg + fMod16 ), // 0f 01 /x xxx r/m? (r) - sgdt/sidt/lgdt/lidt/smsw/-/lmsw/invlpg
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 02 /r lar r?, r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 03 /r lsl r?, r/m? r
(fInvalid ), // -----
(fNoReg ), // 0f 05 syscall (AMD)
(fNoReg ), // 0f 06 clts
(fNoReg ), // 0f 07 sysret (AMD)
(fNoReg ), // 0f 08 invd
(fNoReg ), // 0f 09 wbinvd
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fNoReg + fMod8 + fPtr ), // 0f 0d /x prefetch(w) r/mb
(fNoReg ), // 0f 0e femms
(fReg64 + fMod64 + fPtr + fI8 ), // 0f 0f xx xxx pq, qq
(fReg128 + fMod128 ), // 0f 10 /r movups xmm, xmm/m
(fReg128 + fMod128 + fOrder ), // 0f 11 /r movups xmm/m, xmm
(fReg128 + fMod128 ), // 0f 12 /r movlps xmm, m
(fReg128 + fMod128 + fOrder ), // 0f 13 /r movlps m, xmm
(fReg128 + fMod128 ), // 0f 14 /r unpcklps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 15 /r unpckhps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 16 /r movhps xmm, m
(fReg128 + fMod128 + fOrder ), // 0f 17 /r movhps m, xmm
(fNoReg + fMod8 + fPtr ), // 0f 18 /x prefetchxxx
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fNoReg + fMod32 + fPtr ), // 0f 1f /0 nop md
(fRegxx + fMod32 + fOrder + fClrM ), // 0f 20 /r mov rd, cr0-4 r
(fRegxx + fMod32 + fOrder + fClrM ), // 0f 21 /r mov rd, dr0-7 r
(fRegxx + fMod32 ), // 0f 22 /r mov cr0-4, rd
(fRegxx + fMod32 ), // 0f 23 /r mov dr0-7, rd
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fReg128 + fMod128 ), // 0f 28 /r movaps xmm, xmm/m
(fReg128 + fMod128 + fOrder ), // 0f 29 /r movaps xmm/m, xmm
(fReg128 + fMod64 ), // 0f 2a /r cvtpi2ps xmm, mm/r/m
(fReg128 + fMod128 + fOrder ), // 0f 2b /r movntps m, xmm
(fReg64 + fMod128 ), // 0f 2c /r cvttps2pi m/r, xmm/m (r)
(fReg64 + fMod128 ), // 0f 2d /r cvtps2pi m/r, xmm/m (r)
(fReg128 + fMod128 ), // 0f 2e /r ucomiss xmm, xmm/m
(fReg128 + fMod128 ), // 0f 2f /r comiss xmm, xmm/m
(fNoReg ), // 0f 30 wrmsr
(fNoReg + fClrA ), // 0f 31 rdtsc edx eax
(fNoReg + fClrA ), // 0f 32 rdmsr edx eax
(fNoReg + fClrA ), // 0f 33 rdpmc edx eax
(fNoReg ), // 0f 34 sysenter
(fNoReg ), // 0f 35 sysexit
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 40 /r cmovo r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 41 /r cmovno r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 42 /r cmovb r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 43 /r cmovnb r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 44 /r cmovz r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 45 /r cmovnz r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 46 /r cmovbe r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 47 /r cmova r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 48 /r cmovs r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 49 /r cmovns r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 4a /r cmovp r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 4b /r cmovnp r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 4c /r cmovl r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 4d /r cmovge r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 4e /r cmovle r?,r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f 4f /r cmovg r?,r/m? r
(fReg32 + fMod128 + fClrM ), // 0f 50 /r movmskps r, xmm r
(fReg128 + fMod128 ), // 0f 51 /r sqrtps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 52 /r rsqrtps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 53 /r rcpps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 54 /r andps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 55 /r andnps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 56 /r orps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 57 /r xorps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 58 /r addps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 59 /r mulps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 5a /r cvtps2pd xmm, xmm/m
(fReg128 + fMod128 ), // 0f 5b /r cvtdq2ps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 5c /r subps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 5d /r minps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 5e /r divps xmm, xmm/m
(fReg128 + fMod128 ), // 0f 5f /r maxps xmm, xmm/m
(fReg64 + fMod64 + f66RM ), // 0f 60 /r punpcklbw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 61 /r punpcklwd mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 62 /r punpckldq mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 63 /r packsswb mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 64 /r pcmpgtb mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 65 /r pcmpgtw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 66 /r pcmpgtd mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 67 /r packuswb mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 68 /r punpckhbw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 69 /r punpckhwd mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 6a /r punpckhdq mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 6b /r packssdw mm1, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 6c /r punpcklqdq xmm, xmm/m
(fReg64 + fMod64 + f66RM ), // 0f 6d /r punpckhqdq xmm, xmm/m
(fReg64 + fMod32 + f66R ), // 0f 6e /r movd mm, r/md
(fReg64 + fMod64 + f66RM ), // 0f 6f /r movq mm, mm/m
(fReg64 + fMod64 + f66RM + fI8 ), // 0f 70 /r ib pshufw mm, mm/m, ib
(fNoReg + fMod64 + f66M + fI8 ), // 0f 71 /x ib xxx (x)mm, ib
(fNoReg + fMod64 + f66M + fI8 ), // 0f 72 /x ib xxx (x)mm, ib
(fNoReg + fMod64 + f66M + fI8 ), // 0f 73 /x ib xxx (x)mm, ib
(fReg64 + fMod64 + f66RM ), // 0f 74 /r pcmpeqb mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 75 /r pcmpeqw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f 76 /r pcmpeqd mm, mm/m
(fNoReg ), // 0f 77 emms
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fInvalid ), // -----
(fReg128 + fMod128 + f66RM ), // 0f 7c /r haddpd xmm, xmm/m
(fReg128 + fMod128 + f66RM ), // 0f 7d /r hsubpd xmm, xmm/m
(fReg64 + fMod32 + f66R + fOrder ), // 0f 7e /r movd r/md, mm (r)
(fReg64 + fMod64 + f66RM + fOrder ), // 0f 7f /r movq mm/m, mm
(fNoReg + fI32 + fJmpRel ), // 0f 80 c? jo relb
(fNoReg + fI32 + fJmpRel ), // 0f 81 c? jno relb
(fNoReg + fI32 + fJmpRel ), // 0f 82 c? jb relb
(fNoReg + fI32 + fJmpRel ), // 0f 83 c? jnb relb
(fNoReg + fI32 + fJmpRel ), // 0f 84 c? jz relb
(fNoReg + fI32 + fJmpRel ), // 0f 85 c? jnz relb
(fNoReg + fI32 + fJmpRel ), // 0f 86 c? jbe relb
(fNoReg + fI32 + fJmpRel ), // 0f 87 c? ja relb
(fNoReg + fI32 + fJmpRel ), // 0f 88 c? js relb
(fNoReg + fI32 + fJmpRel ), // 0f 89 c? jns relb
(fNoReg + fI32 + fJmpRel ), // 0f 8a c? jp relb
(fNoReg + fI32 + fJmpRel ), // 0f 8b c? jnp relb
(fNoReg + fI32 + fJmpRel ), // 0f 8c c? jl relb
(fNoReg + fI32 + fJmpRel ), // 0f 8d c? jge relb
(fNoReg + fI32 + fJmpRel ), // 0f 8e c? jle relb
(fNoReg + fI32 + fJmpRel ), // 0f 8f c? jg relb
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 90 seto r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 91 setno r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 92 setb r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 93 setae r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 94 sete r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 95 setne r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 96 setbe r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 97 seta r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 98 sets r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 99 setns r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 9a setp r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 9b setnp r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 9c setl r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 9d setge r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 9e setle r/mb (r)
(fNoReg + fMod8 + fPtr + fClrM ), // 0f 9f setg r/mb (r)
(fNoReg ), // 0f a0 push fs
(fNoReg ), // 0f a1 pop fs
(fNoReg + fClrA ), // 0f a2 cpuid eax ebx ecx edx
(fReg32 + fMod32 + f66RM + fOrder ), // 0f a3 bt r/m?, r?
(fReg32 + fMod32 + f66RM + fPtr + fOrder + fI8 + fClrM ), // 0f a4 ib shld r/m?, r?, ib (r)
(fReg32 + fMod32 + f66RM + fPtr + fOrder + fClrM ), // 0f a5 shld r/m?, r?, cl (r)
(fReg8 + fMod8 + fOrder + fClrMA), // 0f a6 /r cmpxchg r/mb, rb (r) eax
(fReg32 + fMod32 + f66RM + fOrder + fClrMA), // 0f a7 /r cmpxchg r/m?, r? (r) eax
(fNoReg ), // 0f a8 push gs
(fNoReg ), // 0f a9 pop gs
(fNoReg ), // 0f aa rsm
(fReg32 + fMod32 + f66RM + fOrder + fClrM ), // 0f ab bts r/m?, r? (r)
(fReg32 + fMod32 + f66RM + fPtr + fOrder + fI8 + fClrM ), // 0f ac ib shrd r/m?, r?, ib (r)
(fReg32 + fMod32 + f66RM + fPtr + fOrder + fClrM ), // 0f ad shrd r/m?, r?, cl (r)
(fNoReg + fMod32 ), // 0f ae /x xxx (m)
(fReg32 + fMod32 + f66RM + fClrR ), // 0f af /r imul r?, r/m? r
(fReg8 + fMod8 + fOrder + fClrMA), // 0f b0 /r cmpxchg r/mb, rb (r) eax
(fReg32 + fMod32 + f66RM + fOrder + fClrMA), // 0f b1 /r cmpxchg r/m?, r? (r) eax
(fReg32 + fMod32 + f66RM + fClrR ), // 0f b2 /r lss r?, m16:? r
(fReg32 + fMod32 + f66RM + fOrder + fClrM ), // 0f b3 btr r/m?, r? (r)
(fReg32 + fMod32 + f66RM + fClrR ), // 0f b4 /r lfs r?, m16:? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f b5 /r lgs r?, m16:? r
(fReg32 + fMod8 + f66R + fPtr + fClrR ), // 0f b6 /r movzx r?, r/mb r
(fReg32 + fMod16 + f66R + fPtr + fClrR ), // 0f b7 /r movzx rd, r/mw r
(fInvalid ), // -----
(fInvalid ), // -----
(fNoReg + fMod32 + f66M + fPtr + fI8 ), // 0f ba /x ib btx r/m?, ib (r) - bt/bts/btr/btc
(fReg32 + fMod32 + f66RM + fOrder + fClrM ), // 0f bb btc r/m?, r? (r)
(fReg32 + fMod32 + f66RM + fClrR ), // 0f bc bsf r?, r/m? r
(fReg32 + fMod32 + f66RM + fClrR ), // 0f bd bsr r?, r/m? r
(fReg32 + fMod8 + f66R + fPtr + fClrR ), // 0f be /r movsx r?, r/mb r
(fReg32 + fMod16 + f66R + fPtr + fClrR ), // 0f bf /r movsx rd, r/mw r
(fReg8 + fMod8 + fOrder + fClrRM), // 0f c0 /r xadd r/mb, rb (r) r
(fReg32 + fMod32 + f66RM + fOrder + fClrRM), // 0f c1 /r xadd r/m?, r? (r) r
(fReg128 + fMod128 + fI8 ), // 0f c2 /r ib cmpps xmm, xmm/m, ib
(fReg32 + fMod32 + fOrder ), // 0f c3 /r movnti md, rd
(fReg64 + fMod32 + f66R + fI8 ), // 0f c4 /r ib pinsrw mm, rd/mw, ib
(fReg32 + fMod64 + f66M + fI8 + fClrR ), // 0f c5 /r ib pextrw rd, mm, ib r
(fReg128 + fMod128 + fI8 ), // 0f c6 /r ib shufps xmm, xmm/m, ib
(fNoReg + fMod64 + fPtr + fClrA ), // 0f c7 /1 mq cmpxchg8b mq edx eax
(fRegO32 + fClrO ), // 0f c8 bswap (e)ax r
(fRegO32 + fClrO ), // 0f c9 bswap (e)cx r
(fRegO32 + fClrO ), // 0f ca bswap (e)dx r
(fRegO32 + fClrO ), // 0f cb bswap (e)bx r
(fRegO32 + fClrO ), // 0f cc bswap (e)sp r
(fRegO32 + fClrO ), // 0f cd bswap (e)bp r
(fRegO32 + fClrO ), // 0f ce bswap (e)si r
(fRegO32 + fClrO ), // 0f cf bswap (e)di r
(fReg128 + fMod128 + f66RM ), // 0f d0 /r addsubpd xmm, xmm/m
(fReg64 + fMod64 + f66RM ), // 0f d1 /r psrlw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f d2 /r psrld mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f d3 /r psrlq mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f d4 /r paddq mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f d5 /r pmullw mm, mm/m
(fReg64 + fMod64 + f66RM + fOrder ), // 0f d6 /r movq xmm/m, xmm
(fReg32 + fMod64 + f66M + fClrR ), // 0f d7 /r pmovmskb rd, mm r
(fReg64 + fMod64 + f66RM ), // 0f d8 /r psubusb mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f d9 /r psubusw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f da /r pminub mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f db /r pand mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f dc /r paddusb mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f dd /r paddusw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f de /r pmaxub mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f df /r pandn mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f e0 /r pavgb mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f e1 /r psraw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f e2 /r psrad mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f e3 /r pavgw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f e4 /r pmulhuw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f e5 /r pmulhw mm, mm/m
(fReg128 + fMod128 ), // 0f e6 /r cvttpd2dq xmm, xmm/m
(fReg64 + fMod64 + f66RM + fOrder ), // 0f e7 /r movntq m, mm
(fReg64 + fMod64 + f66RM ), // 0f e8 /r psubsb mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f e9 /r psubsw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f ea /r pminsw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f eb /r por mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f ec /r paddsb mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f ed /r paddsw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f ee /r pmaxsw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f ef /r pxor mm, mm/m
(fReg128 + fMod128 ), // 0f f0 /r lddqu xmm, m
(fReg64 + fMod64 + f66RM ), // 0f f1 /r psllw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f f2 /r pslld mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f f3 /r psllq mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f f4 /r pmuludq mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f f5 /r pmaddwd mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f f6 /r psadbw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f f7 /r maskmovq mm, mm
(fReg64 + fMod64 + f66RM ), // 0f f8 /r psubb mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f f9 /r psubw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f fa /r psubd mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f fb /r psubq mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f fc /r paddb mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f fd /r paddw mm, mm/m
(fReg64 + fMod64 + f66RM ), // 0f fe /r paddd mm, mm/m
(fNoReg )); // -----
// flags for some opcodes which differ a lot depending on the modrm byte
COpcodeFlagsEx : array [0..9] of record
opcode : byte;
flags : array [0..15] of word;
end =
((opcode : $f6;
flags : (fNoReg + fMod8 + fPtr + fI8, // f6 /0 ib test r/mb, ib
fInvalid, // -----
fNoReg + fMod8 + fPtr + fClrM, // f6 /2 not r/mb (r)
fNoReg + fMod8 + fPtr + fClrM, // f6 /3 neg r/mb (r)
fNoReg + fMod8 + fPtr + fClrA, // f6 /4 mul r/mb eax
fNoReg + fMod8 + fPtr + fClrA, // f6 /5 imul r/mb eax
fNoReg + fMod8 + fPtr + fClrA, // f6 /6 div r/mb eax
fNoReg + fMod8 + fPtr + fClrA, // f6 /7 idiv r/mb eax
fNoReg + fMod8 + fPtr + fI8,
fInvalid,
fNoReg + fMod8 + fPtr + fClrM,
fNoReg + fMod8 + fPtr + fClrM,
fNoReg + fMod8 + fPtr + fClrA,
fNoReg + fMod8 + fPtr + fClrA,
fNoReg + fMod8 + fPtr + fClrA,
fNoReg + fMod8 + fPtr + fClrA)),
(opcode : $f7;
flags : (fNoReg + fMod32 + f66M + fPtr + fI32, // f7 /0 i? test r/m?, i?
fInvalid, // -----
fNoReg + fMod32 + f66M + fPtr + fClrM, // f7 /2 not r/m? (r)
fNoReg + fMod32 + f66M + fPtr + fClrM, // f7 /3 neg r/m? (r)
fNoReg + fMod32 + f66M + fPtr + fClrA, // f7 /4 mul r/m? eax edx
fNoReg + fMod32 + f66M + fPtr + fClrA, // f7 /5 imul r/m? eax edx
fNoReg + fMod32 + f66M + fPtr + fClrA, // f7 /6 div r/m? eax edx
fNoReg + fMod32 + f66M + fPtr + fClrA, // f7 /7 idiv r/m? eax edx
fNoReg + fMod32 + f66M + fPtr + fI32,
fInvalid,
fNoReg + fMod32 + f66M + fPtr + fClrM,
fNoReg + fMod32 + f66M + fPtr + fClrM,
fNoReg + fMod32 + f66M + fPtr + fClrA,
fNoReg + fMod32 + f66M + fPtr + fClrA,
fNoReg + fMod32 + f66M + fPtr + fClrA,
fNoReg + fMod32 + f66M + fPtr + fClrA)),
(opcode : $d8;
flags : (fNoReg + fMod32 + fPtr, // d8 /0 fadd mdreal
fNoReg + fMod32 + fPtr, // d8 /1 fmul mdreal
fNoReg + fMod32 + fPtr, // d8 /2 fcom mdreal
fNoReg + fMod32 + fPtr, // d8 /3 fcomp mdreal
fNoReg + fMod32 + fPtr, // d8 /4 fsub mdreal
fNoReg + fMod32 + fPtr, // d8 /5 fsubr mdreal
fNoReg + fMod32 + fPtr, // d8 /6 fdiv mdreal
fNoReg + fMod32 + fPtr, // d8 /7 fdivr mdreal
fRegSt + fMod80, // d8 c0+i fadd st(0), st(i)
fRegSt + fMod80, // d8 c8+i fmul st(0), st(i)
fNoReg + fMod80, // d8 d0+i fcom st(i)
fNoReg + fMod80, // d8 d8+i fcomp st(i)
fRegSt + fMod80, // d8 e0+i fsub st(0), st(i)
fRegSt + fMod80, // d8 e8+i fsubr st(0), st(i)
fRegSt + fMod80, // d8 f0+i fdiv st(0), st(i)
fRegSt + fMod80)), // d8 f8+i fdivr st(0), st(i)
(opcode : $d9;
flags : (fNoReg + fMod32 + fPtr, // d9 /0 fld mdreal
fInvalid, // -----
fNoReg + fMod32 + fPtr, // d9 /2 fst mdreal
fNoReg + fMod32 + fPtr, // d9 /3 fstp mdreal
fNoReg + fMod8 + fPtr, // d9 /4 fldenv m14/28byte
fNoReg + fMod16 + fPtr, // d9 /5 fldcw m2byte
fNoReg + fMod8 + fPtr, // d9 /6 fnstenv m14/28byte
fNoReg + fMod16 + fPtr, // d9 /7 fnstcw m2byte
fNoReg + fMod80, // d9 c0+i fld st(i)
fNoReg + fMod80, // d9 c8+i fxch st(i)
fNoReg, // d9 d0 fnop
fNoReg + fMod80, // d9 d8+i fstp1 st(i)
fNoReg, // d9 e0 fxxx
fNoReg, // d9 e8 fxxx
fNoReg, // d9 f0 fxxx
fNoReg)), // d9 f8 fxxx
(opcode : $da;
flags : (fNoReg + fMod32 + fPtr, // da /0 fiadd mdint
fNoReg + fMod32 + fPtr, // da /1 fimul mdint
fNoReg + fMod32 + fPtr, // da /2 ficom mdint
fNoReg + fMod32 + fPtr, // da /3 ficomp mdint
fNoReg + fMod32 + fPtr, // da /4 fisub mdint
fNoReg + fMod32 + fPtr, // da /5 fisubr mdint
fNoReg + fMod32 + fPtr, // da /6 fidiv mdint
fNoReg + fMod32 + fPtr, // da /7 fidivr mdint
fRegSt + fMod80, // da c0+i fcmovb st(0), st(i)
fRegSt + fMod80, // da c8+i fcmove st(0), st(i)
fRegSt + fMod80, // da d0+i fcmovbe st(0), st(i)
fRegSt + fMod80, // da d8+i fcmovu st(0), st(i)
fInvalid, // -----
fNoReg, // da e9 fucompp
fInvalid, // -----
fInvalid)), // -----
(opcode : $db;
flags : (fNoReg + fMod32 + fPtr, // db /0 fild mdint
fNoReg + fMod32 + fPtr, // db /1 fisttp mdint
fNoReg + fMod32 + fPtr, // db /2 fist mdint
fNoReg + fMod32 + fPtr, // db /3 fistp mdint
fInvalid, // -----
fNoReg + fMod80 + fPtr, // db /5 fld m80real
fInvalid, // -----
fNoReg + fMod80 + fPtr, // db /7 fstp m80real
fRegSt + fMod80, // db c0+i fcmovnb st(0), st(i)
fRegSt + fMod80, // db c8+i fcmovne st(0), st(i)
fRegSt + fMod80, // db d0+i fcmovnbe st(0), st(i)
fRegSt + fMod80, // db d8+i fcmovnu st(0), st(i)
fNoReg, // db e0 fxxx
fRegSt + fMod80, // db e8+i fucomi st(0), st(i)
fRegSt + fMod80, // db f0+i fcomi st(0), st(i)
fInvalid)), // -----
(opcode : $dc;
flags : (fNoReg + fMod64 + fPtr, // dc /0 fadd mqreal
fNoReg + fMod64 + fPtr, // dc /1 fmul mqreal
fNoReg + fMod64 + fPtr, // dc /2 fcom mqreal
fNoReg + fMod64 + fPtr, // dc /3 fcomp mqreal
fNoReg + fMod64 + fPtr, // dc /4 fsub mqreal
fNoReg + fMod64 + fPtr, // dc /5 fsubr mqreal
fNoReg + fMod64 + fPtr, // dc /6 fdiv mqreal
fNoReg + fMod64 + fPtr, // dc /7 fdivr mqreal
fRegSt + fMod80 + fOrder, // dc c0+i fadd st(i), st(0)
fRegSt + fMod80 + fOrder, // dc c8+i fmul st(i), st(0)
fNoReg + fMod80, // dc d0+i fcom st(i)
fNoReg + fMod80, // dc d8+i fcomp st(i)
fRegSt + fMod80 + fOrder, // dc e0+i fsubr st(i), st(0)
fRegSt + fMod80 + fOrder, // dc e8+i fsub st(i), st(0)
fRegSt + fMod80 + fOrder, // dc f0+i fdivr st(i), st(0)
fRegSt + fMod80 + fOrder)), // dc f8+i fdiv st(i), st(0)
(opcode : $dd;
flags : (fNoReg + fMod64 + fPtr, // dd /0 fld mqreal
fNoReg + fMod64 + fPtr, // dd /1 fisttp mqreal
fNoReg + fMod64 + fPtr, // dd /2 fst mqreal
fNoReg + fMod64 + fPtr, // dd /3 fstp mqreal
fNoReg + fMod8 + fPtr, // dd /4 frstor m94/108byte
fInvalid, // -----
fNoReg + fMod8 + fPtr, // dd /6 fnsave m94/108byte
fNoReg + fMod16 + fPtr, // dd /7 fnstsw m2byte
fNoReg + fMod80, // dd c0+i ffree st(i)
fNoReg + fMod80, // dd c8+i xch4 st(i)
fNoReg + fMod80, // dd d0+i fst st(i)
fNoReg + fMod80, // dd d8+i fstp st(i)
fNoReg + fMod80, // dd e0+i fucom st(i)
fNoReg + fMod80, // dd e8+i fucomp st(i)
fInvalid, // -----
fInvalid)), // -----
(opcode : $de;
flags : (fNoReg + fMod16 + fPtr, // de /0 fiadd mwint
fNoReg + fMod16 + fPtr, // de /1 fimul mwint
fNoReg + fMod16 + fPtr, // de /2 ficom mwint
fNoReg + fMod16 + fPtr, // de /3 ficomp mwint
fNoReg + fMod16 + fPtr, // de /4 fisub mwint
fNoReg + fMod16 + fPtr, // de /5 fisubr mwint
fNoReg + fMod16 + fPtr, // de /6 fidiv mwint
fNoReg + fMod16 + fPtr, // de /7 fidivr mwint
fRegSt + fMod80 + fOrder, // de c0+i faddp st(i), st(0)
fRegSt + fMod80 + fOrder, // de c8+i fmulp st(i), st(0)
fNoReg + fMod80, // de c0+i fcomp5 st(i)
fRegSt + fMod80 + fOrder, // de d8+i fcompp st(i), st(0)
fRegSt + fMod80 + fOrder, // de e0+i fsubrp st(i), st(0)
fRegSt + fMod80 + fOrder, // de e8+i fsubp st(i), st(0)
fRegSt + fMod80 + fOrder, // de f0+i fdivrp st(i), st(0)
fRegSt + fMod80 + fOrder)), // de f8+i fdivp st(i), st(0)
(opcode : $df;
flags : (fNoReg + fMod16 + fPtr, // df /0 fild mwint
fNoReg + fMod16 + fPtr, // df /1 fisttp mwint
fNoReg + fMod16 + fPtr, // df /2 fist mwint
fNoReg + fMod16 + fPtr, // df /3 fistp mwint
fNoReg + fMod80 + fPtr, // df /4 fbld m80dec
fNoReg + fMod64 + fPtr, // df /5 fild mqint
fNoReg + fMod80 + fPtr, // df /6 fbstp m80bcd
fNoReg + fMod64 + fPtr, // df /7 fistp mqint
fNoReg + fMod80, // df c0+i ffreep st(i)
fNoReg + fMod80, // df c8+i fxch7 st(i)
fNoReg + fMod80, // df d0+i fstp8 st(i)
fNoReg + fMod80, // df d8+i fstp9 st(i)
fNoReg, // df e0 fnstsw ax eax
fRegSt + fMod80, // df e8+i fucomip st(0), st(i)
fRegSt + fMod80, // df f0+i fcomip st(0), st(i)
fInvalid))); // -----
// register labels (byte/word/segment/dword)
CRegLabels : array [1..{$ifdef win64}6, 0..15{$else}4, 0..7{$endif}] of PAnsiChar =
(( 'al', 'cl', 'dl', 'bl', 'ah', 'ch', 'dh', 'bh' {$ifdef win64}, 'r8b', 'r9b', 'r10b', 'r11b', 'r12b', 'r13b', 'r14b', 'r15b' {$endif}),
( 'ax', 'cx', 'dx', 'bx', 'sp', 'bp', 'si', 'di' {$ifdef win64}, 'r8w', 'r9w', 'r10w', 'r11w', 'r12w', 'r13w', 'r14w', 'r15w' {$endif}),
( 'es', 'cs', 'ss', 'ds', 'fs', 'gs', nil, nil {$ifdef win64}, nil, nil, nil, nil, nil, nil, nil, nil {$endif}),
('eax', 'ecx', 'edx', 'ebx', 'esp', 'ebp', 'esi', 'edi' {$ifdef win64}, 'r8d', 'r9d', 'r10d', 'r11d', 'r12d', 'r13d', 'r14d', 'r15d' {$endif})
{$ifdef win64},
('rax', 'rcx', 'rdx', 'rbx', 'rsp', 'rbp', 'rsi', 'rdi' {$ifdef win64}, 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15' {$endif}),
( 'al', 'cl', 'dl', 'bl', 'spl', 'bpl', 'sil', 'dil' {$ifdef win64}, nil, nil, nil, nil, nil, nil, nil, nil {$endif})
{$endif}
);
// conditional labels (%cc)
Ccc : PAnsiChar = 'o|no|b|nb|z|nz|be|a|s|ns|p|np|l|ge|le|g';
// opcode labels (one byte + two byte + mmx/sse/sse2)
COpcodeLabels : PAnsiChar =
'aaa' +#1'aad' +#1'aam' +#1'aas' +#1'adc' +#1'add' +#1'addPS' +#1'and'
{$ifdef win64}
+#1'andnPS' +#1'movsxd' +#1'bound' +#1'bsf' +#1'bsr' +#1'bswap' +#1'bt' +#1'btc'
{$else}
+#1'andnPS' +#1'arpl' +#1'bound' +#1'bsf' +#1'bsr' +#1'bswap' +#1'bt' +#1'btc'
{$endif}
+#1'btr' +#1'bts' +#1'call' +#1'clc' +#1'cld' +#1'cli' +#1'clts' +#1'cmc'
+#1'cmov%cc' +#1'cmp' +#1'cmpPS' +#1'cmpsb' +#1'cmpxchg' +#1'cmpxchg8b' +#1'comisS' +#1'cpuid'
+#1'cvtPi2PS' +#1'cvtPS2Pi' +#1'cvttPS2Pi'+#1'daa' +#1'das' +#1'dec' +#1'divPS' +#1'emms'
+#1'enter' +#1'femms' +#1'hlt' +#1'imul' +#1'in' +#1'inc' +#1'insb' +#1'int'
+#1'int 3' +#1'int01' +#1'into' +#1'invd' +#1'iret' +#1'j%cc' +#1'jecxz' +#1'jmp'
+#1'lahf' +#1'lar' +#1'lds' +#1'lea' +#1'leave' +#1'les' +#1'lfs' +#1'lgs'
+#1'sysret' +#1'lodsb' +#1'loop' +#1'loope' +#1'loopne' +#1'lsl' +#1'lss' +#1'maxPS'
+#1'minPS' +#1'mov' +#1'movaPS' +#1'movd' +#1'movhPS' +#1'movlPS' +#1'movmskPS' +#1'movnti'
+#1'movntPS' +#1'movsb' +#1'movsx' +#1'movzx' +#1'mulPS' +#1'nop' +#1'or' +#1'orPS'
+#1'out' +#1'outsb' +#1'packssdw' +#1'packsswb' +#1'packuswb' +#1'paddb' +#1'paddd' +#1'paddq'
+#1'paddsb' +#1'paddsw' +#1'paddusb' +#1'paddusw' +#1'paddw' +#1'pand' +#1'pandn' +#1'pavgb'
+#1'pavgw' +#1'pcmpeqb' +#1'pcmpeqd' +#1'pcmpeqw' +#1'pcmpgtb' +#1'pcmpgtd' +#1'pcmpgtw' +#1'pextrw'
+#1'pinsrw' +#1'pmaddwd' +#1'pmaxsw' +#1'pmaxub' +#1'pminsw' +#1'pminub' +#1'pmovmskb' +#1'pmulhuw'
+#1'pmulhw' +#1'pmullw' +#1'pmuludq' +#1'pop' +#1'por' +#1'psadbw' +#1'pslld' +#1'psllq'
+#1'psllw' +#1'psrad' +#1'psraw' +#1'psrld' +#1'psrlq' +#1'psrlw' +#1'psubb' +#1'psubd'
+#1'psubq' +#1'psubsb' +#1'psubsw' +#1'psubusb' +#1'psubusw' +#1'psubw' +#1'punpckhbw'+#1'punpckhdq'
+#1'punpckhqdq'+#1'punpckhwd'+#1'punpcklbw'+#1'punpckldq'+#1'punpcklwd'+#1'punpcklqdq'+#1'push' +#1'pxor'
+#1'rcpPS' +#1'rdmsr' +#1'rdpmc' +#1'rdtsc' +#1'ret' +#1'rsm' +#1'rsqrtPS' +#1'sahf'
+#1'salc' +#1'sbb' +#1'scasb' +#1'set%cc' +#1'shld' +#1'shrd' +#1'shufPS' +#1'sqrtPS'
+#1'stc' +#1'std' +#1'sti' +#1'stosb' +#1'sub' +#1'subPS' +#1'syscall' +#1'sysenter'
+#1'sysexit' +#1'test' +#1'ucomisS' +#1'unpckhPS' +#1'unpcklPS' +#1'wait' +#1'wbinvd' +#1'wrmsr'
+#1'xadd' +#1'xchg' +#1'xlat' +#1'xor' +#1'xorPS'
+#1'cwde/cbw' +#1'cdq/cwd' +#1'pop %seg' +#1'popad/popa'
{$ifdef win64}
+#1'popfq/popf' +#1'push %seg' +#1'pushad/pusha' +#1'pushfq/pushf'
{$else}
+#1'popfd/popf' +#1'push %seg' +#1'pushad/pusha' +#1'pushfd/pushf'
{$endif}
+#1'outsd/outsw' +#1'insd/insw' +#1'movsd/movsw' +#1'cmpsd/cmpsw'
+#1'lodsd/lodsw' +#1'stosd/stosw' +#1'scasd/scasw' +#1'maskmovq/maskmovdqu'
+#1'movd///movq' +#1'movntq/movntdq' +#1'movq//movdq2q/movq2dq' +#1'movq/movdqa//movdqu'
+#1'movuPS//movPS/movPS' +#1'prefetch|prefetchw'
+#1'pshufw/pshufd/pshuflw/pshufhw' +#1'sgdt|sidt|lgdt|lidt|smsw||lmsw|invlpg'
+#1'sldt|str|lldt|ltr|verr|verw' +#1'||||bt|bts|btr|btc'
+#1'||psrld||psrad||pslld' +#1'||psrlq|psrldq|||psllq|pslldq'
+#1'||psrlw||psraw||psllw' +#1'cvtdq2ps/cvtps2dq//cvttps2dq'
+#1'cvtPs2Pd/cvtPd2Ps/cvtPd2Ps' +#1'cvttpd2dq//cvtpd2dq/cvtdq2pd'
+#1'add|or|adc|sbb|and|sub|xor|cmp' +#1'inc|dec|call|call|jmp|jmp|push'
+#1'rol|ror|rcl|rcr|shl|shr|sal|sar' +#1'test||not|neg|mul|imul|div|idiv'
+#1'add|mul|com|comp|sub|subr|div|divr|add|mul|com|comp|subr|sub|divr|div'
+#1'movhlPS:movlPS/movlPS/movddup/movsldup'
+#1'movlhPS:movhPS/movhPS/movlhPS:movhPS/movshdup'
+#1'prefetchnta|prefetcht0|prefetcht1|prefetcht2|prefetcht3'
+#1'fxsave|fxrstor|ldmxcsr|stmxcsr||lfence|mfence|sfence:clflush'
+#1'ld||st|stp|ldenv|ldcw|nstenv|nstcw|ld|xch|nop|stp1|chs-abs---tst-xam|' +
'ld1-ldl2t-ldl2e-ldpi-ldlg2-ldln2-ldz|2xm1-yl2x-ptan-patan-xtract-prem1-decstp-incstp|' +
'prem-yl2xp1-sqrt-sincos-rndint-scale-sin-cos'
+#1'iadd|imul|icom|icomp|isub|isubr|idiv|idivr|cmovb|cmove|cmovbe|cmovu||ucompp'
+#1'ild|isttp|ist|istp||ld||stp|cmovnb|cmovne|cmovnbe|cmovnu|neni-ndisi-nclex-ninit-nsetpm|ucomi|comi'
+#1'ld|isttp|st|stp|rstor||nsave|nstsw|free|xch4|st|stp|ucom|ucomp'
+#1'iadd|imul|icom|icomp|isub|isubr|idiv|idivr|addp|mulp|comp5|compp|subrp|subp|divrp|divp'
+#1'ild|isttp|ist|istp|bld|ild|bstp|istp|freep|xch7|stp8|stp9|nstsw ax|ucomip|comip'
+#1'andPS'
+#1'addsubpd' // 'addsubpd/addsubpd/addsubps'
+#1'haddpd' // 'haddpd/haddpd/haddps'
+#1'hsubpd' // 'hsubpd/hsubpd/hsubps'
+#1'lddqu'; // 'lddqu//lddqu';
// 3dnow opcode labels
C3dNowLabels : PAnsiChar =
'|'#$0c'pi2fw' +'|'#$0d'pi2fd' +'|'#$1c'pf2iw' +'|'#$1d'pf2id'
+'|'#$8a'pfnacc' +'|'#$8e'pfpnacc' +'|'#$90'pfcmpge' +'|'#$94'pfmin'
+'|'#$96'pfrcp' +'|'#$97'pfrsqrt' +'|'#$9a'pfsub' +'|'#$9e'pfadd'
+'|'#$a0'pfcmpgt' +'|'#$a4'pfmax' +'|'#$a6'pfrcpit1' +'|'#$a7'pfrsqit1'
+'|'#$aa'pfsubr' +'|'#$ae'pfacc' +'|'#$b0'pfcmpeq' +'|'#$b4'pfmul'
+'|'#$b6'pfrcpit2' +'|'#$b7'pmulhrw' +'|'#$bb'pswapd' +'|'#$bf'pavgusb';
// one byte opcode index into opcode label array
COpcodeLabelIndex : array [$00..$ff] of byte =
($06, $06, $06, $06, $06, $06, $c3, $c0, $57, $57, $57, $57, $57, $57, $c3, $00,
$05, $05, $05, $05, $05, $05, $c3, $c0, $a2, $a2, $a2, $a2, $a2, $a2, $c3, $c0,
$08, $08, $08, $08, $08, $08, $00, $24, $ad, $ad, $ad, $ad, $ad, $ad, $00, $25,
$bc, $bc, $bc, $bc, $bc, $bc, $00, $01, $1a, $1a, $1a, $1a, $1a, $1a, $00, $04,
$2e, $2e, $2e, $2e, $2e, $2e, $2e, $2e, $26, $26, $26, $26, $26, $26, $26, $26,
$97, $97, $97, $97, $97, $97, $97, $97, $7c, $7c, $7c, $7c, $7c, $7c, $7c, $7c,
$c4, $c1, $0b, $0a, $00, $00, $00, $00, $97, $2c, $97, $2c, $2f, $c7, $5a, $c6,
$36, $36, $36, $36, $36, $36, $36, $36, $36, $36, $36, $36, $36, $36, $36, $36,
$de, $de, $de, $de, $b2, $b2, $ba, $ba, $4a, $4a, $4a, $4a, $4a, $3c, $4a, $7c,
$56, $ba, $ba, $ba, $ba, $ba, $ba, $ba, $be, $bf, $13, $b6, $c5, $c2, $a0, $39,
$4a, $4a, $4a, $4a, $52, $c8, $1c, $c9, $b2, $b2, $ac, $cb, $42, $ca, $a3, $cc,
$4a, $4a, $4a, $4a, $4a, $4a, $4a, $4a, $4a, $4a, $4a, $4a, $4a, $4a, $4a, $4a,
$e0, $e0, $9d, $9d, $3e, $3b, $4a, $4a, $29, $3d, $9d, $9d, $31, $30, $33, $35,
$e0, $e0, $e0, $e0, $03, $02, $a1, $bb, $e2, $e7, $e8, $e9, $e2, $ea, $eb, $ec,
$45, $44, $43, $37, $2d, $2d, $59, $59, $13, $38, $38, $38, $2d, $2d, $59, $59,
$00, $32, $00, $00, $2b, $18, $e1, $e1, $14, $a9, $16, $ab, $15, $aa, $df, $df);
// two byte ($0f $xx) opcode index into opcode label array
COpcodeLabelIndex0f : array [$00..$ff] of byte =
($d6, $d5, $3a, $46, $00, $af, $17, $41, $34, $b7, $00, $00, $00, $d3, $2a, $00,
$d2, $d2, $e3, $4e, $b5, $b4, $e4, $4d, $e5, $00, $00, $00, $00, $00, $00, $56,
$4a, $4a, $4a, $4a, $00, $00, $00, $00, $4b, $4b, $21, $51, $23, $22, $b3, $1f,
$b8, $9c, $9a, $9b, $b0, $b1, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$19, $19, $19, $19, $19, $19, $19, $19, $19, $19, $19, $19, $19, $19, $19, $19,
$4f, $a8, $9f, $99, $ed, $09, $58, $bd, $07, $55, $dc, $db, $ae, $49, $27, $48,
$93, $95, $94, $5c, $6d, $6f, $6e, $5d, $8f, $92, $90, $5b, $96, $91, $4c, $d1,
$d4, $da, $d8, $d9, $6a, $6c, $6b, $28, $00, $00, $00, $00, $ef, $f0, $ce, $d1,
$36, $36, $36, $36, $36, $36, $36, $36, $36, $36, $36, $36, $36, $36, $36, $36,
$a4, $a4, $a4, $a4, $a4, $a4, $a4, $a4, $a4, $a4, $a4, $a4, $a4, $a4, $a4, $a4,
$c3, $c0, $20, $0f, $a5, $a5, $1d, $1d, $c3, $c0, $9e, $12, $a6, $a6, $e6, $2c,
$1d, $1d, $47, $11, $3f, $40, $54, $54, $00, $00, $d7, $10, $0c, $0d, $53, $53,
$b9, $b9, $1b, $50, $71, $70, $a7, $1e, $0e, $0e, $0e, $0e, $0e, $0e, $0e, $0e,
$ee, $86, $84, $85, $60, $7a, $d0, $77, $8c, $8d, $76, $66, $63, $64, $74, $67,
$68, $83, $82, $69, $78, $79, $dd, $cf, $8a, $8b, $75, $7d, $61, $62, $73, $98,
$f1, $81, $7f, $80, $7b, $72, $7e, $cd, $87, $8e, $88, $89, $5e, $65, $5f, $00);
type
// reg state
TRegState = array [0..{$ifdef win64}15{$else}7{$endif}] of pointer;
TPRegState = ^TRegState;
function ParseCode(code : pointer;
var prfxSeg : byte; // 26, 2e, 36, 3e, 64..65 segment override prefix
var prfx66 : boolean; // 66 - operand size prefix
var prfx67 : boolean; // 67 - address size prefix
var prfxF0 : boolean; // f0 - lock prefix
var prfxF2 : boolean; // f2 - repne (or sse2 special size) prefix
var prfxF3 : boolean; // f3 - rep (or sse2 special size) prefix
{$ifdef win64}
var prfxRex : byte; // 40..4f - REX prefix
{$endif}
var opcode : word; // instruction opcode, one or two bytes
var flgs : dword; // flags from one of the const flag tables
var labelIdx : integer; // label index
var modRm : byte; // modrm byte
var sib : byte; // sib byte
var regPtr : boolean; // do the modrm reg/mem bits address a register?
var reg : integer; // modrm reg/mem bits
var multi : integer; // modrm multi purpose bits
var regScale : integer; // sib scale register
var scale : integer; // sib scale factor
var dispSize : integer; // size of displacement data
var dispp : pointer; // at which address is the displacement data stored?
var dispi : NativeInt; // displacement data in integer form
var dispc : NativeUInt; // displacement data in cardinal form
var imLen : integer; // size of immediate data
var imVal : NativeInt; // immediate value (in integer form, if available)
var dw : integer; // 4 or 2, depending on the operand size prefix
regState : TPRegState;
useRegState : boolean;
tryRead_ : THandle ) : TCodeInfo; overload;
function CheckPrefix : boolean;
// strip off all prefixes and react on the important ones
begin
result := true;
case opcode of
{$ifdef win64}
$40..$4f : prfxRex := opcode;
{$endif}
$66 : begin
prfx66 := true;
// operand size prefix found, so we're working with words now
dw := 2;
end;
$67 : prfx67 := true;
$f0 : prfxF0 := true;
$f2 : begin
prfxF2 := true;
// f2 is only "repne" in one byte opcodes
// in two byte opcodes it is used as a special size flag for sse2
if byte(code^) <> $0f then
// "repne" usually changes the ecx register
if regState <> nil then
regState^[1] := nil;
end;
$f3 : begin
prfxF3 := true;
// f3 is only "rep" in one byte opcodes
// in two byte opcodes it is used as a special size flag for sse2
if byte(code^) <> $0f then
// "rep" usually changes the ecx register
if regState <> nil then
regState^[1] := nil;
end;
else // $26, $2e, $36, $3e and $64..$65 are segment override prefixes
if opcode and $E7 = $26 then
prfxSeg := (opcode shr 3) and $3 + 1
else
if opcode in [$64..$65] then
prfxSeg := opcode - $60 + 1
else
result := false;
end;
end;
procedure ParseModRm;
// parse the modrm byte (plus the sib byte, if available)
begin
modRm := byte(code^);
sib := 0;
regPtr := modRm and $c0 <> $c0;
reg := modRm and $7;
multi := (modRm shr 3) and $7;
scale := 0;
inc(NativeUInt(code));
case modRm and $c0 of
$40 : dispSize := 1;
$80 : {$ifndef win64}
if prfx67 then
dispSize := 2
else
{$endif}
dispSize := 4;
else dispSize := 0;
end;
{$ifndef win64}
if regPtr and prfx67 then begin
// the address size prefix has serious effect on modrm pointers
if (reg >= 0) and (reg <= 3) then begin
regScale := 6 + reg and 1;
scale := 1;
end;
if (modRm and $c0 = 0) and (reg = 6) then begin
dispSize := 2;
reg := -1;
end else
case reg of
0, 1, 7 : reg := 3;
2, 3, 6 : reg := 5;
4 : reg := 6;
5 : reg := 7;
end;
end else begin
{$endif}
if (reg = 4) and regPtr then begin
// there's also a sib byte, so let's parse it, too
sib := byte(code^);
inc(NativeUInt(code));
reg := sib and $7;
if (sib and $38 <> $20) {$ifdef win64} or (prfxRex and $2 <> 0) {$endif} then begin
scale := 1 shl (sib shr 6);
regScale := (sib shr 3) and $7;
end;
end;
if (modRm and $c0 = 0) and (reg = 5) then begin
dispSize := 4;
reg := -1;
end;
{$ifndef win64}
end;
{$endif}
{$ifdef win64}
if prfxRex and $1 <> 0 then
reg := reg + 8;
if prfxRex and $4 <> 0 then
multi := multi + 8;
if prfxRex and $2 <> 0 then
regScale := regScale + 8;
{$endif}
// store the displacement data pointer
dispp := code;
{$ifdef win64}
result.PDispl := code;
{$endif}
// store the displacement data into dispi and dispc
case dispSize of
1 : begin
dispi := shortInt(code^);
dispc := byte(code^);
end;
2 : begin
dispi := smallInt(code^);
dispc := word(code^);
end;
4 : begin
dispi := integer(code^);
dispc := dword(code^);
end;
else begin
dispi := 0;
dispc := 0;
end;
end;
{$ifdef win64}
result.DisplDword := dispc;
{$endif}
// skip the displacement data
inc(NativeUInt(code), dispSize);
result.ModRm := modRm;
end;
function ParseImmediateData : integer;
// how long (if available at all) is the immediate data for the current opcode?
begin
case flgs and fI of
fI8 : result := 1; // byte immediate data
fI16 : result := 2; // word immediate data
fI32 : {$ifdef win64}
if (prfxRex and $8 <> 0) and (opcode >= $b8) and (opcode <= $bf) then
result := 8
else
{$endif}
result := dw; // dword/word immediate data (operand size prefix)
else begin
result := 0;
case opcode of
$9a, $ea : result := dw + 2; // call/jmp $wwww:$dddddddd
$c8 : result := 3; // enter iw, ib
$a0..$a3 : {$ifdef win64} // mov (e)ax/al <-> [$dddddddd]
if prfx67 then result := 4
else result := 8;
{$else}
if prfx67 then result := 2
else result := 4;
{$endif}
end;
end;
end;
// now store the value in integer form, if it is 1, 2 or 4 bytes long
case result of
1 : imVal := shortInt(code^); // 1 byte integer immediate data
2 : imVal := smallInt(code^); // 2 byte integer immediate data
4 : imVal := integer (code^); // 4 byte integer immediate data
{$ifdef win64}
8 : imVal := int64 (code^); // 8 byte integer immediate data
{$endif}
else imVal := 0;
end;
end;
function IsValidOpcode : boolean;
// is the current opcode/modrm combination valid?
begin
// the flags already sort out a lot of invalid opcodes
result := flgs <> fInvalid;
if result then
// but some opcodes are valid only in specific situations
if opcode > $ff then begin
case byte(opcode) of
$00: result := multi <= 5;
$01: result := (multi <> 5) and (regPtr or (multi > 3));
$0d: result := regPtr and (multi <= 1);
$0f: result := byte(code^) in [$0c..$0d, $1c..$1d, $8a, $8e,
$90, $94, $96..$97, $9a, $9e,
$a0, $a4, $a6..$a7, $aa, $ae,
$b0, $b4, $b6..$b7, $bb, $bf];
$18: result := regPtr and (multi <= 3);
$20, $22: result := not regPtr;
$21, $23: result := not regPtr;
$6c, $6d: result := prfx66;
$71..$72: result := (not regPtr) and (multi in [2, 4, 6]);
$73: result := (not regPtr) and (multi in [2, 3, 6, 7]);
$7c..$7d, $d0:
result := prfx66 or prfxF2;
$ae: result := ( multi = 7 ) or
((multi < 4) and regPtr ) or
((multi > 4) and (not regPtr));
$b2, $b4..$b5, $c3, $c7:
result := regPtr;
$ba: result := multi >= 4;
$d6, $e6: result := prfx66 or prfxF2 or prfxF3;
$f0: result := prfxF2;
end;
end else
case byte(opcode) of
$62, $8d, $c4..$c5:
result := regPtr;
$8c, $8e: result := multi <= 5;
$d9: result := regPtr or (not (modRm in [$d1..$d7, $e2..$e3, $e6..$e7, $ef]));
$da: result := regPtr or (not (modRm in [$e8, $ea..$ef]));
$db: result := regPtr or (not (modRm in [$e5..$e7]));
$df: result := regPtr or (not (modRm in [$e1..$e7]));
$fe: result := multi <= 1;
$ff: result := (multi < 7) and (regPtr or (not (multi in [3, 5])));
end;
end;
procedure CheckTarget;
// is this a jmp or call instruction?
begin
if flgs and fJmpRel <> 0 then begin
// this is a relative jmp or call instruction, so we know the target
result.RelTarget := true;
result.PTarget := code;
result.Target := pointer(NativeInt(code) + imLen + imVal);
result.TargetSize := imLen;
result.Enlargeable := not (opcode in [$e0..$e3]);
if opcode = $e8 then
result.Call := true
else result.Jmp := true;
end else
if (opcode = $ff) and (multi in [2..5]) then begin
// jmp or call, target known or unknown, depending on the modrm byte
if multi in [2..3] then
result.Call := true
else result.Jmp := true;
if (reg = -1) and (scale = 0) and (dispSize = 4) then begin
// just a plain jmp/call [$xxxxxxxx], so we know the target
result.PPTarget := dispp;
result.IsValid := (prfxSeg <> 0) or TryRead(pointer(dispc), @result.Target, sizeOf(pointer), tryRead_);
// result.PTarget := dispc; // doesn't match mTargetSize in 64bit
result.TargetSize := sizeOf(pointer);
result.Enlargeable := true;
end else
if useRegState and (regState <> nil) and
(reg <> -1) and (regState^[reg] <> nil) and
(scale = 0) and (dispSize = 0) then begin
// "jmp/call exx" or "jmp/call [exx]" with known register value
if regPtr then begin
result.PPTarget := regState^[reg];
result.IsValid := (prfxSeg <> 0) or TryRead(result.PPTarget^, @result.Target, sizeOf(pointer), tryRead_);
end else begin
result.PTarget := regState^[reg];
result.Target := pointer(result.PTarget^);
end;
result.TargetSize := sizeOf(pointer);
end;
end else
if opcode = $9a then result.Call := true // unknown call
else if opcode = $ea then result.Jmp := true; // unknown jmp
end;
procedure CheckRegState;
// and here we keep track on which value the registers have
var clr : dword;
i1 : integer;
begin
clr := flgs and fClr;
// we clear all the register states that the flags tell us
if (clr = fClrA) or (clr = fClrMA) or (clr = fClrOA) then
regState^[0] := nil;
if (not regPtr) and ((clr = fClrM) or (clr = fClrRM) or (clr = fClrMA)) then
regState^[reg] := nil;
if (clr = fClrR) or (clr = fClrRM) then
regState^[multi] := nil;
if (clr = fClrO) or (clr = fClrOA) then
regState^[opcode and $7] := nil;
// a lot of special cases need to be handled manually
if opcode > $ff then begin
// here we do that for two byte opcodes
case byte(opcode) of
$00: if (multi <= 1) and (not regPtr) then
regState^[reg] := nil;
$01: if (multi = 4) and (not regPtr) then
regState^[reg] := nil;
$2c..$2d: if prfxF2 or prfxF3 then
regState^[multi] := nil;
$31..$33, $c7:
regState^[2] := nil;
$7e..$7f: if (not prfxF3) and (not regPtr) then
regState^[reg] := nil;
$a2: for i1 := 0 to 3 do
regState^[i1] := nil;
$ba: if (multi >= 5) and (not regPtr) then
regState^[reg] := nil;
end;
end else
// here one byte opcodes are handled
case opcode of
{$ifndef win64}
$61: for i1 := 0 to 7 do
regState^[i1] := nil;
{$endif}
$6c..$6d, $aa..$ab, $ae..$af:
regState^[7] := nil;
$6e..$6f, $ac..$ad:
regState^[6] := nil;
$80..$83: if (not regPtr) and (multi <> 7) then
regState^[reg] := nil;
$8b: if not regPtr then
regState^[multi] := regState^[reg];
$99: regState^[2] := nil;
$a4..$a7: begin
regState^[6] := nil;
regState^[7] := nil;
end;
$b8..$bf: if (not prfx66) {$ifdef win64} and (imLen = 8) {$endif} then
regState^[opcode and $7] := code;
$c8, $c9: regState^[5] := nil;
{$ifndef win64}
$c7: if (not regPtr) and (not prfx66) then
regState^[reg] := code;
{$endif}
$df: if modRm = $e0 then
regState^[0] := nil;
$e0..$e2: regState^[1] := nil;
$f7: if multi >= 4 then
regState^[2] := nil;
$ff: if (multi <= 1) and (not regPtr) then
regState^[reg] := nil;
end;
end;
var c1 : dword;
begin
// first of all let's initialize the variables
ZeroMemory(@result, sizeOf(TCodeInfo));
result.This := code;
if code <> nil then begin
opcode := 0;
try
// first let's initialize the prefix variables
prfxSeg := 0;
prfx66 := false;
prfx67 := false;
prfxF0 := false;
prfxF2 := false;
prfxF3 := false;
{$ifdef win64}
prfxRex := 0;
{$endif}
// typically we are working with dwords
dw := 4;
// now we strip off all prefixes, so we end up with the real opcode
repeat
opcode := byte(code^);
inc(NativeUInt(code));
until not CheckPrefix;
if opcode = $0f then begin
// we have a two byte opcode ($0f $xx)
opcode := byte(code^);
flgs := COpcodeFlags0f[opcode];
labelIdx := COpcodeLabelIndex0f[opcode];
opcode := opcode + $0f00;
inc(NativeUInt(code));
end else begin
// this is the usual one byte opcode
flgs := COpcodeFlags[opcode];
labelIdx := COpcodeLabelIndex[opcode];
end;
if flgs and fMod <> 0 then begin
// this instruction has a modrm byte, so let's parse it
ParseModRm;
// this is one of the few opcodes, which differ quite much,
// depending on the modrm opcode extension
// so have to get the real flags from an additional table
if flgs and fMod = fModOpc then
for c1 := 0 to high(COpcodeFlagsEx) do
if COpcodeFlagsEx[c1].opcode = opcode then begin
if regPtr then
flgs := COpcodeFlagsEx[c1].flags[multi]
else flgs := COpcodeFlagsEx[c1].flags[multi + 8];
break;
end;
end;
// how long (if available at all) is the immediate data for this opcode?
imLen := ParseImmediateData;
// is this opcode/modrm combination valid?
if IsValidOpcode then begin
// it is valid
result.IsValid := true;
{$ifdef win64}
result.RipRelative := (flgs and fMod <> 0) and (reg = -1) and (scale = 0) and (dispSize = 4) and (sib = 0);
if result.RipRelative then begin
dispc := NativeUInt(NativeInt(code) + imLen + dispi);
dispi := NativeInt(dispc);
result.DisplDword := dispc;
end;
{$endif}
// is this a jmp or call instruction?
CheckTarget;
if regState <> nil then
// and here we keep track on which value the registers have
CheckRegState;
end;
// skip the immediate data
inc(NativeUInt(code), imLen);
except result.IsValid := false end;
result.Opcode := opcode;
end;
result.Next := code;
if (result.Jmp or result.Call) and (result.Target = result.Next) then begin
result.Jmp := false;
result.Call := false;
result.RelTarget := false;
result.Target := nil;
result.PTarget := nil;
result.PPTarget := nil;
result.TargetSize := 0;
result.Enlargeable := false;
end;
end;
function CodeToHex(code: pointer; fixlen: boolean = true) : AnsiString;
begin
if fixlen then begin
{$ifdef win64}
if NativeUInt(code) > $ffffffff then
result := IntToHexExA(NativeUInt(code), 16)
else
{$endif}
result := IntToHexExA(NativeUInt(code), 8);
end else
result := IntToHexExA(NativeUInt(code), 1);
{$ifdef cstyle}
DeleteR(result, 1);
{$else}
Delete(result, 1, 1);
{$endif}
end;
function FindProcName(module: HMODULE; proc: pointer) : AnsiString;
begin
result := GetImageProcName(module, proc, true);
if (result = '') and (@GetProcNameFromMapFile <> nil) then
result := AnsiString(GetProcNameFromMapFile(proc));
end;
function ParseCode(code: pointer; var disAsm: AnsiString;
regState: TPRegState; useRegState: boolean) : TCodeInfo; overload;
// calls the raw "ParseCode" and adds a disassemble string
var prfxSeg : byte; // 26, 2e, 36, 3e, 64..65 segment override prefix
prfx66 : boolean; // 66 - operand size prefix
prfx67 : boolean; // 67 - address size prefix
prfxF0 : boolean; // f0 - lock prefix
prfxF2 : boolean; // f2 - repne (or sse2 special size) prefix
prfxF3 : boolean; // f3 - rep (or sse2 special size) prefix
{$ifdef win64}
prfxRex : byte; // 40..4f - REX prefix
{$endif}
opcode : word; // instruction opcode, one or two bytes
flgs : dword; // flags from one of the const flag tables
labelIdx : integer; // label index
modRm : byte; // modrm byte
sib : byte; // sib byte
regPtr : boolean; // do the modrm reg/mem bits address a register?
reg : integer; // modrm reg/mem bits
multi : integer; // modrm multi purpose bits
regScale : integer; // sib scale register
scale : integer; // sib scale factor
dispSize : integer; // size of displacement data
dispp : pointer; // at which address is the displacement data stored?
dispi : NativeInt; // displacement data in integer form
dispc : NativeUInt; // displacement data in dword form
imLen : integer; // size of immediate data
imVal : NativeInt; // immediate value (in integer form, if available)
dw : integer; // 4 or 2, depending on the operand size prefix
function St(reg: integer = 0) : AnsiString;
// return the "reg" floating point register as a string
begin
result := 'st';
if reg <> 0 then
result := result + '(' + IntToStrExA(reg) + ')';
end;
{$ifdef win64}
function DisAsmModRm(next: pointer) : AnsiString;
{$else}
function DisAsmModRm : AnsiString;
{$endif}
// compose the modrm byte (plus sib byte, if available) into a string
function CalcMSize : integer;
// how big is the register/memory, the modrm byte refers to?
begin
if prfx66 and ((flgs and f66 = f66M) or (flgs and f66 = f66RM)) then begin
if (flgs and fMod <> 0) and ((flgs and fReg > fReg32) or (flgs and fMod > fMod32)) then
// 66 prefix set for sse2 instructions means -> oword (16 bytes)
result := 16
else
// 66 prefix set for other instructions means -> word (2 bytes)
result := 2;
end else
// 66 prefix is not set, or doesn't have any effect this time
case flgs and fMod of
fMod8 : result := 1; // byte ( 1 byte )
fMod16 : result := 2; // word ( 2 bytes)
fMod32 : {$ifdef win64}
if ((prfxRex and $8 <> 0) and (opcode <> $63)) or
(opcode = $8f) or ((opcode = $ff) and (multi in [2, 4, 6])) or
((opcode >= $0f20) and (opcode <= $0f23)) then
result := 5
else
{$endif}
result := 4; // dword ( 4 bytes)
fMod64 : result := 8; // qword ( 8 bytes)
fMod80 : result := 10; // real (10 bytes)
else result := 16; // oword (16 bytes)
end;
end;
var rs, ms : integer; // register/modrm size
begin
result := '';
if regPtr then begin
// this modrm byte references memory
// now let's begin to set up the modrm result string
// a modrm memory location is always printed in "[]" brackets
result := '[';
{$ifdef win64}
if prfx67 then rs := 4
else rs := 5;
{$else}
if prfx67 then rs := 2
else rs := 4;
{$endif}
if reg <> -1 then
// address register * 1
result := result + CRegLabels[rs, reg];
if scale <> 0 then begin
// address register * scale
result := result + '+' + CRegLabels[rs, regScale];
if scale > 1 then
result := result + '*' + IntToStrExA(scale);
end;
if Length(result) > 1 then begin
// we do have address registers
// so print the displacement data (if available) in integer form
if dispi <> 0 then
if dispi > 0 then begin
if dispi >= 10 then
result := result + '+' + IntToHexExA(dispi)
else result := result + '+' + IntToStrExA(dispi);
end else
if dispi <= -10 then
result := result + '-' + IntToHexExA(-dispi)
else result := result + '-' + IntToStrExA(-dispi);
end else begin
// we don't have any address registers, just our displacement data
// so print the displacement data in dword form
// if no displacement data is available (strange), print out "0"
if dispc < 10 then
result := result + IntToStrExA(dispc)
else result := result + IntToHexExA(dispc);
end;
result := result + ']';
if result[2] = '+' then
// just in case our string begins with "[+", remove that ugly "+"
Delete(result, 2, 1);
// add segment override prefix, if available
if prfxSeg <> 0 then
result := CRegLabels[3, prfxSeg - 1] + ':' + result;
if flgs and fPtr <> 0 then begin
// the flags tell us to add a "xxx ptr" in front of our modrm string
result := ' ptr ' + result;
case CalcMSize of
1 : result := 'byte' + result;
2 : result := 'word' + result;
4 : result := 'dword' + result;
{$ifdef win64}
5 : result := 'qword' + result;
{$endif}
8 : result := 'qword' + result;
10 : result := 'tbyte' + result;
16 : result := 'oword' + result;
end;
end;
end else
// the modrm byte doesn't refer to memory
// it addresses a pure register, so print out the register
if (opcode < $d8) or (opcode > $df) then begin
// here we have the usual case, namely non floating point registers
ms := CalcMSize;
case ms of
{$ifdef win64}
1..5 : result := CRegLabels[ms, reg];
{$else}
1..4 : result := CRegLabels[ms, reg];
{$endif}
8 : result := 'mm' + IntToStrExA(reg);
16 : result := 'xmm' + IntToStrExA(reg);
end;
end else
// print out the floating pointer register
result := St(reg);
end;
function GetLabel(next: pointer) : AnsiString;
// compose the instruction label
var i1 : integer;
s1 : AnsiString;
begin
result := '';
if labelIdx > 0 then begin
// we do have a valid label index, so let's get the indexed string
s1 := SubStrA(COpcodeLabels, labelIdx, #1);
// replace some placeholders
ReplaceStrA(s1, '%cc', SubStrA(Ccc, opcode and $F + 1));
ReplaceStrA(s1, '%seg', CRegLabels[3, (opcode shr 3) and $7]);
// pick the correct sub string for the given prefixes (if available)
if prfx66 then result := SubStrA(s1, 2, '/')
else if prfxF2 then result := SubStrA(s1, 3, '/')
else if prfxF3 then result := SubStrA(s1, 4, '/');
if result = '' then
result := SubStrA(s1, 1, '/');
// pick the sub string for the modrm multi purpose value (if available)
i1 := SubStrCountA(result);
if i1 > 1 then
if (i1 > 8) and (not regPtr) then begin
result := SubStrA(result, 8 + multi + 1);
if PosStrA('-', result) > 0 then
result := SubStrA(result, reg + 1, '-');
end else
result := SubStrA(result, multi + 1);
// does the label depend on whether the modrm byte addresses memory?
if PosStrA(':', result) > 0 then
if regPtr then
result := SubStrA(result, 2, ':')
else result := SubStrA(result, 1, ':');
// a lot of sse2 labels need to be adjusted according to the prefixes
if prfx66 or prfxF2 then
ReplaceStrA(result, 'S', 'd')
else ReplaceStrA(result, 'S', 's');
if prfxF2 or prfxF3 then
ReplaceStrA(result, 'P', 's')
else ReplaceStrA(result, 'P', 'p');
// add the leading "f" for floating point instructions
if (opcode >= $d8) and (opcode <= $df) then
result := 'f' + result;
end else
if opcode = $0f0f then
// this is a 3dnow instruction, let's search for the label
for i1 := 1 to Length(C3dNowLabels) do
if (C3dNowLabels[i1] = '|') and (byte(C3dNowLabels[i1 + 1]) = byte(pointer(NativeUInt(next) - 1)^)) then begin
result := SubStrA(PAnsiChar(@C3dNowLabels[i1 + 2]), 1);
break;
end;
end;
function RegStr(len, reg: integer) : AnsiString;
// compose the register string
begin
if (opcode < $d8) or (opcode > $df) then begin
// this is a non floating point register
if prfx66 and ((flgs and f66 = f66R) or (flgs and f66 = f66RM)) then begin
if (flgs and fMod <> 0) and ((flgs and fReg > fReg32) or (flgs and fMod > fMod32)) then
// 66 prefix set for sse2 instructions means -> oword (16 bytes)
len := 16
else
// 66 prefix set for other instructions means -> word (2 bytes)
len := 2;
end {$ifdef win64} else
if (len = 4) and ((prfxRex and $8 <> 0) or (opcode in [$50..$5f])) then
len := 5{$endif};
case len of
{$ifdef win64}
1..2, 4..6 : result := CRegLabels[len, reg];
{$else}
1..2, 4 : result := CRegLabels[len, reg];
{$endif}
8 : result := 'mm' + IntToStrExA(reg);
16 : result := 'xmm' + IntToStrExA(reg);
end;
end else
// return the floating point register
result := St(reg);
end;
procedure IxToDw(out ims: AnsiString);
// return the immediate data as a string in dword form
var ic, ic2 : dword;
begin
ims := '';
// 3dnow instructions' ($0f0f) immediate data is hidden
// same with aam/aad, if the immediate data is the default value of $0a
if (opcode <> $0f0f) and
(((opcode <> $d4) and (opcode <> $d5)) or (byte(pointer(NativeUInt(code) - 1)^) <> $0a)) then begin
// in all other cases we show the immediate data
ic2 := 0;
case imLen of
1 : if opcode = $6a then begin
// byte immediate data sign extended
ic := dword(shortInt(code^));
if prfx66 then
ic := word(ic);
end else
// byte immediate data unsigned
ic := byte(code^);
2 : // word immediate data unsigned
ic := word(code^);
3 : begin
// "word, byte" immediate data unsigned
ic := word(code^);
ic2 := byte(pointer(NativeUInt(code) + 2)^);
end;
4 : if (opcode = $9a) or (opcode = $ea) then begin
// "word:word" immediate data unsigned
ic := word(code^);
ic2 := word(pointer(NativeUInt(code) + 2)^);
end else
// dword immediate data unsigned
ic := dword(code^);
else begin
// "word:dword" immediate data unsigned
ic := dword(code^);
ic2 := word(pointer(NativeUInt(code) + 4)^);
end;
end;
if (ic2 = 0) {$ifdef win64} and (imLen <> 8) {$endif} then begin
// we have the usual immediate form, just one value
if ic < 10 then
ims := IntToStrExA(ic)
else ims := IntToHexExA(ic);
end else
// strange form, either "iw, ib" or "iw:iw" or "iw:id"
case imLen of
3 : ims := IntToHexExA(ic) + ', ' + IntToHexExA(ic2);
{$ifdef win64}
8 : ims := IntToHexExA(int64(code^));
{$endif}
else ims := IntToHexExA(ic2) + ':' + IntToHexExA(ic);
end;
// we have a memory address without having a modrm byte
// this only happens with the opcodes a0..a3
if (flgs and fPtr <> 0) and (flgs and fMod = 0) then begin
ims := '[' + ims + ']';
// add segment override prefix, if available
if prfxSeg <> 0 then
ims := CRegLabels[3, prfxSeg - 1] + ':' + ims;
end;
end else
ims := '';
end;
procedure IxToInt(out ims: AnsiString);
// return the immediate data as a string in integer form
begin
// first of all lets print out the relative immediate value
if (imVal > -10) and (imVal < 10) then begin
if imVal < 0 then ims := '-' + IntToStrExA(-int64(imVal))
else if flgs and fJmpRel <> 0 then ims := '+' + IntToStrExA( imVal)
else ims := IntToStrExA( imVal);
end else
if imVal < 0 then ims := '-' + IntToHexExA(-int64(imVal))
else if flgs and fJmpRel <> 0 then ims := '+' + IntToHexExA( imVal)
else ims := IntToHexExA( imVal);
if result.RelTarget then
// this is a relative call or jmp, let's add the absolute target
ims := ims + ' (' + IntToHexExA(NativeUInt(result.Target)) + ')';
end;
function CheckFunctionName(target: pointer) : boolean;
// find out the name of the target, if available/possible
var nh : PImageNtHeaders32;
mh : HMODULE;
mn : AnsiString;
ci : TCodeInfo;
s1 : AnsiString;
begin
result := false;
s1 := '';
// to which module does the target belong?
if FindModuleA(target, mh, mn) then begin
// try to find the target name
s1 := FindProcName(mh, target);
if s1 = '' then begin
// no name found, maybe this is a static linking?
nh := GetImageNtHeaders(mh);
if (nh <> nil) and (NativeUInt(target) >= mh) and
(NativeUInt(target) <= mh + GetSizeOfImage(nh)) then begin
// at least the target is inside of the code area of the module
ci := ParseCode(target);
if (ci.Target <> nil) and FindModule(ci.Target, mh, mn) then
// and the target is itself a jmp or call again
// let's see whether we can find the name of the target's target
s1 := FindProcName(mh, ci.Target);
end;
end;
if s1 <> '' then begin
// the target name was found, let's add the name of the module
if (mh <> HInstance) and (PosStrA('(', s1) = 0) then begin
// but only if the module is not me
Delete(mn, 1, PosStrA('\', mn, maxInt, 1));
s1 := s1 + ' (' + mn + ')';
end;
// finally let's add the function name to the output string
disAsm := FillStrA(disAsm, -40) + ' ; ' + s1;
result := true;
end;
end;
end;
procedure CheckStringData(data: pointer; isPtr: boolean);
// does this instruction reference a string constant?
var mh : HMODULE;
s1 : AnsiString;
nh : PImageNtHeaders32;
pc : PAnsiChar;
i1, i2, i3, i4, i5 : integer;
begin
if FindModuleA(code, mh, s1) then begin
nh := GetImageNtHeaders(mh);
if nh <> nil then
if (NativeUInt(data) >= mh) and (NativeUInt(data) <= mh + GetSizeOfImage(nh) - 100) then begin
if isPtr and (NativeUInt(data^) > mh) and (NativeUInt(data^) <= mh + GetSizeOfImage(nh) - 100) then
data := pointer(data^);
if not CheckFunctionName(data) then begin
pc := data;
i2 := 0;
i3 := 0;
i4 := 0;
i5 := 0;
for i1 := 1 to 100 do begin
if pc^ in [#0, #10, #12] then break
else if pc^ in ['A'..'Z', 'a'..'z', '0'..'9', ' '] then inc(i2)
else if pc^ in [':', '\', '.', ','] then inc(i3)
else if pc^ in [#33..#93] then inc(i4)
else if byte(pc^) < 32 then exit
else inc(i5);
inc(pc);
end;
if (i1 > 4) and
(i2 * 2 > i3 * 5) and (i2 + (i3 div 2) > i4 * 8) and
(i2 + (i3 div 2) + (i4 div 4) > i5 * 10) then begin
SetString(s1, PAnsiChar(data), i1 - 1);
{$ifdef cstyle}
disAsm := FillStrA(disAsm, -40) + ' ; "' + s1 + '"';
{$else}
disAsm := FillStrA(disAsm, -40) + ' ; ''' + s1 + '''';
{$endif}
end;
end;
end;
end;
end;
var ms, rs, ims : AnsiString;
s2, s3, s4, s5 : AnsiString;
c1 : NativeUInt;
begin
disAsm := '';
result := ParseCode(code,
prfxSeg, prfx66, prfx67, prfxF0, prfxF2, prfxF3, {$ifdef win64}prfxRex,{$endif}
opcode, flgs, labelIdx,
modRm, sib, regPtr, reg, multi, regScale, scale,
dispSize, dispp, dispi, dispc, imLen, imVal, dw,
regState, useRegState, 0);
if result.This <> result.Next then begin
// is this opcode/modrm combination valid?
if result.IsValid then begin
// it is, first of all let's print out the code address
disAsm := CodeToHex(code) + ' ';
// then we check the prefixes
if prfxF0 then
disAsm := disAsm + 'lock ';
// f2/f3 are only "rep(ne)" in one byte opcodes
// in two byte opcodes they're used as special size flags for sse2
if opcode < $ff then begin
if prfxF2 then
disAsm := disAsm + 'repne ';
if prfxF3 then
disAsm := disAsm + 'rep ';
end;
// now we add the instruction label
disAsm := disAsm + GetLabel(result.Next);
// finally let's look at all the instruction parameters
if flgs and fMod <> 0 then begin
// we do have a modrm byte
// first check for some special cases
if (opcode > $ff) and (prfxF2 or prfxF3) then
// we seem to have a sse2 instruction with some special prefixes
if byte(opcode) in [$2a, $2c, $2d] then begin
// for these instructions change the mod64 or reg64 flags to 32
if flgs and fMod = fMod64 then
flgs := (flgs and (not fMod)) + fMod32
else flgs := (flgs and (not fReg)) + fReg32;
end else
if byte(opcode) in [$6f, $70, $7e..$7f] then begin
// for these instructions set both the mod and reg flags to 128
flgs := (flgs and (not fMod)) + fMod128;
flgs := (flgs and (not fReg)) + fReg128;
end else
if opcode = $0fd6 then
// finally for this instruction set either mod or reg to 128
if prfxF3 then
flgs := (flgs and (not fMod)) + fMod128
else flgs := (flgs and (not fReg)) + fReg128;
// now we compose the register string, if a register is available
// we have a modrm byte, so the register information is stored there
case flgs and FReg of
fReg8 : {$ifdef win64}
if (prfxRex <> 0) and (multi < 8) then
rs := RegStr(6, multi) // byte register win64 special mode
else
{$endif}
rs := RegStr(1, multi); // byte register
fReg16 : rs := RegStr(2, multi); // word register
fRegxx : if opcode > $ff then begin // segment/cr/dr register
if odd(opcode) then
rs := 'dr' + IntToStrExA(multi)
else rs := 'cr' + IntToStrExA(multi);
end else
rs := CRegLabels[3, multi];
fReg32 : rs := RegStr(4, multi); // (d)word register
fReg64 : rs := RegStr(8, multi); // qword register
fRegSt : rs := St; // st floating point register
fReg128 : rs := RegStr(16, multi); // oword register
else rs := '';
end;
// compose the modrm byte into a string
ms := DisAsmModRm{$ifdef win64}(result.Next){$endif};
end else begin
// we have no modrm byte
// we compose the register string, if a register is available
// check the flags for register information
ms := '';
case flgs and FReg of
fRegAl : rs := RegStr(1, 0); // al register
fRegEax : rs := RegStr(4, 0); // (e)ax register
fRegO8 : {$ifdef win64} // byte register depending on opcode
if prfxRex and $1 <> 0 then
rs := RegStr(1, opcode and $7 + 8)
else
{$endif}
rs := RegStr(1, opcode and $7);
fRegO32 : {$ifdef win64} // (d)word register depending on opcode
if prfxRex and $1 <> 0 then
rs := RegStr(4, opcode and $7 + 8)
else
{$endif}
rs := RegStr(4, opcode and $7);
fRegEaxO : begin // fRegEax + fRegO32
rs := RegStr(4, 0);
ms := RegStr(4, opcode and $7);
end;
fRegDxA : begin // dx register + (e)ax/al register
if odd(opcode) then rs := RegStr(4, 0)
else rs := RegStr(1, 0);
ms := 'dx';
end;
else rs := '';
end;
end;
// prepare code pointer for getting immediate data
code := pointer(NativeUInt(result.Next) - dword(imLen));
// now let's compose the immediate data string
if imLen > 0 then
if result.RelTarget or (opcode in [$69, $6b, $80..$83]) then
IxToInt(ims)
else IxToDw (ims);
// now we have 3 strings: register (rs), modrm (ms) and immediate (ims)
// let's sort out empty strings, the filled strings are stored into s2-s4
if rs <> '' then begin
s2 := rs;
if ms <> '' then begin
s3 := ms;
s4 := ims;
end else begin
s3 := ims;
s4 := '';
end;
end else begin
if ms <> '' then begin
s2 := ms;
s3 := ims;
end else begin
s2 := ims;
s3 := '';
end;
s4 := '';
end;
// do we have any parameters at all?
if s2 <> '' then begin
// yes, we have, so let's prepare the disassembler string for that
disAsm := FillStrA(disAsm, -19);
if disAsm[Length(disAsm)] <> ' ' then
disAsm := disAsm + ' ';
// if the flags tell us to swap the order of the parameters, we do so
if flgs and fOrder <> 0 then begin
s5 := s2;
s2 := s3;
s3 := s5;
end;
// now let's add all the available parameters
disAsm := disAsm + s2;
if s3 <> '' then begin
disAsm := disAsm + ', ' + s3;
if s4 <> '' then
disAsm := disAsm + ', ' + s4;
end;
end;
// the following special cases didn't fit into the flags logic
// so we handle them here manually
if (opcode = $d0) or (opcode = $d1) then
disAsm := disAsm + ', 1';
if (opcode = $d2) or (opcode = $d3) or (opcode = $0fa5) or (opcode = $0fad) then
disAsm := disAsm + ', cl';
// if this instruction is call/jmp, we try to find the target name
if result.Target <> nil then
CheckFunctionName(result.Target)
else begin
// does this instruction reference a string constant?
if (flgs and fMod <> 0) and (dispSize = sizeOf(pointer)) then
CheckStringData(pointer(dispc), true);
if imLen = sizeOf(pointer) then
CheckStringData(pointer(imVal), flgs and fPtr <> 0);
end;
end else begin
// this opcode/modrm combination is invalid, print the data in "db"s
for c1 := NativeUInt(result.This) to NativeUInt(code) + NativeUInt(imLen) - 1 do
disAsm := disAsm + #$d#$a + CodeToHex(pointer(c1)) + ' ' +
'db ' + IntToHexExA(dword(byte(pointer(c1)^)), 2);
Delete(disAsm, 1, 2);
end;
end;
end;
function ParseCode(code: pointer; regState: TPRegState; useRegState: boolean; tryRead_: THandle) : TCodeInfo; overload;
var prfxSeg : byte;
prfx66, prfx67, prfxF0, prfxF2, prfxF3 : boolean;
{$ifdef win64}
prfxRex : byte;
{$endif}
opcode : word;
flgs : dword;
labelIdx : integer;
modRm, sib : byte;
regPtr : boolean;
reg, multi, regScale, scale : integer;
dispSize : integer;
dispp : pointer;
dispi : NativeInt;
dispc : NativeUInt;
imLen : integer;
imVal : NativeInt;
dw : integer;
begin
result := ParseCode(code,
prfxSeg, prfx66, prfx67, prfxF0, prfxF2, prfxF3, {$ifdef win64}prfxRex,{$endif}
opcode, flgs, labelIdx,
modRm, sib, regPtr, reg, multi, regScale, scale,
dispSize, dispp, dispi, dispc, imLen, imVal, dw,
regState, useRegState, tryRead_);
end;
function ParseCode(code: pointer) : TCodeInfo; overload;
begin
result := ParseCode(code, nil, false, 0);
end;
function ParseCode_(code: pointer; tryRead_: THandle) : TCodeInfo;
begin
result := ParseCode(code, nil, false, tryRead_);
end;
function ParseCode(code: pointer; var disAsm: AnsiString) : TCodeInfo; overload;
begin
result := ParseCode(code, disAsm, nil, false);
end;
const CEmptyFunctionInfo : TFunctionInfo = (IsValid: false);
function ParseFunction_(func : pointer;
tryRead_ : THandle;
HandleAnyExceptionAddr : pointer;
HandleOnExceptionAddr : pointer;
HandleAutoExceptionAddr : pointer;
HandleFinallyAddr : pointer;
Halt0Addr : pointer) : TFunctionInfo;
const CENEWHDR = $003C; // offset of new EXE header
CEMAGIC = $5A4D; // old EXE magic id: 'MZ'
CPEMAGIC = $4550; // NT portable executable
var cac : integer; // counter for code areas
cca : integer; // current code area
mcb : NativeUInt; // module code begin
mce : NativeUInt; // module code end
mdb : NativeUInt; // module data begin
mde : NativeUInt; // module data end
rs : TRegState;
msvcrtThrowExceptionAddr : pointer;
procedure AddCodeArea(newAreaBegin, newCaller: pointer);
var i1, i2 : integer;
b1 : boolean;
nae : pointer;
begin
nae := nil;
with result do begin
b1 := true;
for i1 := 0 to cac - 1 do
with CodeAreas[i1] do
if newAreaBegin = AreaBegin then begin
if (calledFrom = nil) or
( (NativeUInt(newCaller) > NativeUInt(calledFrom)) and
(NativeUInt(newCaller) < NativeUInt(AreaBegin )) ) then
calledFrom := newCaller;
for i2 := 0 to high(Registers) do
if rs[i2] <> Registers[i2] then
Registers[i2] := nil;
b1 := false;
break;
end else
if (NativeUInt(newAreaBegin) > NativeUInt(AreaBegin)) and
(NativeUInt(newAreaBegin) <= NativeUInt(AreaEnd )) then begin
nae := AreaEnd;
AreaEnd := pointer(NativeUInt(newAreaBegin) - 1);
for i2 := 0 to high(Registers) do
if rs[i2] <> Registers[i2] then
rs[i2] := nil;
if i1 = cca then
cca := cac;
break;
end;
if b1 then begin
if cac = Length(CodeAreas) then
if CodeAreas = nil then
SetLength(CodeAreas, 8)
else SetLength(CodeAreas, Length(CodeAreas) * 2);
inc(cac);
with CodeAreas[cac - 1] do begin
AreaBegin := newAreaBegin;
AreaEnd := nae;
CaseBlock := false;
OnExceptBlock := false;
CalledFrom := newCaller;
Move(rs, Registers, sizeOf(Registers));
end;
end;
end;
end;
procedure AddSpecialBlock(newAreaBegin: pointer; areaLen: dword; isCaseBlock, isOnExceptBlock: boolean);
begin
with result do begin
if cac = Length(CodeAreas) then
if CodeAreas = nil then
SetLength(CodeAreas, 8)
else SetLength(CodeAreas, Length(CodeAreas) * 2);
inc(cac);
with CodeAreas[cac - 1] do begin
AreaBegin := newAreaBegin;
AreaEnd := pointer(NativeUInt(newAreaBegin) + areaLen - 1);
CaseBlock := isCaseBlock;
OnExceptBlock := isOnExceptBlock;
CalledFrom := nil;
ZeroMemory(@Registers, sizeOf(Registers));
end;
end;
end;
procedure CheckAddTarget(var ci: TCodeInfo; HandleAnyExceptionAddr, HandleOnExceptionAddr, HandleAutoExceptionAddr, HandleFinallyAddr: pointer);
var b1 : boolean;
i1 : integer;
ci2 : TCodeInfo;
by1 : byte;
mh : HMODULE;
s1 : AnsiString;
{$ifndef win64}
c1 : dword;
{$endif}
begin
if ci.Call or ci.Jmp then
with result do
if (ci.PTarget <> nil) or (ci.PPTarget <> nil) then begin
b1 := false;
{$ifndef win64}
if ci.Target <> nil then
if ci.Target = HandleAnyExceptionAddr then begin
// we have a Delphi try..except end block here
// "b1" makes sure that "jmp @HandleAnyException" is treated as a "call"
b1 := true;
// furthermore we add the code after the "jmp @HandleAnyException"
// as a new code area, cause @HandleAnyException will end up there
AddCodeArea(ci.Next, nil);
end else
if ci.Target = HandleFinallyAddr then begin
// we have a Delphi try..finally end block here
// "b1" makes sure that "jmp @HandleFinally" is treated as a "call"
b1 := true;
// furthermore we add the code after the "jmp @HandleFinally"
// as a new code area, cause @HandleFinally will end up there
AddCodeArea(ci.Next, nil);
// the code after @HandleFinally is a "jmp" to the finally block
// directly before the finally block there is a "push dword"
// this push is the address of the code after the finally block
// we try to find this push and add it as another code area
ci2 := ParseCode(ci.Next);
if ci2.Jmp and // we found the "jmp" after @HandleFinally
(NativeUInt(ci2.Target) > NativeUInt(func)) and // the target is inside of our function
(NativeUInt(ci2.Target) < NativeUInt(ci.This)) and // but before the @HandleFinally call
(TPByte(NativeUInt(ci2.Target) - 5)^ = $68) and // before the target there is a "push"
(TPCardinal(NativeUInt(ci2.Target) - 4)^ > NativeUInt(func)) then // the push target is inside of our function
// we found the push, now we add the code area
AddCodeArea(TPPointer(NativeUInt(ci2.Target) - 4)^, nil);
end else
if ci.Target = HandleOnExceptionAddr then begin
if TPCardinal(ci.Next)^ < $100 then begin
// we have a Delphi try..except end block here
// the except block has branches for different exception classes
// "b1" makes sure that "jmp @HandleOnException" is treated as a "call"
b1 := true;
// now we identify the branch information block
AddSpecialBlock(ci.Next, 4 + TPCardinal(ci.Next)^ * 8, false, true);
// finally we add each branch as a new code area
for c1 := 1 to TPCardinal(ci.Next)^ do
AddCodeArea(TPPointer(NativeUInt(ci.Next) + 8 * c1)^, nil);
end;
end else
if ci.Target = HandleAutoExceptionAddr then begin
// we have a safecall exception block here
// "b1" makes sure that "jmp @HandleAutoException" is treated as a "call"
b1 := true;
end else
if (ci.Target = BcbInitExceptBlockLDTC) and (NativeUInt(CodeAreas[cca].AreaBegin) < NativeUInt(ci.This)) then begin
// this is a BCB try..whatever statement
// somewhere before the call to "__InitExceptBlockLDTC"
// there's a "mov eax, $xxxxxxxx" call
c1 := 0;
ci2 := ParseCode(CodeAreas[cca].AreaBegin);
while ci2.IsValid and (ci2.This <> ci.This) do begin
if ci2.Opcode = $b8 then
// found a "mov eax", let's store the $xxxxxxxx
// there might be multiple such "mov"s
// we're interested in the last one only
// so we don't leave the loop just yet
c1 := TPCardinal(dword(ci2.This) + 1)^;
ci2 := ParseCode(ci2.Next);
end;
if c1 <> 0 then begin
inc(c1, 10);
case TPWord(c1)^ of
0: AddCodeArea(TPPointer(c1 + 2)^, nil); // try/finally ("C")
1: AddCodeArea(TPPointer(c1 + 6)^, nil); // try/except(expr ) ("C") XB_EXCEXP
2: AddCodeArea(TPPointer(c1 + 6)^, nil); // try/except(const) ("C") XB_EXCCNS
3: begin // try (C++)
c1 := TPCardinal(c1 + 2)^ + 8;
while TPPointer(c1)^ <> nil do begin
AddCodeArea(TPPointer(c1)^, nil);
inc(c1, 5 * 4);
end;
end;
end;
end;
end;
{$endif}
if (ci.TargetSize >= 4) and
( ci.Call or (NativeUInt(ci.Target) < mcb) or (NativeUInt(ci.Target) > mce) or b1 ) then begin
if (ci.Target <> nil) and TryRead(ci.Target, @by1, 1, tryRead_) and (by1 in [$e9, $eb, $ff]) then begin
// statically linked APIs are normally realized by a "call" to a "jmp"
// we want to have the *real* target, so we take the "jmp" target
ci2 := ParseCode(ci.Target);
if ci2.IsValid and (ci2.Target <> nil) and // if the "jmp" target is valid and
((not FindModuleA(ci.Target, mh, s1)) or (GetImageProcName(mh, ci.Target, false) = '')) and // if the original "call" is not an exported API and
FindModuleA(ci2.Target, mh, s1) and (GetImageProcName(mh, ci2.Target, false) <> '') then // if the "jmp" target is an exported API then
ci.Target := ci2.Target; // the "jmp" target is what we're looking for
end;
b1 := true;
for i1 := 0 to high(FarCalls) do
if FarCalls[i1].CodeAddr2 = ci.Next then begin
b1 := false;
break;
end;
if b1 then begin
SetLength(FarCalls, Length(FarCalls) + 1);
with FarCalls[high(FarCalls)] do begin
Call := ci.Call;
CodeAddr1 := ci.This;
CodeAddr2 := ci.Next;
Target := ci.Target;
RelTarget := ci.RelTarget;
PTarget := ci.PTarget;
PPTarget := ci.PPTarget;
if PPTarget <> nil then
inc(Copy.BufferLen, sizeOf(pointer));
{$ifdef win64}
RipRelative := ci.RipRelative;
if ci.RelTarget then
inc(Copy.BufferLen, 6 + sizeOf(pointer));
{$endif}
end;
end;
end else
AddCodeArea(ci.Target, ci.This);
for i1 := 0 to high(UnknownTargets) do
if UnknownTargets[i1].CodeAddr1 = ci.This then begin
UnknownTargets[i1] := UnknownTargets[high(UnknownTargets)];
SetLength(UnknownTargets, high(UnknownTargets));
break;
end;
end else begin
i1 := Length(UnknownTargets);
SetLength(UnknownTargets, i1 + 1);
UnknownTargets[i1].Call := ci.Call;
UnknownTargets[i1].CodeAddr1 := ci.This;
UnknownTargets[i1].CodeAddr2 := ci.Next;
end;
end;
procedure ParseCodeArea(var ci: TCodeInfo; HandleAnyExceptionAddr, HandleOnExceptionAddr, HandleAutoExceptionAddr, HandleFinallyAddr: pointer);
var i1 : integer;
cc : pointer;
cmp : record
switches : byte;
reg : byte;
jumpFound : boolean;
end;
b1 : boolean;
stp : NativeUInt;
rs2 : TRegState;
{$ifndef win64}
push : dword;
{$endif}
begin
{$ifndef win64}
push := 0;
{$endif}
cmp.switches := 0;
with result do begin
cc := CodeAreas[cca].AreaBegin;
Move(CodeAreas[cca].Registers, rs, sizeOf(rs));
stp := mce;
for i1 := 0 to high(CodeAreas) do
if (NativeUInt(CodeAreas[i1].AreaBegin) > NativeUInt(CodeAreas[cca].AreaBegin)) and
(NativeUInt(CodeAreas[i1].AreaBegin) < stp) then
stp := NativeUInt(CodeAreas[i1].AreaBegin);
while true do begin
rs2 := rs;
ci := ParseCode(cc, @rs, false, tryRead_);
if not ci.IsValid then
break;
CodeAreas[cca].AreaEnd := pointer(NativeUInt(ci.Next) - 1);
if ci.Opcode in [$c2..$c3, $ca..$cb, $cf] then // ret/iret
break;
{$ifndef win64}
if (ci.Opcode = $68) and (NativeUInt(ci.Next) - NativeUInt(ci.This) = 5) and (TPByte(ci.Next)^ = $64) then
// this is a "push dword" instruction, followed by a "fs:" prefix
// we store it because it can be part of a try..except/finally block
push := TPCardinal(dword(ci.This) + 1)^
else
if push <> 0 then begin
if (ci.Opcode = $ff) and
( ( (ci.ModRm and $f8 = $30) and // (1) push dword ptr fs:[register]
(NativeUInt(ci.Next) - NativeUInt(ci.This) = 3) and // instruction = 3 bytes
(rs2[ci.ModRm and $7] = nil) ) or // register = 0
( (ci.ModRm = $35) and // (2) push dword ptr fs:[dword]
(NativeUInt(ci.Next) - NativeUInt(ci.This) = 7) and // instruction = 7 bytes
(TPCardinal(dword(ci.This) + 3)^ = 0) ) // dword = 0
) then
// some exception handler is being installed
// we add this pointer to this handler to our list of code areas
AddCodeArea(pointer(push), nil);
push := 0;
end;
if cmp.switches = 0 then begin
if (ci.Opcode = $83) and (ci.ModRm in [$f8..$ff]) then begin
// we have found a "cmp reg, byteValue"
// this *may* be the beginning of a "case" statement
cmp.switches := TPByte(NativeUInt(cc) + 2)^;
cmp.reg := ci.ModRm and $7;
cmp.jumpFound := false;
end;
end else
if not cmp.jumpFound then begin
if (ci.Opcode in [$77, $7f]) or (ci.Opcode = $0f87) or (ci.Opcode = $0f8f) then begin
// this may still be a "case" statement with ja/jg
cmp.jumpFound := true;
inc(cmp.switches);
end else
if (ci.Opcode in [$73, $7d]) or (ci.Opcode = $0f83) or (ci.Opcode = $0f8d) then
// this may still be a "case" statement with jae/jge
cmp.jumpFound := true
else
cmp.switches := 0;
end else
if (ci.Opcode = $ff) and (ci.ModRm = $24) and
(TPByte(NativeUInt(cc) + 2)^ and $c7 = $85) and
((TPByte(NativeUInt(cc) + 2)^ shr 3) and $7 = cmp.reg) and
(TPPointer(NativeUInt(cc) + 3)^ = ci.Next) then begin
// it *is* a case statement!
// so let's fill the code areas for the case branches
for i1 := 0 to cmp.switches - 1 do
AddCodeArea(TPAPointer(ci.Next)^[i1], cc);
// now let's add a data area for the case jump pointers
AddSpecialBlock(ci.Next, cmp.switches * 4, true, false);
break;
end else
cmp.switches := 0;
{$endif}
b1 := (ci.Target <> nil) and (ci.Target = Halt0Addr);
CheckAddTarget(ci, HandleAnyExceptionAddr, HandleOnExceptionAddr, HandleAutoExceptionAddr, HandleFinallyAddr);
if (NativeUInt(CodeAreas[cac - 1].AreaBegin) > NativeUInt(CodeAreas[cca].AreaBegin)) and
(NativeUInt(CodeAreas[cac - 1].AreaBegin) < stp) then
stp := NativeUInt(CodeAreas[cac - 1].AreaBegin);
if b1 or ((ci.Target <> nil) and (ci.Target = msvcrtThrowExceptionAddr)) then // Halt or ThrowException?
break;
if ci.Jmp and ( (ci.Opcode in [$e9..$eb]) or
((ci.Opcode = $ff) and (ci.ModRm and $30 = $20)) ) then // jmp?
break;
cc := ci.Next;
b1 := false;
for i1 := 0 to high(rs) do
if rs[i1] <> rs2[i1] then begin
b1 := true;
break;
end;
if b1 or (NativeUInt(cc) >= stp) then begin
AddCodeArea(cc, nil);
break;
end;
end;
if NativeUInt(cc) > stp then
ci.IsValid := false;
end;
end;
procedure CalcCodeBegin(var ce: NativeUInt);
var i1 : integer;
begin
with result do begin
CodeBegin := pointer(-1);
ce := 0;
for i1 := 0 to cac - 1 do begin
if NativeUInt(CodeAreas[i1].AreaBegin) < NativeUInt(CodeBegin) then
CodeBegin := CodeAreas[i1].AreaBegin;
if NativeUInt(CodeAreas[i1].AreaEnd) > ce then
ce := NativeUInt(CodeAreas[i1].AreaEnd);
end;
CodeLen := ce - NativeUInt(CodeBegin) + 1;
end;
end;
procedure FindCodeArea;
var i1 : integer;
begin
with result do
for i1 := 0 to cac - 1 do
if CodeAreas[i1].AreaBegin = CodeBegin then begin
CodeBegin := pointer(NativeUInt(CodeAreas[i1].AreaEnd) + 1);
FindCodeArea;
break;
end;
end;
var ci : TCodeInfo;
ce : NativeUInt; // code end
i1, i2, i3 : integer;
mbi : TMemoryBasicInformation;
ih : PImageNtHeaders;
sh : PImageSectionHeader;
b1 : boolean;
begin
{$ifndef win64}
if HandleAnyExceptionAddr = nil then HandleAnyExceptionAddr := GetHandleAnyExceptionAddr;
{$endif}
if HandleOnExceptionAddr = nil then HandleOnExceptionAddr := GetHandleOnExceptionAddr;
if HandleAutoExceptionAddr = nil then HandleAutoExceptionAddr := GetHandleAutoExceptionAddr;
if HandleFinallyAddr = nil then HandleFinallyAddr := GetHandleFinallyAddr;
if Halt0Addr = nil then Halt0Addr := GetHalt0Addr;
msvcrtThrowExceptionAddr := GetProcAddress(GetModuleHandle('msvcrt.dll'), '_CxxThrowException');
Finalize(result);
ZeroMemory(@ci, sizeOf(ci));
result := CEmptyFunctionInfo;
with result do
if (VirtualQuery(func, mbi, sizeOf(mbi)) = sizeOf(mbi)) and (mbi.State = MEM_COMMIT) then begin
ih := pointer(GetImageNtHeaders(HMODULE(mbi.AllocationBase)));
if ih <> nil then begin
sh:= pointer(NativeUInt(ih) + sizeOf(TImageNtHeaders));
if sh.Characteristics and IMAGE_SCN_CNT_CODE <> 0 then begin
mcb := NativeUInt(mbi.AllocationBase) + sh^.VirtualAddress;
mce := mcb + sh.Misc.VirtualSize - 1;
if ih.FileHeader.NumberOfSections > 1 then begin
inc(sh);
if sh.Characteristics and IMAGE_SCN_CNT_CODE <> 0 then
mce := NativeUInt(mbi.AllocationBase) + sh.VirtualAddress + sh.Misc.VirtualSize - 1;
end;
end else begin
mcb := NativeUInt(mbi.AllocationBase) + ih.OptionalHeader.BaseOfCode;
mce := mcb + ih.OptionalHeader.SizeOfCode;
end;
{$ifdef win64}
mdb := mce;
sh := pointer(NativeUInt(ih) + sizeOf(TImageNtHeaders));
for i1 := 0 to ih.FileHeader.NumberOfSections - 1 do begin
if (sh.Characteristics and IMAGE_SCN_CNT_INITIALIZED_DATA <> 0) or
(sh.Characteristics and IMAGE_SCN_CNT_UNINITIALIZED_DATA <> 0) then begin
mdb := NativeUInt(mbi.AllocationBase) + sh.VirtualAddress;
break;
end;
inc(sh);
end;
{$else}
mdb := NativeUInt(mbi.AllocationBase) + ih.OptionalHeader.BaseOfData;
{$endif}
mde := mdb + ih^.OptionalHeader.SizeOfUninitializedData + ih^.OptionalHeader.SizeOfInitializedData - 1;
if mcb > mdb then mcb := mdb;
if mce < mde then mce := mde;
end else begin
mcb := NativeUInt(mbi.BaseAddress);
mce := mcb + mbi.RegionSize;
end;
EntryPoint := func;
Interceptable := false;
Copy.IsValid := true;
cac := 0;
ZeroMemory(@rs, sizeOf(rs));
AddCodeArea(EntryPoint, nil);
while true do begin
cca := -1;
for i1 := 0 to cac - 1 do
if CodeAreas[i1].AreaEnd = nil then begin
cca := i1;
break;
end;
if cca = -1 then
for i1 := high(UnknownTargets) downto 0 do begin
b1 := false;
for i2 := 0 to cac - 1 do
if (NativeUInt(UnknownTargets[i1].CodeAddr1) >= NativeUInt(CodeAreas[i2].AreaBegin)) and
(NativeUInt(UnknownTargets[i1].CodeAddr2) <= NativeUInt(CodeAreas[i2].AreaEnd )) then begin
ci := ParseCode(UnknownTargets[i1].CodeAddr1, @CodeAreas[i2].Registers, true, tryRead_);
if ci.Target <> nil then begin
CheckAddTarget(ci, HandleAnyExceptionAddr, HandleOnExceptionAddr, HandleAutoExceptionAddr, HandleFinallyAddr);
if CodeAreas[cac - 1].AreaEnd = nil then begin
cca := cac - 1;
b1 := true;
end;
end;
break;
end;
if b1 then
break;
end;
if cca = -1 then begin
b1 := false;
for i1 := 0 to high(UnknownTargets) do
if not UnknownTargets[i1].Call then begin
b1 := true;
break;
end;
if b1 then begin
CalcCodeBegin(ce);
FindCodeArea;
if (NativeUInt(CodeBegin) - 1 <> ce) and (ce - NativeUInt(CodeBegin) < $400) then begin
// some bytes in the middle of our function are not disassembled yet
// plus there are jump instructions to unknown targets in the code
// so we guess that these not yet disassembled parts are code, too
cca := cac;
ZeroMemory(@rs, sizeOf(rs));
AddCodeArea(CodeBegin, nil);
ParseCodeArea(ci, HandleAnyExceptionAddr, HandleOnExceptionAddr, HandleAutoExceptionAddr, HandleFinallyAddr);
if not ci.IsValid then begin
// oooops, we guessed wrong, let's pretend we didn't even try...
cac := cca;
ci.IsValid := true;
break;
end;
end else
break;
end else
break;
end else begin
ParseCodeArea(ci, HandleAnyExceptionAddr, HandleOnExceptionAddr, HandleAutoExceptionAddr, HandleFinallyAddr);
if not ci.IsValid then
break;
end;
end;
SetLength(CodeAreas, cac);
if not ci.IsValid then begin
IsValid := false;
LastErrorAddr := ci.Next;
LastErrorNo := CErrorNo_InvalidCode;
LastErrorStr := CErrorStr_InvalidCode;
end else begin
IsValid := true;
CalcCodeBegin(ce);
inc(Copy.BufferLen, CodeLen + sizeOf(pointer));
i1 := 0;
i2 := 0;
repeat
b1 := true;
if CodeAreas[i1].CaseBlock or CodeAreas[i1].OnExceptBlock or (CodeAreas[i1].CalledFrom <> nil) then
break;
inc(i2, NativeUInt(CodeAreas[i1].AreaEnd) - NativeUInt(CodeAreas[i1].AreaBegin) + 1);
for i3 := 1 to high(CodeAreas) do
if NativeUInt(CodeAreas[i3].AreaBegin) = NativeUInt(CodeAreas[i1].AreaEnd) + 1 then begin
i1 := i3;
b1 := false;
break;
end;
until b1;
if i2 >= 6 then begin
Interceptable := true;
ci.Next := EntryPoint;
repeat
ci := ParseCode(ci.Next);
if (ci.Call or ci.Jmp) and
((ci.PTarget <> nil) or (ci.PPTarget <> nil)) and
(not ci.Enlargeable) and (ci.TargetSize < sizeOf(pointer)) then begin
Interceptable := false;
break;
end;
until NativeUInt(ci.Next) - NativeUInt(EntryPoint) >= 6;
end;
end;
end else begin
IsValid := false;
LastErrorAddr := func;
LastErrorNo := ERROR_INVALID_PARAMETER;
LastErrorStr := ErrorCodeToStrA(LastErrorNo);
end;
end;
function ParseFunction(func: pointer) : TFunctionInfo; overload;
begin
result := ParseFunction_(func, 0, nil, nil, nil, nil, nil);
end;
function ParseFunctionEx(func: pointer; var disAsm: AnsiString; exceptAddr: pointer;
maxLines: integer; autoDelimiters: boolean) : TFunctionInfo;
var len : integer;
lines : integer;
before : boolean;
procedure FindCodeArea(var ca: integer);
var min, cur : NativeUInt;
i1 : integer;
begin
if ca = -1 then min := 0
else min := NativeUInt(result.CodeAreas[ca].AreaEnd) + 1;
cur := high(cur);
ca := -1;
for i1 := 0 to high(result.CodeAreas) do
if (NativeUInt(result.CodeAreas[i1].AreaBegin) >= min) and
(NativeUInt(result.CodeAreas[i1].AreaBegin) < cur) then begin
cur := NativeUInt(result.CodeAreas[i1].AreaBegin);
ca := i1;
end;
end;
var cai : array of integer;
name : AnsiString;
lineNoLen : integer;
procedure DoItAll(justCount: boolean; var first: integer);
var lineNo : integer;
minAddr, maxAddr : pointer;
procedure AddLine(code: pointer; line: AnsiString = ''; addCodePos: boolean = true; addLineNo: boolean = true);
var i1, i2 : integer;
begin
if (not justCount) and (lines >= first) then begin
if addCodePos then begin
if line <> '' then
line := ' ' + line;
line := CodeToHex(code) + line;
end else
if code = exceptAddr then
line[10] := '>';//'»';
if lineNoLen > 0 then begin
i1 := lineNo;
if addLineNo then
GetLineNumber(code, lineNo, minAddr, maxAddr);
if i1 <> lineNo then begin
if Length(line) < 10 then
line := line + ' ' + IntToStrExA(lineNo, lineNoLen)
else Insert(IntToStrExA(lineNo, lineNoLen) + ' ', line, 10);
end else
if Length(line) >= 10 then
Insert(FillStrA('', lineNoLen + 1), line, 10);
end;
line := line + #$d#$a;
while len + Length(line) > Length(disAsm) do
if disAsm = '' then
SetLength(disAsm, 200)
else SetLength(disAsm, (len + Length(line)) * 2);
Move(line[1], disAsm[len + 1], Length(line));
inc(len, Length(line));
end;
if maxLines > 0 then begin
inc(lines);
if (NativeUInt(exceptAddr) <= NativeUInt(code)) and before then begin
if lines > maxLines * 12 div 10 then begin
if justCount then begin
first := lines - maxLines - 2;
if first < 0 then
first := 0;
end else begin
first := 0;
i2 := len;
for i1 := 0 to maxLines do
i2 := PosStrA(#$d#$a, disAsm, i2 - 2, 1);
if i2 > 0 then begin
Delete(disAsm, 1, i2 + 1);
dec(len, i2 + 1);
disAsm := '[...]' + #$d#$a + disAsm;
inc(len, 7);
end;
end;
end;
lines := 0;
before := false;
end;
end;
end;
var b1, b2 : boolean;
i1, i2, i3 : integer;
s1 : AnsiString;
ci : TCodeInfo;
rs : TRegState;
clss : TClass;
lastCi : pointer;
begin
lineNo := 0;
minAddr := nil;
maxAddr := nil;
lines := 0;
before := true;
b1 := false;
b2 := false;
lastCi := nil;
ci.IsValid := false;
for i1 := 0 to high(cai) do
with result.CodeAreas[cai[i1]] do begin
if (AreaBegin = func) or (autoDelimiters and (CalledFrom <> nil)) then begin
if b1 then
AddLine(lastCi);
if (not justCount) and (lines >= first) then begin
if AreaBegin = func then
s1 := name
else s1 := 'loc_' + CodeToHex(AreaBegin, false);
s1 := s1 + ':';
if AreaBegin = func then
s1 := FillStrA(s1, -31) + ' ; function entry point';
AddLine(AreaBegin, s1, true, false);
end else
AddLine(AreaBegin, '');
end;
if CaseBlock then begin
if autoDelimiters then begin
if b1 then
AddLine(lastCi);
if b2 then begin
AddLine(ci.This, '; ---------------------------------------------------------');
AddLine(ci.This);
end;
end;
for i2 := 0 to (NativeUInt(AreaEnd) + 1 - NativeUInt(AreaBegin)) div 4 - 1 do begin
if (not justCount) and (lines >= first) then begin
s1 := ' dd loc_' + CodeToHex(TPAPointer(AreaBegin)^[i2], false);
if i2 = 0 then
s1 := FillStrA(s1, -31) + ' ; case jump table';
AddLine(pointer(NativeUInt(AreaBegin) + NativeUInt(i2) * 4), s1);
end else
AddLine(pointer(NativeUInt(AreaBegin) + NativeUInt(i2) * 4), '');
if (not before) and (justCount or (lines >= maxLines)) then
break;
end;
if autoDelimiters and (before or (lines < maxLines)) then begin
AddLine(pointer(NativeUInt(AreaEnd) + 1));
AddLine(pointer(NativeUInt(AreaEnd) + 1), '; ---------------------------------------------------------');
AddLine(pointer(NativeUInt(AreaEnd) + 1));
end;
b1 := false;
b2 := false;
end else
if OnExceptBlock then begin
// this is a Delphi style "exception on E: Exception do ..." block
i3 := 1;
if not justCount then
for i2 := 1 to TPCardinal(AreaBegin)^ do
if TPAPointer(AreaBegin)^[i2 * 2 - 1] <> nil then begin
try
clss := TClass(TPAPointer(AreaBegin)^[i2 * 2 - 1]^);
if Length(clss.ClassName) > i3 then
i3 := Length(clss.ClassName);
except end;
end;
for i2 := 1 to TPCardinal(AreaBegin)^ do begin
if (not justCount) and (lines >= first) then begin
if TPAPointer(AreaBegin)^[i2 * 2 - 1] <> nil then begin
try
s1 := AnsiString(TClass(TPAPointer(AreaBegin)^[i2 * 2 - 1]^).ClassName);
except
s1 := 'EUnknown';
end;
s1 := ' on ' + FillStrA(s1, -i3) + ' do';
end else
s1 := ' else' + FillStrA('', i3 + 2);
s1 := s1 + ' loc_' + CodeToHex(TPAPointer(AreaBegin)^[i2 * 2], false);
end else
s1 := '';
AddLine(pointer(NativeUInt(AreaBegin) + NativeUInt(i2) * 8 - 4), s1);
if (not before) and (justCount or (lines >= maxLines)) then
break;
end;
if autoDelimiters and (before or (lines < maxLines)) then begin
AddLine(pointer(NativeUInt(AreaEnd) + 1));
AddLine(pointer(NativeUInt(AreaEnd) + 1), '; ---------------------------------------------------------');
AddLine(pointer(NativeUInt(AreaEnd) + 1));
end;
b1 := false;
b2 := false;
end else begin
ci.Next := AreaBegin;
Move(Registers, rs, sizeOf(rs));
repeat
b1 := true;
b2 := true;
if ci.IsValid then
lastCi := ci.This
else
lastCi := AreaBegin;
if (not justCount) and (lines >= first) then begin
ci := ParseCode(ci.Next, s1, @rs, true);
if ci.RelTarget then
for i2 := 0 to high(result.CodeAreas) do
if ci.Target = result.CodeAreas[i2].AreaBegin then begin
Delete(s1, PosStrA(' ', s1, 12), maxInt);
s1 := FillStrA(s1, -19);
if s1[Length(s1)] <> ' ' then
s1 := s1 + ' ';
if ci.Target = func then
s1 := s1 + name
else s1 := s1 + 'loc_' + CodeToHex(ci.Target, false);
break;
end;
AddLine(ci.This, s1, false);
end else begin
ci := ParseCode(ci.Next, @rs, true, 0);
AddLine(ci.This, '', false);
end;
if autoDelimiters and
(ci.Jmp or ci.Call or (ci.Opcode in [$c2..$c3, $ca..$cb, $cf])) and
((NativeUInt(ci.Next) <= NativeUInt(AreaEnd)) or (i1 < high(cai))) then begin
AddLine(ci.This);
if ((not (ci.Jmp or ci.Call)) or (ci.Jmp and (ci.Opcode in [$e9..$eb, $ff]))) and
( (ci.Target = nil) or
( (ci.Target <> GetHandleAnyExceptionAddr) and
(ci.Target <> GetHandleOnExceptionAddr ) and
(ci.Target <> GetHandleFinallyAddr ) ) ) then begin
AddLine(ci.This, '; ---------------------------------------------------------');
AddLine(ci.This);
b2 := false;
end;
b1 := false;
end;
until (NativeUInt(ci.Next) > NativeUInt(AreaEnd)) or ((not before) and (justCount or (lines >= maxLines)));
end;
if (not before) and (lines >= maxLines) and
((NativeUInt(ci.Next) <= NativeUInt(AreaEnd)) or (i1 < high(cai))) then begin
if not justCount then begin
SetLength(disAsm, len);
disAsm := disAsm + '[...]';
len := len + 7;
end;
exit;
end;
end;
end;
var i1, i2 : integer;
p1 : pointer;
first : integer;
mbi : TMemoryBasicInformation;
begin
len := 0;
result := ParseFunction(func);
if result.IsValid then begin
if (VirtualQuery(func, mbi, sizeOf(mbi)) = sizeOf(mbi)) and
(mbi.State = MEM_COMMIT) and (mbi.AllocationBase <> nil) then
name := FindProcName(NativeUInt(mbi.AllocationBase), func)
else name := '';
if name = '' then
name := 'sub_' + CodeToHex(func, false)
else name := 'public ' + name;
lineNoLen := 0;
if @GetLineNumber <> nil then begin
i1 := 0;
GetLineNumber(pointer(NativeUInt(result.EntryPoint) + NativeUInt(result.CodeLen) - 1), i1, p1, p1);
if i1 = 0 then begin
GetLineNumber(exceptAddr, i1, p1, p1);
i1 := i1 * 10;
end;
if i1 <> 0 then
lineNoLen := Length(IntToStrExA(i1));
end;
SetLength(cai, Length(result.CodeAreas));
i2 := -1;
for i1 := 0 to high(cai) do begin
FindCodeArea(i2);
if i2 = -1 then begin
disAsm := 'Internal error in ParseFunction while composing the disassembling string... :-(';
exit;
end;
cai[i1] := i2;
end;
first := 0;
if maxLines > 0 then
DoItAll(true, first);
DoItAll(false, first);
end;
SetLength(disAsm, len - 2);
end;
function ParseFunction(func: pointer; var disAsm: AnsiString) : TFunctionInfo; overload;
begin
result := ParseFunctionEx(func, disAsm, nil, 0, true);
end;
// ***************************************************************
var kernel32handle_ : HMODULE = 0;
function kernel32handle : HMODULE;
var s1 : AnsiString;
begin
if kernel32handle_ = 0 then begin
s1 := DecryptStr(CKernel32);
if GetVersion and $80000000 = 0 then
kernel32handle_ := GetModuleHandleW(pointer(AnsiToWideEx(s1)))
else kernel32handle_ := GetModuleHandleA(pointer( s1 ));
end;
result := kernel32handle_;
end;
var kernelbasehandle_ : HMODULE = 0;
function kernelbasehandle : HMODULE;
// in Windows 7 many kernel32.dll APIs are actually in kernelbase.dll
// for hooking purposes it's better to hook the kernelbase.dll APIs directly
var s1 : AnsiString;
begin
if kernelbasehandle_ = 0 then
if GetVersion and $80000000 = 0 then begin
s1 := DecryptStr(CKernelbase);
kernelbasehandle_ := GetModuleHandleW(pointer(AnsiToWideEx(s1)));
end;
if kernelbasehandle_ = 0 then
kernelbasehandle_ := 1;
result := kernelbasehandle_;
end;
function KernelProc(const api: AnsiString; doubleCheck: boolean = false) : pointer;
begin
result := nil;
if kernelbasehandle > 1 then
result := GetImageProcAddress(kernelbasehandle, DecryptStr(api), doubleCheck);
if result = nil then
result := GetImageProcAddress(kernel32handle, DecryptStr(api), doubleCheck);
end;
var ntdllhandle_ : HMODULE;
function ntdllhandle : HMODULE;
begin
if ntdllhandle_ = 0 then
ntdllhandle_ := GetModuleHandleW(pointer(AnsiToWideEx(DecryptStr(CNtDll))));
result := ntdllhandle_;
end;
function NtProc(const api: AnsiString; doubleCheck: boolean = false) : pointer;
var nh : PImageNtHeaders32;
begin
if api = '' then begin
result := nil;
nh := GetImageNtHeaders(ntdllhandle);
if nh <> nil then
if nh^.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC then
result := @PImageOptionalHeader64(@nh^.OptionalHeader).AddressOfEntryPoint
else result := @ nh^.OptionalHeader .AddressOfEntryPoint;
end else
result := GetImageProcAddress(ntdllhandle, DecryptStr(api), doubleCheck);
end;
function GetExportDirectory(code: pointer; out module: HMODULE; out pexp: PImageExportDirectory) : boolean;
var mbi : TMemoryBasicInformation;
arrCh : array [0..MAX_PATH] of AnsiChar;
pinh : PImageNtHeaders;
begin
result := false;
if (VirtualQuery(code, mbi, sizeOf(mbi)) = sizeOf(mbi)) and
(mbi.State = MEM_COMMIT) and (mbi.AllocationBase <> nil) and
(GetModuleFileNameA(HMODULE(mbi.AllocationBase), arrCh, MAX_PATH) <> 0) then begin
module := HMODULE(mbi.AllocationBase);
if TPWord(module)^ = CEMAGIC then begin
pinh := pointer(module + TPCardinal(module + CENEWHDR)^);
if pinh^.signature = CPEMAGIC then begin
pexp := pointer(module + pinh^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
result := pexp <> nil;
end;
end;
end;
end;
function SolveW9xDebugMode(code: pointer) : pointer;
var module : HMODULE;
pexp : PImageExportDirectory;
i1 : integer;
c1 : cardinal;
by1 : byte;
begin
result := code;
if {(DebugHook <> 0) and} (GetVersion and $80000000 <> 0) and
(code <> nil) and TryRead(code, @by1, 1) and (by1 = $68) and
(not GetExportDirectory(code, module, pexp)) then begin // w9x debug mode?
code := TPPointer(NativeUInt(code) + 1)^;
if GetExportDirectory(code, module, pexp) then
with pexp^ do
for i1 := 0 to NumberOfFunctions - 1 do begin
c1 := TPACardinal(module + AddressOfFunctions)^[i1];
if module + c1 = NativeUInt(code) then begin
result := code;
break;
end;
end;
end;
end;
var FMagic : cardinal = 0;
FMagic95 : boolean = false;
FMagicReady : boolean = false;
function Magic : cardinal;
function Fs(index: cardinal) : cardinal;
asm
mov eax, fs:[eax]
end;
var c1 : dword;
begin
if not FMagicReady then begin
FMagicReady := true;
FMagic := GetCurrentThreadID xor (Fs($18) - $10);
if (not TryRead(@TPACardinal(GetCurrentThreadID xor FMagic)^[2], @c1, 4)) or
(c1 <> GetCurrentProcessID xor FMagic) then begin
FMagic := GetCurrentProcessID xor Fs($30);
if (not TryRead(@TPACardinal(GetCurrentThreadID xor FMagic)^[14], @c1, 4)) or
(c1 <> GetCurrentProcessID xor FMagic) then
FMagic := 0;
end else
FMagic95 := true;
end;
result := FMagic;
end;
function Magic95 : boolean;
begin
if not FMagicReady then Magic;
result := FMagic95;
end;
// ***************************************************************
type
TTryRead = record
areaBegin : NativeUInt;
areaEnd : NativeUInt;
flags : byte;
end;
TDATryRead = array of TTryRead;
TPDATryRead = ^TDATryRead;
function StartTryRead : THandle;
var tr : TPDATryRead;
trc : integer;
mbi : TMemoryBasicInformation;
c1 : NativeUInt;
b1 : byte;
begin
New(tr);
SetLength(tr^, 64);
trc := 0;
c1 := 0;
while {$ifndef win64} (c1 < $80000000) and {$endif} (VirtualQuery(pointer(c1), mbi, sizeOf(mbi)) = sizeOf(mbi)) do begin
if (mbi.State = MEM_COMMIT) and (mbi.Protect and (PAGE_READONLY or PAGE_READWRITE or PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_WRITECOPY or PAGE_EXECUTE) <> 0) and (mbi.Protect and PAGE_GUARD = 0) then begin
if mbi.Protect and (PAGE_READONLY or PAGE_READWRITE or PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_WRITECOPY) = 0 then
b1 := 1 // PAGE_EXECUTE, in theory not readable, practically it's usually readable
else
b1 := 2; // true read access
end else
b1 := 0;
if (trc = 0) or (tr^[trc - 1].flags <> b1) then begin
if trc = Length(tr^) then
SetLength(tr^, trc * 3 div 2);
tr^[trc].areaBegin := c1;
tr^[trc].flags := b1;
inc(trc);
end;
inc(c1, mbi.RegionSize);
tr^[trc - 1].areaEnd := c1;
end;
SetLength(tr^, trc);
result := THandle(tr);
end;
procedure EndTryRead(tryRead: THandle);
begin
try
Dispose(TPDATryRead(tryRead));
except end;
end;
function CheckTryRead(tryRead: THandle; mem: pointer; len: integer) : byte;
var i1, i2, i3 : integer;
b1 : boolean;
begin
result := 0;
try
i3 := length(TPDATryRead(tryRead)^);
i1 := i3 div 2;
i2 := (i1 + 2) div 2;
b1 := false;
while i2 > 0 do begin
if NativeUInt(mem) < TPDATryRead(tryRead)^[i1].areaBegin then begin
dec(i1, i2);
if i1 < 0 then i1 := 0;
end else if NativeUInt(mem) + dword(len) <= TPDATryRead(tryRead)^[i1].areaEnd then begin
result := TPDATryRead(tryRead)^[i1].flags;
exit;
end else begin
inc(i1, i2);
if i1 >= i3 then i1 := i3 - 1;
end;
if b1 then break;
if i2 = 1 then b1 := true
else i2 := (i2 + 1) div 2;
end;
except end;
end;
function IsBadReadPtrEx(src: pointer; count: dword; tryRead: THandle = 0) : boolean;
var mbi : TMemoryBasicInformation;
begin
if tryRead <> 0 then
result := CheckTryRead(tryRead, src, count) = 2
else
result := (VirtualQuery(src, mbi, sizeOf(mbi)) <> sizeOf(mbi)) or (mbi.State <> MEM_COMMIT) or (mbi.Protect and (PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_READONLY or PAGE_READWRITE or PAGE_WRITECOPY) = 0) or (mbi.Protect and PAGE_GUARD <> 0) or
(NativeUInt(src) + count > NativeUInt(mbi.BaseAddress) + mbi.RegionSize);
end;
var FastPageExecute : boolean = true;
function TryRead(src, dst: pointer; count: dword; tryRead: THandle = 0) : boolean;
var b1 : byte;
c1 : dword;
mbi : TMemoryBasicInformation;
begin
if tryRead <> 0 then
b1 := CheckTryRead(tryRead, src, count)
else
if (VirtualQuery(src, mbi, sizeOf(mbi)) = sizeOf(mbi)) and (mbi.State = MEM_COMMIT) and (mbi.Protect and (PAGE_READONLY or PAGE_READWRITE or PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_WRITECOPY or PAGE_EXECUTE) <> 0) and (mbi.Protect and PAGE_GUARD = 0) then begin
if mbi.Protect and (PAGE_READONLY or PAGE_READWRITE or PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_WRITECOPY) = 0 then
b1 := 1
else
b1 := 2;
end else
b1 := 0;
result := b1 > 0;
if result then begin
if b1 = 1 then begin
// PAGE_EXECUTE needs special handling
if FastPageExecute then
try
// first we try to read it directly, cause that's much faster
Move(src^, dst^, count);
except
// ooops, we got an access violation, after all
// so from now on we will properly use VirtualProtect
FastPageExecute := false;
end;
if not FastPageExecute then begin
result := VirtualProtect(src, count, PAGE_EXECUTE_READ, @c1);
if result then begin
Move(src^, dst^, count);
VirtualProtect(src, count, c1, @c1);
end;
end;
end else
Move(src^, dst^, count);
end;
end;
function TryWrite(src, dst: pointer; count: dword) : boolean;
var mbi : TMemoryBasicInformation;
begin
result := (VirtualQuery(dst, mbi, sizeOf(mbi)) = sizeOf(mbi)) and (mbi.State = MEM_COMMIT) and (mbi.Protect and (PAGE_EXECUTE_READWRITE or PAGE_EXECUTE_WRITECOPY or PAGE_READWRITE or PAGE_WRITECOPY) <> 0) and (mbi.Protect and PAGE_GUARD = 0) and
(NativeUInt(dst) + count <= NativeUInt(mbi.BaseAddress) + mbi.RegionSize);
if result then
Move(src^, dst^, count);
end;
// ***************************************************************
end.