[OFF THE GRID] Inlined "AppendString" not matching existing signature #419

Open
opened 2025-10-09 16:54:33 +02:00 by cheatdeveloper73 · 4 comments
cheatdeveloper73 commented 2025-10-09 16:54:33 +02:00 (Migrated from github.com)

The default signature for an inlined AppendString was invalid for this game and needed minor modifications. I located the target function using the string xref: "ForwardShadingQuality_" and found the call to FName::AppendString alongside the "GetNameEntryFromName" call. I constructed a signature and fixed the resolve relative offsets to match the new signature. It leads directly to the "GetNameEntryFromName" call which solved the issue I had with dumping, and got a successful dump after implementation.

	// Test if AppendString was inlined (modified for OFF THE GRID)
	if ((!AppendString || bFoundPotentiallyOverlappingSig) && !bForceGNames)
	{
                // I left the original comments here, these are no longer accurate to what we're doing in this function.
		/*
		* 0x00: 8B ? ?          mov     ecx, [...]
		* 0x03: E8 ? ? ? ?      call    FName::GetComparisonNameEntry
		* 0x08: 48 8D ? ?       lea     rdx, [...]
		* 0x0B: 48 8B C8        mov     rcx, rax
		* 0x10: E8 ? ? ? ?      call    FNameEntry::GetName
		*/
		if (void* SigScanResult = Platform::FindPatternInRange("E8 ? ? ? ? 48 8D ? ? 48 8B C8 E8 ? ? ? ?", StringRef, 0x180))
		{
			const uintptr_t ResultAsInt = reinterpret_cast<const uintptr_t>(SigScanResult);

			// Resolve the first call (GetNameEntryFromName) which is at the beginning of the signature match.
			GetNameEntryFromName = reinterpret_cast<decltype(GetNameEntryFromName)>(Architecture_x86_64::Resolve32BitRelativeCall(ResultAsInt + 0x0));

			// The second call (AppendString) is at offset 0xC (12 bytes) from the start of the signature.
			// [call] = 5 bytes, [lea] = 4 bytes, [mov] = 3 bytes. 5 + 4 + 3 = 12.
			AppendString = reinterpret_cast<decltype(AppendString)>(Architecture_x86_64::Resolve32BitRelativeCall(ResultAsInt + 0xC));

			Off::InSDK::Name::GetNameEntryFromName = Platform::GetOffset(GetNameEntryFromName);
			Off::InSDK::Name::bIsAppendStringInlinedAndUsed = true;

			ToStr = [](const void* Name) -> std::wstring
			{
				thread_local FFreableString TempString(1024);

				AppendString(GetNameEntryFromName(FName(Name).GetCompIdx()), TempString);

				std::wstring OutputString = TempString.ToWString();
				TempString.ResetNum();

				const uint32 Number = FName(Name).GetNumber();

				if (Number > 0)
					return OutputString + L'_' + std::to_wstring(Number - 1);

				return OutputString;
			};
		}
	}
The default signature for an inlined AppendString was invalid for this game and needed minor modifications. I located the target function using the string xref: "ForwardShadingQuality_" and found the call to FName::AppendString alongside the "GetNameEntryFromName" call. I constructed a signature and fixed the resolve relative offsets to match the new signature. It leads directly to the "GetNameEntryFromName" call which solved the issue I had with dumping, and got a successful dump after implementation. ```c++ // Test if AppendString was inlined (modified for OFF THE GRID) if ((!AppendString || bFoundPotentiallyOverlappingSig) && !bForceGNames) { // I left the original comments here, these are no longer accurate to what we're doing in this function. /* * 0x00: 8B ? ? mov ecx, [...] * 0x03: E8 ? ? ? ? call FName::GetComparisonNameEntry * 0x08: 48 8D ? ? lea rdx, [...] * 0x0B: 48 8B C8 mov rcx, rax * 0x10: E8 ? ? ? ? call FNameEntry::GetName */ if (void* SigScanResult = Platform::FindPatternInRange("E8 ? ? ? ? 48 8D ? ? 48 8B C8 E8 ? ? ? ?", StringRef, 0x180)) { const uintptr_t ResultAsInt = reinterpret_cast<const uintptr_t>(SigScanResult); // Resolve the first call (GetNameEntryFromName) which is at the beginning of the signature match. GetNameEntryFromName = reinterpret_cast<decltype(GetNameEntryFromName)>(Architecture_x86_64::Resolve32BitRelativeCall(ResultAsInt + 0x0)); // The second call (AppendString) is at offset 0xC (12 bytes) from the start of the signature. // [call] = 5 bytes, [lea] = 4 bytes, [mov] = 3 bytes. 5 + 4 + 3 = 12. AppendString = reinterpret_cast<decltype(AppendString)>(Architecture_x86_64::Resolve32BitRelativeCall(ResultAsInt + 0xC)); Off::InSDK::Name::GetNameEntryFromName = Platform::GetOffset(GetNameEntryFromName); Off::InSDK::Name::bIsAppendStringInlinedAndUsed = true; ToStr = [](const void* Name) -> std::wstring { thread_local FFreableString TempString(1024); AppendString(GetNameEntryFromName(FName(Name).GetCompIdx()), TempString); std::wstring OutputString = TempString.ToWString(); TempString.ResetNum(); const uint32 Number = FName(Name).GetNumber(); if (Number > 0) return OutputString + L'_' + std::to_wstring(Number - 1); return OutputString; }; } } ```
Fischsalat commented 2025-10-09 22:28:06 +02:00 (Migrated from github.com)

Hi, could you probably provide a git diff of what you changed? A screenshot of the bytes around the ForwardShadingQuality_ ref would also be great. Thank you!

Hi, could you probably provide a git diff of what you changed? A screenshot of the bytes around the ForwardShadingQuality_ ref would also be great. Thank you!
cheatdeveloper73 commented 2025-10-10 00:26:47 +02:00 (Migrated from github.com)
-	// Test if AppendString was inlined
+	// Test if AppendString was inlined (modified for OFF THE GRID)
	if ((!AppendString || bFoundPotentiallyOverlappingSig) && !bForceGNames)
	{
		/*
		* 0x00: 8B ? ?          mov     ecx, [...]
		* 0x03: E8 ? ? ? ?      call    FName::GetComparisonNameEntry
		* 0x08: 48 8D ? ?       lea     rdx, [...]
		* 0x0B: 48 8B C8        mov     rcx, rax
		* 0x10: E8 ? ? ? ?      call    FNameEntry::GetName
		*/
-		if (void* SigScanResult = Platform::FindPatternInRange("8B ? ? E8 ? ? ? ? 48 8D ? ? ? 48 8B C8 E8 ? ? ? ?", StringRef, 0x180))
+		if (void* SigScanResult = Platform::FindPatternInRange("E8 ? ? ? ? 48 8D ? ? 48 8B C8 E8 ? ? ? ?", StringRef, 0x180))
		{
			const uintptr_t ResultAsInt = reinterpret_cast<const uintptr_t>(SigScanResult);

-			GetNameEntryFromName = reinterpret_cast<decltype(GetNameEntryFromName)>(Architecture_x86_64::Resolve32BitRelativeCall(ResultAsInt + 0x3));
-			AppendString = reinterpret_cast<decltype(AppendString)>(Architecture_x86_64::Resolve32BitRelativeCall(ResultAsInt + 0x10));
+			// Resolve the first call (GetNameEntryFromName) which is at the beginning of the signature match.
+			GetNameEntryFromName = reinterpret_cast<decltype(GetNameEntryFromName)>(Architecture_x86_64::Resolve32BitRelativeCall(ResultAsInt + 0x0));
+
+			// The second call (AppendString) is at offset 0xC (12 bytes) from the start of the signature.
+			// [call] = 5 bytes, [lea] = 4 bytes, [mov] = 3 bytes. 5 + 4 + 3 = 12.
+			AppendString = reinterpret_cast<decltype(AppendString)>(Architecture_x86_64::Resolve32BitRelativeCall(ResultAsInt + 0xC));

			Off::InSDK::Name::GetNameEntryFromName = Platform::GetOffset(GetNameEntryFromName);
			Off::InSDK::Name::bIsAppendStringInlinedAndUsed = true;

			ToStr = [](const void* Name) -> std::wstring
			{
				thread_local FFreableString TempString(1024);

				AppendString(GetNameEntryFromName(FName(Name).GetCompIdx()), TempString);

				std::wstring OutputString = TempString.ToWString();
				TempString.ResetNum();

				const uint32 Number = FName(Name).GetNumber();

				if (Number > 0)
					return OutputString + L'_' + std::to_wstring(Number - 1);

				return OutputString;
			};
		}
	}
.text:00000001431FE992 loc_1431FE992:                          ; CODE XREF: ForwardShadingQualityXREF+5E↑j
.text:00000001431FE992                                         ; ForwardShadingQualityXREF+82↑j ...
.text:00000001431FE992 ; __unwind { // __GSHandlerCheck
.text:00000001431FE992                 mov     [rsp+100h+arg_10], rbx
.text:00000001431FE99A                 lea     rdx, aForwardshading ; "ForwardShadingQuality_"
.text:00000001431FE9A1                 mov     [rsp+100h+arg_18], rsi
.text:00000001431FE9A9                 lea     rcx, [rbp+57h+var_D0]
.text:00000001431FE9A9 ; } // starts at 1431FE992
.text:00000001431FE9AD
.text:00000001431FE9AD loc_1431FE9AD:                          ; DATA XREF: .rdata:000000014F5664F8↓o
.text:00000001431FE9AD                                         ; .rdata:000000014F566508↓o ...
.text:00000001431FE9AD ; __unwind { // __GSHandlerCheck
.text:00000001431FE9AD                 mov     [rsp+100h+var_20], r15
.text:00000001431FE9B5                 call    FName__NameToEntry
.text:00000001431FE9BA                 mov     ecx, dword ptr [rbp+57h+arg_8]
.text:00000001431FE9BD                 call    sub_141F21870
.text:00000001431FE9C2                 lea     rdx, [rbp+57h+var_D0]
.text:00000001431FE9C6                 mov     rcx, rax
.text:00000001431FE9C9                 call    FName__AppendString
.text:00000001431FE9CE                 cmp     dword ptr [rbp+57h+arg_8+4], r13d
.text:00000001431FE9D2                 jz      short loc_1431FEA2C
.text:00000001431FE9D4                 mov     rax, [rbp+57h+var_C8]
.text:00000001431FE9D8                 lea     ebx, [rax-1]
.text:00000001431FE9DB                 test    eax, eax
.text:00000001431FE9DD                 jg      short loc_1431FE9E2
.text:00000001431FE9DF                 mov     ebx, r13d
```diff - // Test if AppendString was inlined + // Test if AppendString was inlined (modified for OFF THE GRID) if ((!AppendString || bFoundPotentiallyOverlappingSig) && !bForceGNames) { /* * 0x00: 8B ? ?          mov     ecx, [...] * 0x03: E8 ? ? ? ?      call    FName::GetComparisonNameEntry * 0x08: 48 8D ? ?       lea     rdx, [...] * 0x0B: 48 8B C8        mov     rcx, rax * 0x10: E8 ? ? ? ?      call    FNameEntry::GetName */ - if (void* SigScanResult = Platform::FindPatternInRange("8B ? ? E8 ? ? ? ? 48 8D ? ? ? 48 8B C8 E8 ? ? ? ?", StringRef, 0x180)) + if (void* SigScanResult = Platform::FindPatternInRange("E8 ? ? ? ? 48 8D ? ? 48 8B C8 E8 ? ? ? ?", StringRef, 0x180)) { const uintptr_t ResultAsInt = reinterpret_cast<const uintptr_t>(SigScanResult); - GetNameEntryFromName = reinterpret_cast<decltype(GetNameEntryFromName)>(Architecture_x86_64::Resolve32BitRelativeCall(ResultAsInt + 0x3)); - AppendString = reinterpret_cast<decltype(AppendString)>(Architecture_x86_64::Resolve32BitRelativeCall(ResultAsInt + 0x10)); + // Resolve the first call (GetNameEntryFromName) which is at the beginning of the signature match. + GetNameEntryFromName = reinterpret_cast<decltype(GetNameEntryFromName)>(Architecture_x86_64::Resolve32BitRelativeCall(ResultAsInt + 0x0)); + + // The second call (AppendString) is at offset 0xC (12 bytes) from the start of the signature. + // [call] = 5 bytes, [lea] = 4 bytes, [mov] = 3 bytes. 5 + 4 + 3 = 12. + AppendString = reinterpret_cast<decltype(AppendString)>(Architecture_x86_64::Resolve32BitRelativeCall(ResultAsInt + 0xC)); Off::InSDK::Name::GetNameEntryFromName = Platform::GetOffset(GetNameEntryFromName); Off::InSDK::Name::bIsAppendStringInlinedAndUsed = true; ToStr = [](const void* Name) -> std::wstring { thread_local FFreableString TempString(1024); AppendString(GetNameEntryFromName(FName(Name).GetCompIdx()), TempString); std::wstring OutputString = TempString.ToWString(); TempString.ResetNum(); const uint32 Number = FName(Name).GetNumber(); if (Number > 0) return OutputString + L'_' + std::to_wstring(Number - 1); return OutputString; }; } } ``` ```asm .text:00000001431FE992 loc_1431FE992: ; CODE XREF: ForwardShadingQualityXREF+5E↑j .text:00000001431FE992 ; ForwardShadingQualityXREF+82↑j ... .text:00000001431FE992 ; __unwind { // __GSHandlerCheck .text:00000001431FE992 mov [rsp+100h+arg_10], rbx .text:00000001431FE99A lea rdx, aForwardshading ; "ForwardShadingQuality_" .text:00000001431FE9A1 mov [rsp+100h+arg_18], rsi .text:00000001431FE9A9 lea rcx, [rbp+57h+var_D0] .text:00000001431FE9A9 ; } // starts at 1431FE992 .text:00000001431FE9AD .text:00000001431FE9AD loc_1431FE9AD: ; DATA XREF: .rdata:000000014F5664F8↓o .text:00000001431FE9AD ; .rdata:000000014F566508↓o ... .text:00000001431FE9AD ; __unwind { // __GSHandlerCheck .text:00000001431FE9AD mov [rsp+100h+var_20], r15 .text:00000001431FE9B5 call FName__NameToEntry .text:00000001431FE9BA mov ecx, dword ptr [rbp+57h+arg_8] .text:00000001431FE9BD call sub_141F21870 .text:00000001431FE9C2 lea rdx, [rbp+57h+var_D0] .text:00000001431FE9C6 mov rcx, rax .text:00000001431FE9C9 call FName__AppendString .text:00000001431FE9CE cmp dword ptr [rbp+57h+arg_8+4], r13d .text:00000001431FE9D2 jz short loc_1431FEA2C .text:00000001431FE9D4 mov rax, [rbp+57h+var_C8] .text:00000001431FE9D8 lea ebx, [rax-1] .text:00000001431FE9DB test eax, eax .text:00000001431FE9DD jg short loc_1431FE9E2 .text:00000001431FE9DF mov ebx, r13d ```
Fischsalat commented 2025-10-10 17:02:03 +02:00 (Migrated from github.com)

I'm sorry, I forgot to mention that you should turn on the option to show opcode bytes, since I have it on by default. You can toggle it on by setting Options>General>Number of opcode bytes (non-graph) to 10.

I'm sorry, I forgot to mention that you should turn on the option to show opcode bytes, since I have it on by default. You can toggle it on by setting `Options>General>Number of opcode bytes (non-graph)` to 10.
cheatdeveloper73 commented 2025-10-10 20:45:11 +02:00 (Migrated from github.com)
.text:00000001431FE992 48 89 9C 24 20 01 00 00                       mov     [rsp+100h+arg_10], rbx
.text:00000001431FE99A 48 8D 15 B7 94 00 0A                          lea     rdx, aForwardshading ; "ForwardShadingQuality_"
.text:00000001431FE9A1 48 89 B4 24 28 01 00 00                       mov     [rsp+100h+arg_18], rsi
.text:00000001431FE9A9 48 8D 4D 87                                   lea     rcx, [rbp+57h+var_D0]
.text:00000001431FE9A9                               ; } // starts at 1431FE992
.text:00000001431FE9AD
.text:00000001431FE9AD                               loc_1431FE9AD:                          ; DATA XREF: .rdata:000000014F5664F8↓o
.text:00000001431FE9AD                                                                       ; .rdata:000000014F566508↓o ...
.text:00000001431FE9AD                               ; __unwind { // __GSHandlerCheck
.text:00000001431FE9AD 4C 89 BC 24 E0 00 00 00                       mov     [rsp+100h+var_20], r15
.text:00000001431FE9B5 E8 76 87 B3 FE                                call    FName__NameToEntry
.text:00000001431FE9BA 8B 4D 6F                                      mov     ecx, dword ptr [rbp+57h+arg_8]
.text:00000001431FE9BD E8 AE 2E D2 FE                                call    sub_141F21870
.text:00000001431FE9C2 48 8D 55 87                                   lea     rdx, [rbp+57h+var_D0]
.text:00000001431FE9C6 48 8B C8                                      mov     rcx, rax
.text:00000001431FE9C9 E8 12 20 D2 FE                                call    FName__AppendString
.text:00000001431FE9CE 44 39 6D 73                                   cmp     dword ptr [rbp+57h+arg_8+4], r13d
.text:00000001431FE9D2 74 58                                         jz      short loc_1431FEA2C
.text:00000001431FE9D4 48 8B 45 8F                                   mov     rax, [rbp+57h+var_C8]
.text:00000001431FE9D8 8D 58 FF                                      lea     ebx, [rax-1]
.text:00000001431FE9DB 85 C0                                         test    eax, eax
.text:00000001431FE9DD 7F 03                                         jg      short loc_1431FE9E2
.text:00000001431FE9DF 41 8B DD                                      mov     ebx, r13d
```asm .text:00000001431FE992 48 89 9C 24 20 01 00 00 mov [rsp+100h+arg_10], rbx .text:00000001431FE99A 48 8D 15 B7 94 00 0A lea rdx, aForwardshading ; "ForwardShadingQuality_" .text:00000001431FE9A1 48 89 B4 24 28 01 00 00 mov [rsp+100h+arg_18], rsi .text:00000001431FE9A9 48 8D 4D 87 lea rcx, [rbp+57h+var_D0] .text:00000001431FE9A9 ; } // starts at 1431FE992 .text:00000001431FE9AD .text:00000001431FE9AD loc_1431FE9AD: ; DATA XREF: .rdata:000000014F5664F8↓o .text:00000001431FE9AD ; .rdata:000000014F566508↓o ... .text:00000001431FE9AD ; __unwind { // __GSHandlerCheck .text:00000001431FE9AD 4C 89 BC 24 E0 00 00 00 mov [rsp+100h+var_20], r15 .text:00000001431FE9B5 E8 76 87 B3 FE call FName__NameToEntry .text:00000001431FE9BA 8B 4D 6F mov ecx, dword ptr [rbp+57h+arg_8] .text:00000001431FE9BD E8 AE 2E D2 FE call sub_141F21870 .text:00000001431FE9C2 48 8D 55 87 lea rdx, [rbp+57h+var_D0] .text:00000001431FE9C6 48 8B C8 mov rcx, rax .text:00000001431FE9C9 E8 12 20 D2 FE call FName__AppendString .text:00000001431FE9CE 44 39 6D 73 cmp dword ptr [rbp+57h+arg_8+4], r13d .text:00000001431FE9D2 74 58 jz short loc_1431FEA2C .text:00000001431FE9D4 48 8B 45 8F mov rax, [rbp+57h+var_C8] .text:00000001431FE9D8 8D 58 FF lea ebx, [rax-1] .text:00000001431FE9DB 85 C0 test eax, eax .text:00000001431FE9DD 7F 03 jg short loc_1431FE9E2 .text:00000001431FE9DF 41 8B DD mov ebx, r13d ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
software-migration-backups/Dumper-7-17-05-2026#419
No description provided.