Use native WoW GUID lookup with validation
This commit is contained in:
+61
-9
@@ -26,6 +26,7 @@
|
||||
|
||||
#define CLIENT_IMAGE_BASE 0x00400000u
|
||||
#define CLIENT_OBJECT_MANAGER_VA 0x00B41414u
|
||||
#define CLIENT_GET_OBJECT_BY_GUID_VA 0x00464870u
|
||||
#define CLIENT_IN_WORLD_VA 0x00B4B424u
|
||||
#define CLIENT_PLAYER_AREA_ID_VA 0x00B4E314u
|
||||
#define AREATABLE_RECORDS_VA 0x00C0E048u
|
||||
@@ -108,6 +109,7 @@ typedef struct CharacterSnapshot {
|
||||
int raw_in_world;
|
||||
int player_confirmed;
|
||||
PlayerReadResult player_read_result;
|
||||
uint32_t lookup_method;
|
||||
uint32_t first_object_offset;
|
||||
char name[NAME_LIMIT + 1];
|
||||
char zone[ZONE_LIMIT + 1];
|
||||
@@ -626,6 +628,47 @@ static int resolve_first_object(const MemoryView *view, uint32_t manager,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lookup_object_by_guid_native(const MemoryView *view, uint64_t guid,
|
||||
uint32_t *object) {
|
||||
static const unsigned char signature[] = {
|
||||
0x55u, 0x8Bu, 0xECu, 0x8Bu, 0x45u, 0x08u, 0x8Bu,
|
||||
0x4Du, 0x0Cu, 0x8Bu, 0xD0u, 0x0Bu, 0xD1u
|
||||
};
|
||||
unsigned char actual[sizeof(signature)];
|
||||
uintptr_t address;
|
||||
uint32_t result = 0;
|
||||
uint64_t returned_guid = 0;
|
||||
|
||||
if (!view || !object || !guid) return 0;
|
||||
*object = 0;
|
||||
address = client_address(view, CLIENT_GET_OBJECT_BY_GUID_VA);
|
||||
if (!address || !memory_copy(view, address, actual, sizeof(actual)) ||
|
||||
memcmp(actual, signature, sizeof(signature)) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
__try {
|
||||
typedef uint32_t (__stdcall *GetObjectByGuidFn)(uint64_t);
|
||||
GetObjectByGuidFn get_object = (GetObjectByGuidFn)address;
|
||||
result = get_object(guid);
|
||||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if (!user_address((uintptr_t)result) ||
|
||||
!memory_u64(view, (uintptr_t)result + OBJECT_GUID_OFF, &returned_guid) ||
|
||||
returned_guid != guid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*object = result;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lookup_object_by_guid_hash(const MemoryView *view, uint32_t manager,
|
||||
uint64_t guid, uint32_t *object) {
|
||||
uint32_t table = 0, mask = 0, low, high, bucket_index;
|
||||
@@ -750,18 +793,26 @@ static PlayerReadResult read_local_player(const MemoryView *view, CharacterSnaps
|
||||
if (!stable_local_guid(view, manager, &wanted_guid))
|
||||
return PLAYER_READ_NO_GUID;
|
||||
|
||||
/* Preferred path: use the same GUID hash table the 1.12.1 client uses
|
||||
* internally. This does not depend on the visible-object list head at
|
||||
* manager+0xAC and is therefore compatible with clients where that field
|
||||
* is absent, relocated, or represented differently. */
|
||||
if (lookup_object_by_guid_hash(view, manager, wanted_guid, &player_object)) {
|
||||
/* Preferred path: call the client's own read-only GUID lookup wrapper.
|
||||
* The wrapper signature is verified before use, its result is guarded by
|
||||
* SEH, and the returned object's GUID is checked before any fields are
|
||||
* consumed. This avoids depending on a particular object-list layout. */
|
||||
if (lookup_object_by_guid_native(view, wanted_guid, &player_object)) {
|
||||
snapshot->lookup_method = 1u;
|
||||
return read_player_object(view, player_object, snapshot);
|
||||
}
|
||||
|
||||
/* Compatibility fallback for clients where the GUID hash table is not
|
||||
* available in the expected layout. */
|
||||
/* First compatibility fallback: reproduce the vanilla GUID hash lookup
|
||||
* without executing client code. */
|
||||
if (lookup_object_by_guid_hash(view, manager, wanted_guid, &player_object)) {
|
||||
snapshot->lookup_method = 2u;
|
||||
return read_player_object(view, player_object, snapshot);
|
||||
}
|
||||
|
||||
/* Final compatibility fallback: validated visible-object list lookup. */
|
||||
if (!resolve_first_object(view, manager, wanted_guid, ¤t, &first_offset))
|
||||
return PLAYER_READ_GUID_LOOKUP_FAILED;
|
||||
snapshot->lookup_method = 3u;
|
||||
snapshot->first_object_offset = first_offset;
|
||||
initial_first = current;
|
||||
|
||||
@@ -967,7 +1018,7 @@ static void publish_fault_snapshot(void) {
|
||||
replace_text_file_atomically(
|
||||
"discord_wow_status.json",
|
||||
"{\"v\":1,\"ts\":0,\"ok\":false,\"in_world\":false,"
|
||||
"\"raw_in_world\":false,\"player_confirmed\":false,\"first_object_offset\":0,"
|
||||
"\"raw_in_world\":false,\"player_confirmed\":false,\"lookup_method\":0,\"first_object_offset\":0,"
|
||||
"\"name\":\"\",\"zone\":\"\",\"level\":0,\"faction\":\"\",\"class\":\"\","
|
||||
"\"guild\":\"\",\"race\":\"\",\"build\":5875,\"err\":\"fault\"}");
|
||||
}
|
||||
@@ -1010,7 +1061,7 @@ static void publish_character_snapshot(void) {
|
||||
_snprintf(
|
||||
json, sizeof(json),
|
||||
"{\"v\":1,\"ts\":%ld,\"ok\":%s,\"in_world\":%s,"
|
||||
"\"raw_in_world\":%s,\"player_confirmed\":%s,\"first_object_offset\":%u,"
|
||||
"\"raw_in_world\":%s,\"player_confirmed\":%s,\"lookup_method\":%u,\"first_object_offset\":%u,"
|
||||
"\"name\":\"%s\",\"zone\":\"%s\",\"level\":%u,"
|
||||
"\"faction\":\"%s\",\"class\":\"%s\",\"guild\":\"%s\","
|
||||
"\"race\":\"%s\",\"build\":5875,\"err\":\"%s\"}",
|
||||
@@ -1019,6 +1070,7 @@ static void publish_character_snapshot(void) {
|
||||
snapshot.in_world ? "true" : "false",
|
||||
snapshot.raw_in_world ? "true" : "false",
|
||||
snapshot.player_confirmed ? "true" : "false",
|
||||
snapshot.lookup_method,
|
||||
snapshot.first_object_offset,
|
||||
safe_name,
|
||||
safe_zone,
|
||||
|
||||
Reference in New Issue
Block a user