Probe route observer service lookup

This commit is contained in:
2026-06-20 01:21:14 -05:00
parent 22ad253fe7
commit ad4907040a
3 changed files with 66 additions and 1 deletions
+3 -1
View File
@@ -37,7 +37,9 @@ Current behavior:
`0x286a85c`. `0x286a85c`.
- Logs the route entry pointer and route object pointer passed to those - Logs the route entry pointer and route object pointer passed to those
observers, including route object vtable slots and common fields such as the observers, including route object vtable slots and common fields such as the
active byte at offset `0x84`. active byte at offset `0x84`, the service lookup key at offset `0x8c`, and
the runtime observer1 service-owner path used before its vtable `0x220`
lookup.
- Resolves the native mappin system when one of those paths fires, logs relevant - Resolves the native mappin system when one of those paths fires, logs relevant
vtable slot addresses, and temporarily hooks the route-adjacent slots. vtable slot addresses, and temporarily hooks the route-adjacent slots.
+28
View File
@@ -337,6 +337,34 @@ The installed probe now hooks those callbacks directly and logs the route entry,
route object, route object vtable slots, and the route object's active byte at route object, route object vtable slots, and the route object's active byte at
offset `0x84`. offset `0x84`.
The first observer-object run separated three route-object vtable families:
```text
vtable 0x142b31138: quest/objective route objects, key at +0x40
vtable 0x142af1510: quest/objective route objects, key at +0x40
vtable 0x142b30e20: custom/player pin route objects, key at +0x40
```
`RouteActivate` and `RouteDeactivate` really do flip the route object's active
byte at `+0x84`. The observer callbacks receive the same route entry after that
flip.
Static disassembly of observer0 (`0xaa6610 -> 0xaa6678`) shows it consuming
route-object virtuals at `+0x1a8`, `+0x188`, `+0x140`, and `+0x198`.
`+0x198` returns a route enum from a nested descriptor; `+0x188` returns a
descriptor payload pointer; and `+0x140` is a route-family-specific boolean.
That observer stores derived route state into buckets under the observer object,
choosing one bucket only when the enum is `0x8e`.
Static disassembly of observer1 (`0xaa6628`) is now the highest-value
downstream branch. It resolves a route object, then calls a generic system
getter with a runtime type/global at `.data` RVA `0x342f6a8`, calls the returned
service's vtable slot `0x220` with `routeObject + 0x8c`, and stores generated
handles/strings under its inner object at offsets around `+0x180`, `+0x1a0`,
`+0x1c0`, and `+0x1e0`. The current probe logs that service-owner path and
`routeObject + 0x8c` so the next runtime test can tell whether observer1 is
crossing into GPS/path computation or just updating presentation state.
The most route-building-looking static candidate currently in this chain is The most route-building-looking static candidate currently in this chain is
`0x5625a4`, called once from the bridge at `0x5984ec`. The bridge resolves `0x5625a4`, called once from the bridge at `0x5984ec`. The bridge resolves
journal route ids through virtual journal-manager calls, collects several route journal route ids through virtual journal-manager calls, collects several route
+35
View File
@@ -137,6 +137,7 @@ constexpr uintptr_t kRouteObserver23ActivateRva = 0x0AA63E0;
constexpr uintptr_t kRouteObserver0DeactivateRva = 0x27B10C0; constexpr uintptr_t kRouteObserver0DeactivateRva = 0x27B10C0;
constexpr uintptr_t kRouteObserver1DeactivateRva = 0x295D4A0; constexpr uintptr_t kRouteObserver1DeactivateRva = 0x295D4A0;
constexpr uintptr_t kRouteObserver23DeactivateRva = 0x286A85C; constexpr uintptr_t kRouteObserver23DeactivateRva = 0x286A85C;
constexpr uintptr_t kObserver1SystemTypeGlobalRva = 0x342F6A8;
HMODULE gModule = nullptr; HMODULE gModule = nullptr;
std::mutex gLogMutex; std::mutex gLogMutex;
@@ -677,12 +678,17 @@ void LogMappinRouteCollections(const char* aName, uint32_t aCount, void* aSystem
void LogRouteObserverContext(const char* aName, uint32_t aCount, void* aObserver, void* aRouteEntry) void LogRouteObserverContext(const char* aName, uint32_t aCount, void* aObserver, void* aRouteEntry)
{ {
auto* observerInner = ReadPointerField(aObserver, 0x08); auto* observerInner = ReadPointerField(aObserver, 0x08);
auto* observerInnerServiceOwner = ReadPointerField(observerInner, 0x18);
auto* observer1SystemType =
ReadPointerField(reinterpret_cast<void*>(GetImageBase() + kObserver1SystemTypeGlobalRva), 0x00);
auto* routeObject = ReadPointerField(aRouteEntry, 0x00); auto* routeObject = ReadPointerField(aRouteEntry, 0x00);
std::ostringstream line; std::ostringstream line;
line << "hook " << aName << " call=" << aCount; line << "hook " << aName << " call=" << aCount;
AppendPointerWithRva(line, "observer", aObserver); AppendPointerWithRva(line, "observer", aObserver);
AppendPointerWithRva(line, "observerInner", observerInner); AppendPointerWithRva(line, "observerInner", observerInner);
AppendPointerWithRva(line, "observerInnerServiceOwner", observerInnerServiceOwner);
AppendPointerWithRva(line, "observer1SystemType342f6a8", observer1SystemType);
AppendPointerWithRva(line, "routeEntry", aRouteEntry); AppendPointerWithRva(line, "routeEntry", aRouteEntry);
AppendPointerWithRva(line, "routeObject", routeObject); AppendPointerWithRva(line, "routeObject", routeObject);
AppendPointerField(line, "routeEntry_f08", aRouteEntry, 0x08); AppendPointerField(line, "routeEntry_f08", aRouteEntry, 0x08);
@@ -696,6 +702,31 @@ void LogRouteObserverContext(const char* aName, uint32_t aCount, void* aObserver
AppendObjectVtableSlots(observerLine, aObserver, {0x48, 0x50}); AppendObjectVtableSlots(observerLine, aObserver, {0x48, 0x50});
LogRed4ext(observerLine.str()); LogRed4ext(observerLine.str());
if (observerInner)
{
std::ostringstream innerLine;
innerLine << "route-observer-inner " << aName << " call=" << aCount;
AppendPointerWithRva(innerLine, "observerInner", observerInner);
AppendObjectVtableSlots(innerLine, observerInner, {0x08});
AppendPointerField(innerLine, "field18", observerInner, 0x18);
AppendPointerField(innerLine, "field180", observerInner, 0x180);
AppendPointerField(innerLine, "field1a0", observerInner, 0x1A0);
AppendPointerField(innerLine, "field1c0", observerInner, 0x1C0);
AppendPointerField(innerLine, "field1e0", observerInner, 0x1E0);
AppendPointerField(innerLine, "field1e8", observerInner, 0x1E8);
AppendPointerField(innerLine, "field1f0", observerInner, 0x1F0);
LogRed4ext(innerLine.str());
}
if (observerInnerServiceOwner)
{
std::ostringstream serviceOwnerLine;
serviceOwnerLine << "route-observer-service-owner " << aName << " call=" << aCount;
AppendPointerWithRva(serviceOwnerLine, "serviceOwner", observerInnerServiceOwner);
AppendObjectVtableSlots(serviceOwnerLine, observerInnerServiceOwner, {0x08});
LogRed4ext(serviceOwnerLine.str());
}
if (!routeObject) if (!routeObject)
{ {
return; return;
@@ -710,9 +741,13 @@ void LogRouteObserverContext(const char* aName, uint32_t aCount, void* aObserver
AppendPointerField(routeLine, "field78", routeObject, 0x78); AppendPointerField(routeLine, "field78", routeObject, 0x78);
AppendUint32Field(routeLine, "field80", routeObject, 0x80); AppendUint32Field(routeLine, "field80", routeObject, 0x80);
AppendByteField(routeLine, "active84", routeObject, 0x84); AppendByteField(routeLine, "active84", routeObject, 0x84);
AppendUint32Field(routeLine, "field8C", routeObject, 0x8C);
AppendUint32Field(routeLine, "field90", routeObject, 0x90);
AppendPointerField(routeLine, "field98", routeObject, 0x98);
AppendPointerField(routeLine, "field88", routeObject, 0x88); AppendPointerField(routeLine, "field88", routeObject, 0x88);
AppendPointerField(routeLine, "fieldF8", routeObject, 0xF8); AppendPointerField(routeLine, "fieldF8", routeObject, 0xF8);
AppendPointerField(routeLine, "field120", routeObject, 0x120); AppendPointerField(routeLine, "field120", routeObject, 0x120);
AppendByteField(routeLine, "fieldE2", routeObject, 0xE2);
LogRed4ext(routeLine.str()); LogRed4ext(routeLine.str());
} }