trumank / patternsleuth

Unreal Engine address scanner and test suite
37 stars 17 forks source link

UE 5.4 signatures for FNameToStringVoid and FTextFString #6

Closed igromanru closed 3 weeks ago

igromanru commented 3 weeks ago

Tested with game Abiotic Factor. FTextFString was also confirmed for Everspace 2

trumank commented 3 weeks ago

FTextFString looks good.

FNameToString (the root signature, FNameToStringVoid shouldn't be used directly) seems to be found in Abiotic Factor and all the other various 5.0-5.4 games I have. Is there a game the additional pattern is needed?

igromanru commented 3 weeks ago

No, FNameToStringVoid isn't needed. I just saw that it can't be found and wanted to fix it as well.
It points to the same function as FNameToString and FNameToStringFString. WindowsTerminal_qDLoMtzC0e

trumank commented 3 weeks ago

Your FNameToStringVoid pattern seems to be matching FNameToStringFString instead, and the added pattern does not find FNameToStringFString for any additional games.

e.g. the following patch results in no additional matches:

 pub struct FNameToStringFString(pub usize);
 impl_resolver_singleton!(all, FNameToStringFString, |ctx| async {
-    let patterns =
-        ["48 8b 48 ?? 48 89 4c 24 ?? 48 8d 4c 24 ?? e8 | ?? ?? ?? ?? 83 7c 24 ?? 00 48 8d"];
+    let patterns = [
+        "48 8b 48 ?? 48 89 4c 24 ?? 48 8d 4c 24 ?? e8 | ?? ?? ?? ?? 83 7c 24 ?? 00 48 8d",
+        "E8 | ?? ?? ?? ?? 33 D2 48 8B C8 48 8B D8 E8 ?? ?? ?? ?? 48 8B 2B", // UE5.4
+    ];
igromanru commented 3 weeks ago

Okey, do you want me to get more UE 5.4 games and fix it or should I just remove it and keep FTextFString for the PR?

trumank commented 3 weeks ago

Just FTextFString would be great. If you have another game FNameToString fails on then that can be done in another PR.

igromanru commented 3 weeks ago

From my understanding all 3 FNameToString functions can point to the function FName::ToString(FString&), this is exactly what IDA shows me in Abiotic Factor with pdb.
Shouldn't it be the same FNameToStringFString pattern for all 3? Or wasn't it the case for some previous Engine versions?

trumank commented 3 weeks ago

The details are fuzzy since I haven't looked at in a while, but the FNameToString resolver can return an address for two possible function signatures that behave almost the same (see https://github.com/trumank/patternsleuth/blob/2ce944a1b63030a6245765a03f54faa3cb09fb5f/patternsleuth/src/resolvers/unreal/fname.rs#L191-L198). The sub-resolvers should return only functions that match the function signature exactly.

igromanru commented 3 weeks ago

Your FNameToStringVoid pattern seems to be matching FNameToStringFString instead, and the added pattern does not find FNameToStringFString for any additional games.

e.g. the following patch results in no additional matches:

 pub struct FNameToStringFString(pub usize);
 impl_resolver_singleton!(all, FNameToStringFString, |ctx| async {
-    let patterns =
-        ["48 8b 48 ?? 48 89 4c 24 ?? 48 8d 4c 24 ?? e8 | ?? ?? ?? ?? 83 7c 24 ?? 00 48 8d"];
+    let patterns = [
+        "48 8b 48 ?? 48 89 4c 24 ?? 48 8d 4c 24 ?? e8 | ?? ?? ?? ?? 83 7c 24 ?? 00 48 8d",
+        "E8 | ?? ?? ?? ?? 33 D2 48 8B C8 48 8B D8 E8 ?? ?? ?? ?? 48 8B 2B", // UE5.4
+    ];

The previous pattern "48 8b 48 ?? 48 89 4c 24 ?? 48 8d 4c 24 ?? e8 | ?? ?? ?? ?? 83 7c 24 ?? 00 48 8d" finds the FNameToStringVoid instead of FNameToStringFString in Abiotic Factor. I've found a pattern that leads directly to FNameToStringFString -> "E8 | ?? ?? ?? ?? 49 8B 07 8B 48" What is the proper way to deal with it? WindowsTerminal_bmebG90P22

EDIT: Also I don't get why the tool doesn't find this signature: "48 0F 45 5C 24 ?? 48 8D 4C 24 ?? E8 | ?? ?? ?? ?? 83 7C 24 ?? ?? 48 8D 15"
It's a better pattern for FNameToStringFString and I can find it in IDA (without |), but patternsleuth can't find it.

trumank commented 3 weeks ago

The resolver and demangled names are admittedly very confusing.

resolver names: FNameToStringFString: public: void __cdecl FName::ToString(class FString &) const FNameToStringVoid: public: class FString __cdecl FName::ToString(void) const

// Abiotic Factor addresses
1410baf70  void FName::ToString(class FName const* this, class FString* Out)
1410bacf0  class FString* FName::ToString(class FName const* this, class FString* __return)

image image

I see now the FNameToStringVoid resolver is incorrectly returning the address for FNameToStringFString for Abiotic Factor. I'm not sure why this is. I would have to check some older versions of UE to see when/if it started matching different functions. As far as I can tell it's at least returning functionally equivalent addresses so I will hold off on merging FNameToString things until I fully understand what's going on.

igromanru commented 3 weeks ago

Alright, I've reverted the FName signatures changes. If needed you can still find them in commits. Unless you want me to squash all my commits and force push them.

Here are the sigs once again: FNameToStringFString: 48 0F 45 5C 24 ?? 48 8D 4C 24 ?? E8 | ?? ?? ?? ?? 83 7C 24 ?? ?? 48 8D 15 FNameToStringVoid: E8 | ?? ?? ?? ?? 4C 8B C0 4C 8D 4F ?? 49 8B D5 Both signatures look pretty good, work for both 5.4 I have here (Everspace 2 and Abiotic Factor) and are unique so they don't interfere with previous AOBs.

Here is my current test log from latest commit (without FNames):
test.txt

EDIT:

// Abiotic Factor addresses
1410baf70  void FName::ToString(class FName const* this, class FString* Out)
1410bacf0  class FString* FName::ToString(class FName const* this, class FString* __return)

This is how functions look like in Everspace 2 as well. Keep in mind it's just how IDA shows them as pseudocode. They still got the right function signature. ida64_vPOy63ujG9

trumank commented 3 weeks ago
# cargo run --release diff-report ...
+---------------------------------+-----------------------------------------+-----------------------------+
| FTextFString - 1361/1491 (91.28%) => 1366/1491 (91.62%): +0.34%                                         |
+=================================+=========================================+=============================+
| 504_BeerFactory                 | Err(Msg("expected at least one value")) | Ok(FTextFString(140fc3ef0)) |
+---------------------------------+-----------------------------------------+-----------------------------+
| 504_Enotria The Last Song       | Err(Msg("expected at least one value")) | Ok(FTextFString(140f826c0)) |
+---------------------------------+-----------------------------------------+-----------------------------+
| 504_MorseHorse6                 | Err(Msg("expected at least one value")) | Ok(FTextFString(140fc41c0)) |
+---------------------------------+-----------------------------------------+-----------------------------+
| 504_Seekers of Skyveil Playtest | Err(Msg("expected at least one value")) | Ok(FTextFString(1410207a0)) |
+---------------------------------+-----------------------------------------+-----------------------------+
| 504_THE JOY OF CREATION Demo    | Err(Msg("expected at least one value")) | Ok(FTextFString(140fa65b0)) |
+---------------------------------+-----------------------------------------+-----------------------------+

+-------------------+--------+--------+----------+---------+
| resolver          | a      | b      | increase | changed |
+===================+========+========+==========+=========+
| FTextFString      | 91.28% | 91.62% | +0.34%   | 0       |
+-------------------+--------+--------+----------+---------+