Learning to read memory from ExileAPI [Help] menu

User Tag List

Results 1 to 10 of 10
  1. #1
    natemiddleman's Avatar Member
    Reputation
    3
    Join Date
    Jun 2020
    Posts
    28
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    Learning to read memory from ExileAPI [Help]

    I am trying to learn how ExileAPI works and while I think I have the general idea, when I try to put it into practice it doesn't come out as I expect. For an example, to find the str stat of the player I can scan for the address using Cheat Engine. The xp, dex, and int values are in the right place relative the the str address so I know I have found the correct one.

    Now going though ExileAPI it should be Pattern Scan > InGameState + 0x378 > Data + 0x408 > Player + 17C. I do the pattern scan then get the pointer to the InGameState address at scan + 0x1D. I am fairly certain I have done it right up to this point as the pointer puts me close to the str address. I then use dissect data/structures and find the pointer at 0x378. The problem now is that there is no pointer at 0x408 just an int of 128. At 0x410 there is a string of "Art/2DArt/UIImages/InGame/ArchetypeSelect/Custom/Chainserker" so I know I'm not in the right place. I've also tried creating a multi-pointer and using show relative offsets in memory viewer but it always ends the same.

    Can someone please explain where I am going wrong. I have spent days going over every resource I could find but none of it has helped.

    Learning to read memory from ExileAPI [Help]
  2. #2
    pushedx's Avatar Contributor
    Reputation
    257
    Join Date
    Nov 2009
    Posts
    137
    Thanks G/R
    8/135
    Trade Feedback
    0 (0%)
    Mentioned
    12 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by natemiddleman View Post
    Can someone please explain where I am going wrong. I have spent days going over every resource I could find but none of it has helped.
    Your InGameState pointer is wrong, because ExileApi's code to get the InGameState pointer is more than just "Pattern Scan + 0x1D"

    To help you visualize what's going on, I'll go bit by bit through their code:

    1.
    private static readonly Pattern GameStatePattern = new Pattern(
    new byte[]
    {
    0x48, 0x83, 0xec, 0x50, 0x48, 0xc7, 0x44, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x89, 0x9c, 0x24, 0x00, 0x00, 0x00, 0x00, 0x48,
    0x8b, 0xf9, 0x33, 0xed, 0x48, 0x39
    }, "xxxxxxxx?????xxxx????xxxxxxx", "Game State", 1240000);
    This creates the following pattern (which is actually usable in x64dbg/ida): 48 83 ec 50 48 c7 44 24 ?? ?? ?? ?? ?? 48 89 9c 24 ?? ?? ?? ?? 48 8b f9 33 ed 48 39

    You'll land here in the client for reference: Screenshot - 92c6238a81211bdf999e05d4e44b4f5f - Gyazo

    2.
    GameStateOffset = m.Read<int>(baseAddress + array[index] + 29) + array[index] + 33;
    This is x64 RIP relative addressing calculation code, if you're not familiar with the concept.

    In the above screenshot, the address being calculated is "0x7FF7EFBDF460". However, if you check the x64 asm, only a DWORD is stored (bytes 97 2F 3F 03) which starts at 0x1D (or 29).
    To get the absolute address, you read the DWORD, "m.Read<int>(baseAddress + array[index] + 29)" then add the address of the start of the instruction, which is 0x1A, and then add the length of the instruction, which is 7. Or, to express it like ExileApi does, 0x1A + 7 = 0x21 = 33, which is the offset of the next instruction, since the address is being calculated relative to the start of the sig itself

    0x7FF7EC7EC4A8 (start of pattern) + 0x033F2F97 (relative DWORD from client) + 0x21 (offset of next instruction after the memory address) = 0x7FF7EFBDF460 - so you know you've done it correctly.

    3. Once you have this address, you now need to see how ExileApi uses it. If you search in VS for all uses of "OffsetsName.GameStateOffset", you'll land in GameStateController.cs
    Address = m.Read<long>(m.BaseOffsets[OffsetsName.GameStateOffset] + m.AddressOfProcess);
    ...
    AllGameStates = ReadHashMap( Address + 0x48 );
    ...
    InGameStatePtr = AllGameStates["InGameState"].Address;
    This is basically reading the pointer stored at the address found (that is, the pointer stored in 0x7FF7EFBDF460 ), and then reading what they call a "HashMap" structure from it at offset 0x48.

    Memory layout for that object would look something like this: Screenshot - 06a3992acc1a23434c7ef82c853074d4 - Gyazo

    I myself call this struct NativeStateManager and the simplifed representation of the C++ object is: std::map<std::wstring, std:: pair<void*, void*>>(to give you the idea of the memory layout). std::wstring is a standard 0x20 bytes for the the VS version the game is using. To understand the exact layouts (and the sub-types used in std::map), check the header files or debug a program of your own in x64dbg.

    You can trace through the "ReadHashMap" function to get an idea of how it's traversing it in the client, or you can look to do it yourself another way. If you plan to do a lot of stuff in poe, you should take the time to understand how C++ containers look in memory and write good code to work with them as they're used all over the place.

    Once you have found the InGameState pointer from the map, you can then proceed to walk down the chain of offsets like you mentioned. However, once you get to the player's entity pointer (Entity object in ExileApi), you need to access the Player component from that entity pointer and not just add 0x17C. Check out the 'Component' class in ExileApi and the systems in place for finding components in an object to better understand the ECS model PoE uses.

    When you have the Player component from the player's entity, you can then add 0x17C to read the Str, assuming that offset is still correct.

    To fix your work process, it'd be more like this roughly speaking:
    Pattern Scan -> State Manager -> Lookup InGameState
    InGameState + 0x378 > Data
    Data + 0x408 > player's Entity
    player's Entity > Lookup Player Component
    Player Component + 17C > Str

  3. Thanks GameHelper, snowhawk, NoobToken (3 members gave Thanks to pushedx for this useful post)
  4. #3
    natemiddleman's Avatar Member
    Reputation
    3
    Join Date
    Jun 2020
    Posts
    28
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Thanks so much. I was missing : AllGameStates = ReadHashMap( Address + 0x48 ); Now I need to figure out how to read the hashmap. From ExileAPI I see:

    public GameStateHashNode Previous => ReadObject<GameStateHashNode>(Address);
    public GameStateHashNode Root => ReadObject<GameStateHashNode>(Address + 0x;
    public GameStateHashNode Next => ReadObject<GameStateHashNode>(Address + 0x10);
    public bool IsNull => M.Read<byte>(Address + 0x19) != 0;
    public string Key => M.ReadNativeString(Address + 0x20);
    public GameState Value1 => ReadObject<GameState>(Address + 0x40);

    Imgur: The magic of the Internet

    This looks simple enough but of course it's not. Using the pointer at Address + 0x48 (is this supposed to be a pointer?) I find something that looks kind of correct. It has 3 pointers that I think are previous, root, and next. I open the third pointer and I get 3 more pointers but this time the third pointer points back to the first three for a loop. Opening up the pointer at 0x20 I find the WaitingState and InGameState strings through blind luck (not good). Opening up various pointers I can find the different states but not in an order I can recognize and it often loops back on itself. Can you go into a little more detail on how the hashmap works?

  5. #4
    pushedx's Avatar Contributor
    Reputation
    257
    Join Date
    Nov 2009
    Posts
    137
    Thanks G/R
    8/135
    Trade Feedback
    0 (0%)
    Mentioned
    12 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by natemiddleman View Post
    Can you go into a little more detail on how the hashmap works?
    This is the best way I can think of to help you with that.

    Create a new C++ console program:
    Code:
    #include  <windows.h>
    #include  <intrin.h>
    #include  <map>
    #include  <list>
    #include  <string>
    #include  <vector>
    #include  <iostream>
    
    int main(int argc, char* argv[])
    {
    	// Setup an imaginary map that is in the same format as the client's AllStates map
    	std::map<std::wstring, std::pair<void*, void*>> states;
    	states[L"MyState1"] = std::make_pair(reinterpret_cast<void*>(0x1111111111111111), reinterpret_cast<void*>(0x2222222222222222));
    	states[L"MyState2"] = std::make_pair(reinterpret_cast<void*>(0x3333333333333333), reinterpret_cast<void*>(0x4444444444444444));
    	states[L"MyState3"] = std::make_pair(reinterpret_cast<void*>(0x5555555555555555), reinterpret_cast<void*>(0x6666666666666666));
    	states[L"MyState4"] = std::make_pair(reinterpret_cast<void*>(0x7777777777777777), reinterpret_cast<void*>(0x8888888888888888));
    
    	// Show some pointer info to reference in a memory viewer.
    	std::cout << "states: 0x" << std::hex << (&states) << std::endl;
    	std::wcout << std::endl;
    	
    	for (auto itr = states.begin(); itr != states.end(); ++itr)
    	{
    		std::cout << "[0x" << std::hex << &(*itr) << "]" << std::endl;
    		std::wcout << L"\tFirst: " << (*itr).first << std::endl;
    		std::cout << "\tSecond: 0x" << std::hex << (*itr).second.first << " | 0x" << (*itr).second.second << std::endl;
    		std::cout << std::endl;
    	}
    
    	// This is to help separate compile generated code to see each statement's assembly
    	__nop();
    	__nop();
    	__nop();
    	auto startItr = states.begin();
    	__nop();
    	__nop();
    	__nop();
    	auto endItr = states.end();
    	__nop();
    	__nop();
    	__nop();
    	while(startItr != endItr)
    	{
    		__nop();
    		__nop();
    		__nop();
    		OutputDebugStringW(startItr->first.c_str());
    		__nop();
    		__nop();
    		__nop();
    		++startItr;
    		__nop();
    		__nop();
    		__nop();
    	}
    	__nop();
    	__nop();
    	__nop();
    
    	// Let the program stay in memory until you're done reversing
    	std::cout << "Please press Enter to exit...";
    	std::string input;
    	std::getline(std::cin, input);
    	
    	return 0;
    }
    Compile in Release and run it from Visual Studio and attach your memory viewer, or run the release executable in a debugger (you really do need to be using x64dbg if you have big plans for poe).

    You'll see something like:
    states: 0x00000053B56FFDE0

    [0x00000187A48968C0]
    First: MyState1
    Second: 0x1111111111111111 | 0x2222222222222222

    [0x00000187A4896C40]
    First: MyState2
    Second: 0x3333333333333333 | 0x4444444444444444

    [0x00000187A4896CC0]
    First: MyState3
    Second: 0x5555555555555555 | 0x6666666666666666

    [0x00000187A48A0890]
    First: MyState4
    Second: 0x7777777777777777 | 0x8888888888888888

    Please press Enter to exit...
    Your map address is the address listed by 'states', or 0x00000053B56FFDE0. if you view that memory, you should see the map container, which is the head pointer followed by the container size. For each of the key-value paris, scroll up to see the map node they are contained in as the iterator is pointing to your memory and not the map node's memory start.

    Screenshot - 45873ce601234aadf96ee26f6b1a5a47 - Gyazo

    When you follow the head pointer, you'll arrive at the main map node object: Screenshot - a3cb6f3a5b86b329cb0b89c342fa43a9 - Gyazo

    To understand this object, you need to dig through the c++ map header files, but for the sake of making this easier to understand, here's the header file contents for that templated object: Screenshot - d49bab5e02cf7bb4a35b3bc12bc420ce - Gyazo

    So you know the layout of the object your'e viewing. As I mentioned before, the rough memory layout was a std::wstring followed by a pair of pointers, so the 0x20 bytes after the start belong to your std::wstring, followed by the two pointers for the state pointer afterwards.

    Now for the question of how to iterate. The underlying type is a red-black tree, so you can either follow the source code in the map header files, or the general theory of red-black tree traversal works too. If you follow the logic in x64dbg in the code I posted above with nops, you should start to get an idea and be able to match things up, of how it checks the color and isnil fields.

    It's a bit tedious work wise trying to figure this stuff out the first time, but you only have to do it once per container type, then you'll pretty much not have to worry about it until the container changes, which is pretty rare. The code we wrote back in 2012-2013 is still relevant for this stuff, so that's why I mentioned before, if you plan to do anything with poe, take the time to understand the C++ containers as they're used all over the place, and once you can identify them, the memory layout of things starts to make more sense.

  6. Thanks GameHelper, Williamwillbera, Neer (3 members gave Thanks to pushedx for this useful post)
  7. #5
    natemiddleman's Avatar Member
    Reputation
    3
    Join Date
    Jun 2020
    Posts
    28
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    I really appreciate your assistance. I'll try this out as soon as I can.

  8. #6
    xhulic's Avatar Member
    Reputation
    2
    Join Date
    Oct 2020
    Posts
    11
    Thanks G/R
    3/1
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    @pushedx I'm struggling to the all games state ptr.

    Code:
    pattern = rb"\x48\x83\xec\x50\x48\xc7\x44\x24.....\x48\x89\x9c\x24....\x48\x8b\xf9\x33\xed\x48\x39"
    pattern_address = process.scan_module(process.base_module, pattern)
    print(hex(pattern_address)) # 0x7ff66ea904d8
    
    game_state_offset = process.read_int(pattern_address + 29) + pattern_address + 33
    print(hex(game_state_offset)) # 0x7ff67203af78
    
    address = process.read_int(game_state_offset)
    print(hex(game_state_offset)) # 0x41643080
    As you can see 0x41643080 is an address that is not within POE address space: https://i.imgur.com/aLYXUfL.png

    I'm surely doing something small wrong? Can anyone help?

  9. #7
    pushedx's Avatar Contributor
    Reputation
    257
    Join Date
    Nov 2009
    Posts
    137
    Thanks G/R
    8/135
    Trade Feedback
    0 (0%)
    Mentioned
    12 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by xhulic View Post
    @pushedx I'm struggling to the all games state ptr.

    Code:
    pattern = rb"\x48\x83\xec\x50\x48\xc7\x44\x24.....\x48\x89\x9c\x24....\x48\x8b\xf9\x33\xed\x48\x39"
    pattern_address = process.scan_module(process.base_module, pattern)
    print(hex(pattern_address)) # 0x7ff66ea904d8
    
    game_state_offset = process.read_int(pattern_address + 29) + pattern_address + 33
    print(hex(game_state_offset)) # 0x7ff67203af78
    
    address = process.read_int(game_state_offset)
    print(hex(game_state_offset)) # 0x41643080
    As you can see 0x41643080 is an address that is not within POE address space: https://i.imgur.com/aLYXUfL.png

    I'm surely doing something small wrong? Can anyone help?
    Your last 'read_int' is reading only 32-bits, but pointers are 64-bit in x64. Simply read 64-bits instead.

    The reason you read a 32-bit int in the step before, is because you're reading an x64 instruction that uses 32-bit encoding for 64-bit relative addressing.

    Also not sure if it was a copy/paste issue, but you didn't print 'address ' but rather 'game_state_offset' a 2nd time.

  10. #8
    xhulic's Avatar Member
    Reputation
    2
    Join Date
    Oct 2020
    Posts
    11
    Thanks G/R
    3/1
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Also not sure if it was a copy/paste issue, but you didn't print 'address ' but rather 'game_state_offset' a 2nd time.
    Correct, that was a typo!

    Your last 'read_int' is reading only 32-bits, but pointers are 64-bit in x64. Simply read 64-bits instead.
    Yep, I'm an idiot All working now! Also added some bookmarks for RIP-relative. Thanks again!

  11. #9
    carnifex_v2's Avatar Member
    Reputation
    1
    Join Date
    Sep 2019
    Posts
    4
    Thanks G/R
    5/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hey,
    did "GameStateOffset" or "The Game Address" change?
    For AreaChangeCount it works fine (2 Addresses on the right).

    PathOfExile_x64.exe+1B13F8 +29 ---> 5825048B
    PathOfExile_x64.exe + 5825048B + 1B13F8 + 33 --> ???

    ce — ImgBB

    edit:

    nvm I got it
    29 and 33 aren't hex

    PathOfExile_x64.exe+001b13f8+1D ---> 035C7F6F
    PathOfExile_x64.exe + 035C7F6F + 1B13F8 + 21
    Last edited by carnifex_v2; 11-22-2020 at 10:15 AM.

  12. #10
    thefrobel's Avatar Member CoreCoins Purchaser
    Reputation
    8
    Join Date
    Jul 2012
    Posts
    99
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Natemiddleman / PushedX Have you guys kept up w/ the RE for the current release of POE? I have someone interested in building a platform for it, but they don't want to do the RE...
    Feel free to reply or PM me directly.

Similar Threads

  1. Replies: 0
    Last Post: 06-25-2017, 01:38 PM
  2. reading memory from elite dangerouss
    By moe in forum Programming
    Replies: 2
    Last Post: 12-26-2014, 12:22 AM
  3. Learning to play/Video Edit. Please help!
    By Shleven in forum World of Warcraft General
    Replies: 2
    Last Post: 05-19-2012, 10:16 AM
  4. Replies: 1
    Last Post: 07-23-2009, 01:17 PM
  5. Witch util are you using to read memory ?
    By guillaume76290 in forum WoW Memory Editing
    Replies: 3
    Last Post: 07-19-2009, 07:52 PM
All times are GMT -5. The time now is 06:21 PM. Powered by vBulletin® Version 4.2.3
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2024 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search