Hallo liebe Praxis.
Der Titel ließ mir leider nicht sehr viel Platz für mein 'Problem'.
Folgende Situation:
Ich injecte meine
DLL in einen 64 Bit Prozess, der regelmäßig Updates bekommt. Vorweg kann ich sagen, dass es sich hier um kein Multiplayer-Spiel o.Ä. handelt (ehrlich
).
Für den Fall das ich mal zu faul sein sollte, alle meine Offsets an den neuen Build anzupassen, habe ich zunächst eine Liste erstellt. In dieser Liste setze ich alle Funktions-Offsets, die ich in der Zielanwendung aufrufen möchte.
Delphi-Quellcode:
procedure TGlobals.AddOffset(OffsetIdent: TOffset; Offset: NativeUInt; Build: Integer);
var
OffsetRec: TOffsetRec;
begin
OffsetRec.OffsetName := OffsetIdent;
OffsetRec.OffsetBuild := Build;
OffsetRec.Offset := FExeModule + Offset;
FOffsets.Add(OffsetRec);
if (Build <> FAppBuild) and (Assigned(PipeClient)) then
PipeClient.SendMsgToLoader('Offset %s is not compatible with current build', [GetEnumName(TypeInfo(TOffset), Ord(OffsetIdent))], true);
end;
Den Build lese ich direkt aus den Exe-Informationen aus. Sollte der Build nicht mit dem festgelegten Build in meiner
DLL übereinstimmen, setze ich das Offset der Anwendung auf eine "Null Funktion".
Delphi-Quellcode:
function TGlobals.GetOffset(OffsetIdent: TOffset; OffsetType: TOffsetType): Pointer;
var
i: Integer;
begin
for i := 0 to FOffsets.Count -1 do
begin
if (FOffsets[i].OffsetName = OffsetIdent) and (FOffsets[i].OffsetBuild = FAppBuild) then
exit(Pointer(FOffsets[i].Offset));
end;
case OffsetType of
OT_FUNCTION:
result := @NullOffsetFunction;
OT_DETOUR:
result := nil;
OT_POINTER:
result := @NullOffsetPointer
else
result := nil;
end;
end;
Gehört das gewünschte Offset also zu einer Funktion, dass Offset ist jedoch noch nicht geupdatet, setze ich den Pointer auf eine Funktion die einfach nichts tut. Möchte ich in der Zielanwendung z.B. etwas loggen und das Offset ist nicht up2date, wird NullOffsetFunction aufgerufen, ggf. mit Parametern.
Delphi-Quellcode:
function NullOffsetFunction(): Pointer;
begin
result := nil;
end;
Das könnte dann so aussehen.
Delphi-Quellcode:
var
ExternalAppDoSomething: procedure(const S: String);
procedure TGlobals.RegisterOffsets();
begin
@ExternalAppDoSomething := GetOffset(OFF_FUNC_DOSOMETHING, OT_FUNCTION);
end;
ExternalAppDoSomething würde jetzt irgendwo anders im Code aufgerufen, das Offset ist veraltet und GetOffset hat es auf NullOffsetFunction gesetzt. Bei jedem Aufruf wird jedoch ein Parameter übergeben, NullOffsetFunction hat keinen.
Delphi-Quellcode:
proecedure Pipapo();
begin
ExternalAppDoSomething('
hello from dll');
end;
-> NullOffsetFunction wird aufgerufen.
Mit dem Szenario möchte ich etliche try ... except vermeiden.
Wie gesagt, ist das ganze für x64. Eigentlich sollte es da kein Problem geben, da der Caller (unter x64) ja den Stack aufräumt und die Anzahl der übergebenen Parameter die nicht verarbeitet werden irrevant sein müsste, oder täusche ich mich da?
Über die anderen Probleme bin ich mir im Klaren, ein Aufruf erwartet "null" als Rückgabewert und NullOffsetFunction gibt ebenfalls null zurück, dies ist im gesamten Code aber bisher nicht von Relevanz Im Zweifelsfall könnte dann noch immer überprüft werden, ob das Offset auf NullOffsetFunction zeigt, bei anderen Aufrufen würde ich Checks aber gerne vermeiden.
Liebe Grüße