📄 dcprocinstr.pas
字号:
Succeeded(TAssignInstr(Items[I -1]).Target.QueryInterface(IRegSource, ARegTarget)) and
(ARegTarget.RegType = rtDWord) and
// The Target is set again and not used before it is set again
ReplCallback.IsRegSetAgain(ARegTarget.Reg, I, False) then
begin
// Replace Target of the first instr with the source, except by the True target of the second instr.
ASource := TAssignInstr(Items[I]).Source.Replace(
TAssignInstr(Items[I -1]).Target, TAssignInstr(Items[I -1]).Source);
if not Succeeded(TAssignInstr(Items[I]).Target.QueryInterface(IRegSource, ARegTarget)) then
ATarget := TAssignInstr(Items[I]).Target.Replace(
TAssignInstr(Items[I -1]).Target, TAssignInstr(Items[I -1]).Source)
else
ATarget := TAssignInstr(Items[I]).Target;
Instr := TAssignInstr.CreateAssign(Items[I -1].Address, Items[I].Size + Items[I -1].Size, ATarget, ASource);
TInstruction(FItems[I -1]).Free;
TInstruction(FItems[I]).Free;
FItems[I -1] := Instr;
FItems.Delete(I);
FChanged := True;
end;
end;
finally
ReplCallback.Free;
end;
end;
procedure TdcInstructions.OptimizeSources;
// Optimize all the sources.
var
ANewSource: IInstrSource;
ANewTarget: IInstrSource;
I: Integer;
Instr: TInstruction;
begin
for I := 0 to Count -1 do
if (Items[I] is TAssignInstr) then
begin
ANewSource := TAssignInstr(Items[I]).Source.Optimize;
ANewTarget := TAssignInstr(Items[I]).Target.Optimize;
if not (ANewSource.Compare(TAssignInstr(Items[I]).Source) and
ANewTarget.Compare(TAssignInstr(Items[I]).Target)) then
begin
FChanged := True;
Instr := TAssignInstr.CreateAssign(Items[I].Address, Items[I].Size, ANewTarget, ANewSource);
Items[I].Free;
FItems[I] := Instr;
end;
end;
end;
function TdcInstructions.GetAddress: TAddress;
begin
Result := FProc.Address;
end;
function TdcInstructions.GetSize: Integer;
begin
Result := TProc(FProc).ProcSize;
end;
function TdcInstructions.GetCount: Integer;
begin
Result := FItems.Count;
end;
function TdcInstructions.GetItem(Index: Integer): TInstruction;
begin
Result := TInstruction(FItems[Index]);
end;
function TdcInstructions.GetItemIndex(Item: TInstruction): Integer;
resourcestring
EItemNotFound = 'Item not found in instruction list.';
begin
Result := FItems.IndexOf(Item);
if Result = -1 then
raise EDecompilerError.Create(EItemNotFound);
end;
function TdcInstructions.GetText: TStrings;
begin
Result := FText;
if Result = nil then
begin
FText := TStringList.Create;
Result := FText;
// Get the instructions in pascal, or if failed in Assembler.
if not InstrToPascal(Result) then
GetAsmInstr(TProc(Proc), Result);
end;
end;
type
TUsedSourceCallback = class(TObject)
private
Used: set of TRegister;
procedure TargetCallback(Source: IInstrSource);
procedure SourceCallback(Source: IInstrSource);
end;
procedure TUsedSourceCallback.TargetCallback(Source: IInstrSource);
var
RegSource: IRegSource;
begin
// Is is a a reg source and the register is not already used, mark it as used.`
if Succeeded(Source.QueryInterface(IRegSource, RegSource)) then
Used := Used + [RegSource.Reg];
end;
procedure TUsedSourceCallback.SourceCallback(Source: IInstrSource);
var
RegSource: IRegSource;
begin
// Is is a a reg source and the register is not already used, mark it
// as used before write and used.
if Succeeded(Source.QueryInterface(IRegSource, RegSource)) then
Used := Used + [RegSource.Reg];
end;
type
TRegNames = array[TRegister] of string;
TPascalCallback = class(TObject)
private
PascalRegNames: TRegNames;
PascalProc: TProc;
procedure Callback(Source: IInstrSource; var AsPascal: string);
end;
procedure TPascalCallback.Callback(Source: IInstrSource; var AsPascal: string);
var
RegSource: IRegSource;
AddressSource: IAddressSource;
resourcestring
ERegWithoutAName = 'Reg doesn''t have a name (Bug in code)';
EFixupConstError = 'A Const value which is a fixup. Not yet supported.';
begin
// If it is a RegSource replace the AsPascal with the RegName
if Succeeded(Source.QueryInterface(IRegSource, RegSource)) then
begin
AsPascal := PascalRegNames[RegSource.Reg];
if AsPascal = '' then
raise EDecompilerError.Create(ERegWithoutAName);
end;
if Succeeded(Source.QueryInterface(IAddressSource, AddressSource)) then
raise EdcInstructionError.Create(EFixupConstError);
end;
function TdcInstructions.InstrToPascal(Strings: TStrings): Boolean;
const
ProcDef: array[TProcType, Boolean] of string =
(('procedure %s(%s);', 'function %s(%s): %s;'), ('class procedure %s.%s(%s);', 'class function %s.%s(%s): %s;'),
('procedure %s.%s(%s);', 'function %s.%s(%s): %s;'), ('constructor %s.%s(%s);', 'constructor %s.%s(%s);'),
('destructor %s.%s(%s);', 'destructor %s.%s(%s);'), ('', ''), ('', ''), ('', ''));
var
I: Integer;
UsedSourceCallback: TUsedSourceCallback;
Vars: set of TRegister;
Params: set of TRegister;
RegNames: TRegNames;
Reg: TRegister;
RegSource: IRegSource;
StackParams: Integer;
ParamsStr: string;
resourcestring
ENot4MultiplyRet = 'Stack is not popup by a multiply of 4.';
begin
Result := False;
try
// Must be a Strings object passed.
if Strings = nil then
Exit;
// The proc must be only assign instructions.
for I := 0 to Count -2 do
if not (Items[I] is TAssignInstr) then
Exit;
// Except the last one which must be a ret instruction.
if not ((Count > 0) and (Items[count -1] is TRetInstr)) then
Exit;
// The proc may not already have parameters or a function result,
// The proc may also not be a initialization or finalization method.
// The proc must also be a static proc.
with TProc(Proc) do
if (Parameters.Parameters <> '') or (Parameters.FuncResult <> '') or
(ProcType in [ptInitialization, ptFinalization]) or
(MethodBindingType <> mbtStatic) then
Exit;
// Generate a list of register which is read before they are set.
UsedSourceCallback := TUsedSourceCallback.Create;
try
Params := [];
for I := 0 to Count -2 do
begin
TAssignInstr(Items[I]).Source.GetSources(UsedSourceCallback.SourceCallback);
if Succeeded(TAssignInstr(Items[I]).Target.QueryInterface(IRegSource, RegSource)) and
(not (RegSource.Reg in UsedSourceCallback.Used)) then
Params := Params + [RegSource.Reg];
TAssignInstr(Items[I]).Target.GetSources(UsedSourceCallback.TargetCallback);
end;
Vars := UsedSourceCallback.Used;
finally
UsedSourceCallback.Free;
end;
// If this list contains other registers then eax, edx and ecx, we don't do this,
// because there is now way to get access to these registers except assembler.
if (Params >= [rEax, rEdx, rEcx]) or (Vars >= [rEax, rEdx, rEcx]) then
Exit;
// Include also the register for the previous Parameter.
if rEcx in Params then
Params := Params + [rEax, rEcx]
else if rEdx in Params then
Params := Params + [rEax];
// If eax is totally not used, it might simply be re return value.
if not ((rEax in Params) and (rEax in Vars)) then
Params := Params + [rEax];
// Ret AA , AA must be a multiply of 4.
if TRetInstr(Items[Count -1]).PopSize mod 4 <> 0 then
raise EdcInstructionError.Create(ENot4MultiplyRet);
// Stack Params is AA / 4 (Ret AA).
StackParams := TRetInstr(Items[Count -1]).PopSize div 4;
if StackParams > 0 then
begin
// If there are stack parameters and others, all other must be used.
if Params <> [] then
Params := Params + [rEax, rEdx, rEcx];
end;
Vars := Vars - Params;
// Generate names for the registers.
for Reg := Low(TRegister) to High(TRegister) do
begin
if Reg in Params then
RegNames[Reg] := 'Param' + GetEnumName(TypeInfo(TRegister), Integer(Reg));
if Reg in Vars then
RegNames[Reg] := 'Var' + GetEnumName(TypeInfo(TRegister), Integer(Reg));
end;
// There must be return value.
TProc(Proc).Parameters.FuncResult := 'Integer';
// If eax is a var name it result.
if rEax in Vars then
RegNames[rEax] := 'Result';
// Set the parameters.
if rEcx in Params then
ParamsStr := 'ParamrEax, ParamrEdx, ParamrEcx'
else if rEdx in Params then
ParamsStr := 'ParamrEax, ParamrEdx'
else if rEax in Params then
ParamsStr := 'ParamrEax';
for I := 0 to StackParams -1 do
begin
if ParamsStr = '' then
ParamsStr := 'StackParam' + IntToStr(I)
else
ParamsStr := ', StackParam' + IntToStr(I);
end;
if ParamsStr <> '' then
ParamsStr := ParamsStr + ': Integer';
// Create the proc header
with TProc(Proc) do
if ProcType in ptMethods then
Strings.Add(Format(ProcDef[ProcType, Parameters.FuncResult <> ''],
[AClass.AClass.ClassName, Name, Parameters.Parameters, Parameters.FuncResult]))
else
Strings.Add(Format(ProcDef[ProcType, Parameters.FuncResult <> ''], [Name, Parameters.Parameters, Parameters.FuncResult]));
// create the vars.
if Vars <> [] then
begin
Strings.Add('var');
for Reg := Low(TRegister) to High(TRegister) do
if Reg in Vars then
Strings.Add(Format(' Var%s: Integer;', [GetEnumName(TypeInfo(TRegister), Integer(Reg))]));
end;
// Generate the real coce.
Strings.Add('begin');
with TPascalCallback.Create do
try
PascalRegNames := RegNames;
PascalProc := TProc(Proc);
// Decompile the assignment instructions.
for I := 0 to Count -2 do
Strings.Add(Format(' %s := %s;', [
TAssignInstr(Items[I]).Target.AsPascal(Callback),
TAssignInstr(Items[I]).Source.AsPascal(Callback)]));
finally
Free;
end;
// If eax is not already result, set result to eax.
if RegNames[rEax] <> 'Result' then
Strings.Add(Format(' Result := %s;', [RegNames[rEax]]));
Strings.Add('end;');
Result := True;
except
// Swallow EdcInstructionErrors and EDisAsmErrors, There is just no code generated.
on EdcInstructionError do Strings.Clear;
on EDisAsmError do Strings.Clear;
end;
end;
procedure TdcInstructions.AddInstr(Instr: TInstruction);
begin
FItems.Add(Instr);
FChanged := True;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -