Register for your free account! | Forgot your password?

Go Back   elitepvpers > Popular Games > Silkroad Online > SRO Coding Corner
You last visited: Today at 10:21

  • Please register to post and access all features, it's quick, easy and FREE!

Advertisement



Memory (Client) Based Pet Pickup Filters

Discussion on Memory (Client) Based Pet Pickup Filters within the SRO Coding Corner forum part of the Silkroad Online category.

Reply
 
Old   #1

 
elite*gold: 260
Join Date: Aug 2008
Posts: 560
Received Thanks: 3,751
Memory (Client) Based Pet Pickup Filters

In this thread, I'm going to talk about memory (client) based pet pickup filters.

WARNING: This is a super technical thread, with no ready to use files/code. This is basically a behind the scenes look at what all would go into making an end-user client based pet pickup filter. In other words, if I were to make such a tool in the future, I'd reference this thread to show how it was done and how it works. I don't have such tool yet though, outside of my dev prototypes.

Traditionally, pet filters have always been done using packets. You proxy the client, disable the game's pet pickup option, parse packets, then manually send pet pickup packets with the items you want. Pretty straightforward, but by no means simple. If you want more details, DaxterSoul has a great writeup about the process .

By approaching this problem another way, using the client's memory, it is possible to bypass the need to do all that packet parsing, and instead just grab what you need to filter items during runtime. However, this approach is more complex in implementation due to three reasons. First, you need to find what you want to filter by in memory. Second, you need to write some ASM to codecave the client so it calls your filter logic and not theirs. Third, you need to create a way to manage the new filter, either externally, or internally (which is now possible thanks to florian0's )

In this thread, I'll talk about the memory stuff and the asm stuff. However, as it turns out, the VSRO pet pickup logic itself is buggy. I've created a video showing one of the exact, replicable problem with why the pet sometimes doesn't loot items despite it being in the filter. The other known problem is when the pet itself gets blocked by an obstacle and ignores an item, despite you being able to loot it.

VSRO Pickup Bug:

The problem seems to be the pet gets put in a "catch up" state when you move away after it goes to loot an item, and any items that drop during this time end up getting ignored, most likely due to the pick operation failing. The items themselves are marked for looting (as I can see the code check them in my filter), but there's a flaw with the actual client logic for handling this scenario.

That means ultimately, you don't want to just modify the client's pet filter logic like this thread will talk about. Instead, it would be better to replace the pet pickup logic entirety (in which case you'll be coding new filter logic anyways), which is doable thanks to the model SRO_DevKit is using. However, that's outside the scope of this thread, as this thread will just cover the pet filtering logic using their flawed pet pickup logic.

While it is possible to use client memory to tell a packet based pick filter what to loot (which might be the most ideal short term solution), that's even more layers of complexity and requires a bit too much for a thread like this. It is possible though, so if you want to get creative with it, you certainly can.

One last section of notes/disclaimers/warnings. When making threads like this, to talk about some specific topic, the implementation/process used to educate people on various things rarely matches the implementation/process required for a stable/reliable/performant/etc... project. In other words, the purpose of these threads is to show it's possible, one way that is confirmed to be working, but it will never be the best way, or even the right/ideal way, because when it comes to complicated things like this, complexity and "easier to understand" things are mutually exclusive. It's either one or the other, and my idea of "easier to understand" is probably already overly complex anyways.

So please keep that in mind. I focus on identifying/solving problems first using any means that works, and then work on finding better ways to create a usable solution.

With all that said, let's get started! This is for VSRO 188, but you could adapt it to any version conceptually.

First, let's talk about finding pet pickup/filter logic. The pet pickup opcode is 0x70C5. That's been documented over the years, but you could find it with a packet analyzer pretty easily. Knowing that, you'd search the client for all instances of that opcode, and see which code section(s) get called when the pet goes to loot an item. Since you can drop items and have the pet loot them, all you need to do is find a quiet place in town to test.

The main pet pickup function can be found at 0x8AFB50. In this function, the 0x70C5 packet building gets executed when the pet picks up an item, so it was pretty straight forward finding the starting point. Using RTTI information and checking the code that calls this function, you will discover we're executing a function using the context of a CDropItemManager instance.

Just to shout out a few notable lines of code with some comments:
Code:
008AFB7B | 8B0D ECF5EE00           | mov ecx, dword ptr [<g_pCICPlayer>]        | [GLOBAL] CICPlayer : CICUser : CICharactor : CIGIDObject : CIObject : CIEntity : CObjChild : CObj : CAnimationCallback : SICharInfo
008AFB81 | E8 EA501200             | call <GetPlayerCCosDataMgr>                |

...

008AFC08 | F686 A8630100 80        | test byte ptr [esi+0x163A8], 0x80          | is pet enabled?  
008AFC0F | 0F84 D9010000           | je <NextCosLabel>                          |

...

008AFC46 | A1 ECF5EE00             | mov eax, dword ptr [<g_pCICPlayer>]        | [GLOBAL] CICPlayer : CICUser : CICharactor : CIGIDObject : CIObject : CIEntity : CObjChild : CObj : CAnimationCallback : SICharInfo
008AFC4B | 8A88 5C060000           | mov cl, byte ptr [eax+0x65C]               |

...

008AFC91 | 68 F0F6EE00             | push <sro_client.GFXRuntimeClass_CICCos>   | EEF6F0:&"CICCos"
008AFC96 | 8BCF                    | mov ecx, edi                               |
008AFC98 | E8 D34E2E00             | call <GFXRC_IsSame>                        |
008AFC9D | 84C0                    | test al, al                                |
008AFC9F | 75 1C                   | jne 0x8AFCBD                               |
008AFCA1 | 68 44F0DD00             | push sro_client.DDF044                     | DDF044:"pObj->IsSame( GFX_RUNTIME_CLASS(CICCos) )"
008AFCA6 | 68 00F0DD00             | push sro_client.DDF000                     | DDF000:"D:\\vss-od\\Silkroad\\Client\\client\\DropItemManager.cpp"
008AFCAB | 68 FA000000             | push 0xFA                                  |
008AFCB0 | E8 DBE7BEFF             | call 0x49E490                              |
008AFCB5 | 83C4 0C                 | add esp, 0xC                               |
008AFCB8 | 84C0                    | test al, al                                |
008AFCBA | 75 01                   | jne 0x8AFCBD                               |

...

008AFCF5 | 8B4C24 2C               | mov ecx, dword ptr [esp+0x2C]              | CDropItemManager
008AFCF9 | E8 F2FAFFFF             | call 0x8AF7F0                              |

...

008AFD06 | 8B4424 18               | mov eax, dword ptr [esp+0x18]              |
008AFD0A | 50                      | push eax                                   |
008AFD0B | E8 10351100             | call <IGIDObject_GetById2>                 |
008AFD10 | 53                      | push ebx                                   |
008AFD11 | 68 C5700000             | push 0x70C5                                | pet pick
008AFD16 | E8 651AF9FF             | call <CanUseOpcode>                        |
008AFD1B | 83C4 0C                 | add esp, 0xC                               |
008AFD1E | 85C0                    | test eax, eax                              |
008AFD20 | 0F84 8F000000           | je 0x8AFDB5                                |
008AFD26 | 68 C5700000             | push 0x70C5                                |
008AFD2B | 8D4C24 38               | lea ecx, dword ptr [esp+0x38]              |
008AFD2F | E8 6CD1C8FF             | call <SetOpcode>                           |
We can understand the big picture logic as follows: The client iterates through the character's cos, looking for a pickup pet, and then checks to make sure your pet filter is enabled. When it finds the pet, it'll call into another class function to get an item to loot. If it has an item to loot, it'll then build the pet pickup packet and send it. I'm skipping over a few details, but that should be enough to get the gist of things.

What we're interested in, is the function call that filters items and reports back the item that gets used in the packet building code. That function (0x8AF7F0) can be found from the call referenced at address 0x8AFCF9. I lost the comments I had for the function during a multiple client editing mishap, but it's ok because the function itself is pretty small:

Basically, the function will iterate a collection of dropped items, perform type filtering based on client settings (gold/equipment/other), do what seems to be a distance check (only a guess, I've not dug into the entirety of all code yet) and then returns (not literally, via a reference parameter) an item that should be looted by the pet.

The following address are the juicy bits when it comes to the type filtering:
Code:
008AF84F | 8A4C24 28               | mov cl, byte ptr [esp+0x28]                |
008AF853 | F6C1 40                 | test cl, 0x40                              |
008AF856 | 75 08                   | jne 0x8AF860                               |
008AF858 | A8 01                   | test al, 0x1                               |
008AF85A | 0F84 9D000000           | je 0x8AF8FD                                |
008AF860 | F6C1 01                 | test cl, 0x1                               |
008AF863 | 74 04                   | je 0x8AF869                                |
008AF865 | A8 08                   | test al, 0x8                               |
008AF867 | 75 16                   | jne 0x8AF87F                               |
008AF869 | F6C1 02                 | test cl, 0x2                               |
008AF86C | 74 04                   | je 0x8AF872                                |
008AF86E | A8 10                   | test al, 0x10                              |
008AF870 | 75 0D                   | jne 0x8AF87F                               |
008AF872 | F6C1 04                 | test cl, 0x4                               |
008AF875 | 0F84 82000000           | je 0x8AF8FD                                |
008AF87B | A8 18                   | test al, 0x18                              |
008AF87D | 75 7E                   | jne 0x8AF8FD                               |
Your pet filter flags are being loaded from the stack at address 0x8AF84F. I've not created the proper enum for the flags yet, but here are some easily observed values (by looking at packets or debugging memory)
Code:
0x00 - off/my items/nothing
0x80 - on/my items/nothing

0x40 - off/all items/nothing
0xC0 - on/all items/nothing

0x1 - off/my items/gold
0x81 - on/my items/gold

0x41 - off/all items/gold
0xC1 - on/all items/gold
Since this is the logic we want to replace entirety, I'm not going to bother trying to re-code it exactly as they have it. All we needed to know, was that this was the right location for the primitive type matching they use. Doing a bit more debugging, we can figure out where the relevant pointers we care about for the actual item being processed are, and then using previously discovered information, we can throw together a solution for a more complex pet filter.

At this point, we have a good idea of where we need to modify the client, but now the question is what do we modify and why? The approach I use is simply to find all data I want to use, write the asm codecave to call an external function, and then write the external function via an exported function in a C++ DLL. There are other ways to do it of course, but this is a pretty basic way to present a solution to the problem.

If we're going to make a new item filter, what information do we need/care about? For this example, I care about:
- The player name, so I can have player specific item filters
- The item name, so I can filter by easy to understand item names
- The item type name, so I can filter by items in a way that will compatible in different languages/versions
- The item id (as in what's going to be sent via the 0x70C5 packet) for tracking or possibly more complex logic with letting a packet based filter solve the client filter logic issues
- The original item type the client is using for filtering it's basic categories. This is just for debugging or making sure we can still support default filter logic (although I don't do this for this thread)

The last useful thing would be the user's pet filter settings, but I choose to ignore this for this implementation, only because this is a first iteration of a solution that shouldn't be used in practice. As I mentioned before, first find a way to solve the problem, then look into better ways to implement the solution. Right now, the current dev implementation I'm using, is just not good for a number of reasons.

Anyways, we know what we want from memory, but now the question is how to find it? Well, if you don't know how to do anything with memory, you'll want to familiarize with the following two threads:



As I mentioned in those threads, the GFXRC stuff is the cornerstone of future things using client memory By finding relevant classes in the client and dumping pointers, we can track down useful information such as the item name/type, as the pointers will contain information loaded from the PK2s. I'll mention again, but the GFXRC is large portion of important stuff, but the client has plenty more that is separate from that system you'll have to reverse/create layouts for.

Obviously, I'm making a huge jump from what's posted in the GFXRC threads to specific VSRO memory related things, as you'll have to reverse memory layouts much like you would reverse packets. If some new regional SRO version came out using entirely new opcodes and packet layouts, a lot of new work would have to be done to get back to having what we already do for existing SRO versions. Trying to work with memory is like that in a sense, because there's been only a time amount of memory based work (mostly done in SRO_DevKit) vs all the packet based work that's taken place for years.

That inherently means this thread won't be usable for most people, because:
- They aren't familiar with asm and can't do the asm modifications required to expose the client information to an external DLL
- They aren't familiar with working with client memory, and building up the tools and workflow in C# (even though I've provided that already) is still a big hurdle and time investment
- This thread only covers 1/3 of what needs to be done for a proper solution. As mentioned before, VSRO's pet pickup logic is broken and needs to be rewritten, and ideally you'd want a user gui or external gui program to manage filters. You can hack together an improvement using this the ideas in this thread, but you still have quite a bit of work to do if your target is for user-friendly, pserver ready deployment.

Because of these reasons, I'm not going to do a big picture editing guide or even post modified files, because they'd simply not be usable as-is unless you're able to do this stuff on your own (after knowing it can be done, and having a reference of how to do it). However, it's still useful information to know, and for more advanced projects like SRO_DevKit, those are more suitable for stuff like this, since that project is already rewriting large parts of the client for complete customization (which is the ideal solution to the pet filter problem).

What comes next is the ASM I've written to pass client memory information to the external DLL that will contain the new pet filtering logic. The idea behind the ASM is simple: load our filter dll if it's not loaded, get the address of the filter function from the dll, build the function call for the dll using the memory pointers we've found using GFXRC or referenced from other projects, and then call the function and process the result to know where we should return execution to in the client.

Another reason why I'm not doing an editing guide for this stuff, is because you have to make the patches in relation to the address of your newly added code section to the client, and that gets overly cumbersome to explain. As mentioned at the start of the thread, when presenting information like this in a thread, the solution/implementation used, is not the same as the ideal solution/implementation. Normally, I would not manually write a bunch of ASM like this for every little thing, and instead use a framework I've created to do this stuff, but it's not practical to reference complex things like those in already overly complex/long threads like this. Hopefully readers will understand - this stuff is hard to manage when you're trying to share information.


Before getting into the specifics of that ASM and what everything is doing and why, I need to skip ahead to the implementation of the pet filter function we'll be calling from our external DLL. Since the function is pretty much the entirety of the DLL, I'll just paste the main file itself.

DLLMain.cpp

The 'ShouldLoot' function will be called with already processed client data. All of the interesting things I mentioned before, are being passed into the function for processing. An example filter would look like this:
Code:
{
    "types" : [
        "ITEM_ETC_GOLD_01",
        "ITEM_ETC_GOLD_02",
        "ITEM_ETC_GOLD_03",
        "ITEM_ETC_AMMO_BOLT_01",
        "ITEM_ETC_AMMO_ARROW_01"
        ],
    "names" : [
        "Jade tablet of strikes(Lvl.1)",
        ],
}
The idea behind the shown filter logic, is to simply check the item being looted against the user's settings. As stated before, we check types when we want to have a filter that can be used on different SRO versions/languages (as the base types don't change, only stuff is added or removed the vast majority of the time). However, types are ugly not user-friendly names, and rely on PK2 file data to obtain. Names are more natural for the most part, but case/spacing matters, and can be more problematic when you need to support more than 1 language.

For example, in VSRO the name of arrows is actually "Arrow " (with an extra space) as opposed to "Arrow". Obviously you can do some trimming and stuff, but when it comes to case or punctuation, it's far better to just use item types instead, and make a user gui that simplifies the process of adding/removing categories rather than having users manually type in the items (although they could if they wanted to). This gets into the user gui part I mentioned earlier and why I'm not covering it in this thread, there's a lot of decisions to be made, and different options depending on what works best for each server.

Getting back to the ASM code, if you work your ways from bottom to top, as stack arguments have to be pushed in reverse order, each section of the call building code is simply obtaining the necessary information from the client, based on memory structures found using the GFXRC project and a debugger. I dump memory layouts, check pointers, and then code in the std::n_wstring.c_str() logic to pass a wchar_t* to the DLL. The LoadLibrary/GetProcAddress logic should be pretty obvious. The debugger doesn't show the function string correctly, but "ShouldLoot" is being passed to GetProcAddress.

The other ASM code is just doing what the client did to get the CIItem and verify it's actually a valid CIItem. I will note I did not do one thing the client code does, because it wasn't really needed. In the client, the following line ends up not being done in my codecave, because I restore original registers before jumping back:
Code:
008AF888 | 8BF0                    | mov esi, eax                               |
Typically, you'd want to always match registers when you interrupt original code, but in the cases where you know they aren't needed, you don't have to. I should at least mention it though, in case the register is being used in another version (not that my ASM is usable as-is anyways, but if anyone with keen eyes notices).

Lastly, I need to mention the codecave itself. This is where I jmp into the codecave and nop their existing category matching logic:

Keep in mind I return to the client in different locations based on if the item is going to be matched or not. Since I don't do flag checking for all/my items, I'd probably have to add an extra parameter for that, but I don't need it for my dev testing, so I didn't do it (as once again, none of this stuff is ready to use for most people anyways).

An alternative is also possible. Instead of having complex ASM codecave logic like I do, you can opt to just pass in specific pointers to the DLL instead, and then do the memory reading from the external DLL (as it's injected into the client, so it has full access to memory). That approach is also fine, but then ties the filter dll to each client version, as opposed to only needing the custom ASM codecave logic per-client instead. Typically, I'd automate the process of writing that ASM codecave stuff, so making it per version is the better solution and keeping the filter DLL independent, but I can't do that for this thread, so that's why it might seem weird to do things that way.

This is just a development thread that talks about the different things that can be done, but an end user release thread would make a specific decision and go whatever direction was bet for the server. I'm obviously out of the loop on what the SRO playerbase wants for pet filter customization, so I'm not going to choose a solution that would only work for me right now.

The end result of all this stuff, is being able to have a customizable pet filter for the client, that doesn't rely on packet processing. Obviously it's a different type of complexity, and probably overly complex for most if you've never done memory stuff or client editing, but I think it's a viable solution that could most likely fit into a project like SRO_DevKit.

Here's a little dev video of it in action:
(The double debugger output is just a side-effect of the way my debugger is configured, the code itself isn't bugging out. Also if the video is blurry, it's because YT is still processing the HD version)

As mentioned before, there's really little point in doing all this work to make pet filters more customizable and not just go ahead and fix their pet pickup logic while you're at it. In that regards, just using a packet sending DLL instead would probably be better, as you'd still want the pet filter logic running (with no items selected) so the DLL has the info to process still (as it doesn't run if you turn off pet pickup!)

But the process of things is crawl, walk, run, so I started out with this specific thing to be able to make my way into the bigger picture problem with the pet pickup issue. There's a glaring flaw to me in their solution that will be really hard to overcome without server changes. The pet itself is a physical cos entity in the world, so it does pathfinding checks and can get stuck, and has somewhat flakey follow logic.

I think the better solution would be for it to simply be a virtual avatar, and whether it can loot an item or not is based on whether the player can loot an item from where they're standing. Ideally, a more advanced pathfinding system (that they don't have) would be best so it could pathfind from loot to loot, and the time it takes to travel is estimated based on that rather than actually moving in straight lines to physically loot an item. Pets getting stuck and being unable to loot just seems so counterintuitive for that type of "premium" feature, but I digress.

Anyways, I know this was long and excessively technical, and probably not usable for most, but hopefully a few people can get some ideas or motivations from it. Happy hacking!
pushedx is offline  
Thanks
47 Users
Old 09/17/2021, 12:02   #2

 
SubZero**'s Avatar
 
elite*gold: 0
Join Date: Apr 2017
Posts: 986
Received Thanks: 456
Well done bro
SubZero** is offline  
Old 09/17/2021, 13:03   #3


 
XxGhostSpiriTxX's Avatar
 
elite*gold: 53
Join Date: Jul 2012
Posts: 538
Received Thanks: 185
good bro
XxGhostSpiriTxX is offline  
Old 09/19/2021, 20:01   #4
 
elite*gold: 312
Join Date: Jul 2020
Posts: 160
Received Thanks: 13
please boss
create guide for noobs like me how to get the right address for something to hook
for example when i hover on avatar how i can get the address

i see this code in dev_kit

Code:
replaceOffset(0x00682AFC, addr_from_this(&CIFItemComparison::AppendAdvancedInfo)); // set
replaceOffset(0x00682D6E, addr_from_this(&CIFItemComparison::AppendAdvancedInfo)); // accs
replaceOffset(0x00682FBE, addr_from_this(&CIFItemComparison::AppendAdvancedInfo)); // wep
replaceOffset(0x0068320E, addr_from_this(&CIFItemComparison::AppendAdvancedInfo)); // shield
kotsh23 is offline  
Old 09/20/2021, 08:47   #5

 
elite*gold: 260
Join Date: Aug 2008
Posts: 560
Received Thanks: 3,751
Quote:
Originally Posted by kotsh23 View Post
please boss
create guide for noobs like me how to get the right address for something to hook
Unfortunately, no such guide is possible, because figuring out the right address to hook something is a result of just reversing the game. You need to know x86 assembly, how to use a debugger (x32dbg/ollydbg), how C/C++ code gets generated in MSVC, and optionally know how to use a disassembly tool like IDA/GHIDRA. Then, you need to understand how the game itself works, so you can figure out where you need to hook to change features and stuff.

It's years and years of practice and building up experience to get to the point where you can just find whatever you want. I started learning this stuff in 2006 and am still learning stuff and advancing my skills, so it's been a very long time to get to where I am now. There's no real good guides to follow either, you just have to figure out what works for you learning wise, but thankfully a lot of information is on the internet nowadays, so it's not an impossible task.

If you were looking for an explanation of how someone found addresses like the ones you mentioned, the best bet is to usually just ask the person who found them, which if it's from sro_devkit, should be florian0. He's actively posted a lot of useful information about the process he's used to find stuff (check his blog posts too!) so maybe you can message him or post in the devkit thread if you need help understanding how to find something he has, assuming it was him.

Just a note though, if you don't know much about this stuff at all, asking someone who does usually comes off as "solve my problems for me", so getting specific help about something you want is usually hard if it's not something someone in the community wants to do for everyone. I made this thread about client based pet filter only because I saw a lot of threads over the years about people wanting to do it, but only knowing of the packet based way or just using a bot.

After reading this thread, maybe people decide the packet based way is easier/better, but an alternative does exist. If someone asked me a month ago to help them do a client based pet filter, I'd give them general tips on how to go about it, but if I wasn't interested at the time in looking at it, I simply would have declined. That's just the nature of this stuff. So if you need something specific, you can ask around and hope someone is interested, but usually you'll either have to just wait until someone does it, or learn how to do it all yourself (like I did) so you never have to wait on anyone again

That's the main motivation for my posts. Yes, it's super complex and intimidating, but if you're willing to take the time and learn and put in the work over years, you too can do also do stuff like this. A lot of people have spent 5/10/15 years around SRO now. That's a long time. Had they just dedicated the same time to learning how to do things themselves, they'd already be at a point where they wish to be. Time is going to pass regardless, so the best time to start learning this stuff is today. Then, you just have to be willing to stay dedicated to (hopefully) reach your ultimate goals.
pushedx is offline  
Thanks
2 Users
Old 10/31/2021, 22:11   #6
 
elite*gold: 0
Join Date: Apr 2012
Posts: 263
Received Thanks: 267

i have omitted the parts which i think are not necessary , but my client always get crash , can you help me


PHP Code:
std::string wchar2string(const wchar_tstr)
{
    
std::string mystring;
    while( *
str )
        
mystring += (char)*str++;
    return  
mystring;
}
bool __stdcall ShouldLoot(const wchar_tplayerName,uint32_t itemId,const wchar_titemName,const wchar_titemTypeName,uint32_t itemDropType)
{

    {

        static 
std::map<std::stringboolfilteredTypeIds;
        static 
std::map<std::stringboolfilteredNames;
        static 
bool loaded false;

        if (!
loaded)
        {
            try
            {
                
filteredTypeIds.insert(std::pair<std::stringbool>("ITEM_ETC_GOLD_01"true));
                
filteredTypeIds.insert(std::pair<std::stringbool>("ITEM_ETC_GOLD_02"true));
                
filteredTypeIds.insert(std::pair<std::stringbool>("ITEM_ETC_GOLD_03"true));
                
filteredTypeIds.insert(std::pair<std::stringbool>("ITEM_ETC_AMMO_BOLT_01"true));
                
filteredTypeIds.insert(std::pair<std::stringbool>("ITEM_ETC_AMMO_ARROW_01"true));
                
filteredNames.insert(std::pair<std::stringbool>("Jade tablet of strikes(Lvl.1)"true));
                
loaded true;
            }
            catch (const 
std::exceptionex)
            {
                
OutputDebugStringA(ex.what());
                return 
false;
            }
            catch (...) 
// ¯\_(ツ)_/¯
            
{
                
OutputDebugStringA("An unhandled exception occurred");
                return 
false;
            }
        }

        
// Check by type id
        
if (!filteredTypeIds.empty())
        {
            
std::string utf8 wchar2string(itemTypeName);
            static 
std::map<std::stringbool>::iterator itr filteredTypeIds.find(utf8);
            if (
itr != filteredTypeIds.end())
            {
                return 
itr->second;
            }
        }

        if (!
filteredNames.empty())
        {
            
std::string utf8 =wchar2string(itemName);
            static 
std::map<std::stringbool>::iterator itr filteredNames.find(utf8);
            if (
itr != filteredNames.end())
            {
                return 
itr->second;
            }
        }
    }
    


thaidu0ngpr0 is offline  
Old 11/01/2021, 02:19   #7

 
elite*gold: 260
Join Date: Aug 2008
Posts: 560
Received Thanks: 3,751
Quote:
Originally Posted by thaidu0ngpr0 View Post
i have omitted the parts which i think are not necessary , but my client always get crash , can you help me
What are you actually JMP'ing into in your first screenshot? The address the JMP points to is not shown in that second screenshot. If you dynamically load your DLL, you'll have to dynamically obtain the function address like I do to know where to call it. That doesnt seem like a problem since your code execution landed in your DLL, but I'll point it out anyways.

I'd start with that, a JMP is a forceful transfer of execution, with no register or stack changes other than EIP, so if you've made a C++ function in a DLL, and are trying to JMP directly into it, you'll crash because C++ functions expect to be called a certain way. Typically, you'd have a naked C++ function you jmp to, which then calls your normal C++ function inside. This sort of proxy function is how a lot of API hooking works.

If you look at my codecave in the client that jmps into my dll:
Quote:
008AF84F | E9 ACF88B00 | jmp [3][pet_filter]sro_client.116F100 |
Then look at the codecave itself (notice the address 0x116F100 matches)
Quote:
0116F100 | 60 | pushad |
0116F101 | 9C | pushfd |
0116F102 | 833D 00F01601 00 | cmp dword ptr [0x116F000], 0x0 |
0116F109 | 75 32 | jne 0x116F13D |
...
0116F1C3 | A1 04F01601 | mov eax, dword ptr [<sub_116F004>] |
0116F1C8 | FFD0 | call eax |
0116F1CA | 3C 00 | cmp al, 0x0 |
0116F1CC | 0F84 4DFFFFFF | je 0x116F11F |
0116F1D2 | 9D | popfd |
0116F1D3 | 61 | popad |
0116F1D4 | E9 D10674FF | jmp <[3][pet_filter]sro_client.sub_8AF8AA> |
All that code is doing something like this in C++:
Code:
HMODULE g_hDLL;
FARPROC g_ShouldLoot;
..
if (!g_hDLL)
{
   g_hDLL = LoadLibraryA("BasicPetFilter.Backend.dll");
   if (!g_hDLL)
      return false;
   g_ShouldLoot = GetProcAddress(g_hDLL, "ShouldLoot");
   if (!g_ShouldLoot )
      return false;
}
if (!g_ShouldLoot( .. stuff from client ..))
   return false;

return true; // Although not quite return true, since it jumps to a different location to resume than where return false jmps to
So if you forgot about the exact way I did things, what you'd want to do is:
- Get an address to your ShouldLoot function
- Build the function call by getting all the info like I do from the client and pushing it into the stack in the right order
- Call your function and process the result to return to either the loop to check other items or the "should loot item" client code

See if that helps any, you might want to try to replicate my assembly as well (although it'd be in a different address in your client, so you'd have to carefully fix all the addresses) because you can call LoadLibrary on a DLL already loaded, you'll just get a handled to the existing DLL and increment it's reference count. As long as you then exported the ShouldLoot function correctly, GetProcAddress will find it, and the call will be properly executed like mine is because I build the call stack right.

Worse case, start simpler, and just get the client to call your hooked function (with no parameters, so no call building in asm) and make sure that works, then slowly build it up. This stuff is pretty niche and complicated as it's easy to mess up very tiny details expedically when transferring execution across modules. The way your C++ DLL is compiled can matter too, so make sure to use Release to avoid any corruption from debug mode as well.
pushedx is offline  
Old 11/01/2021, 09:09   #8
 
elite*gold: 0
Join Date: Apr 2012
Posts: 263
Received Thanks: 267
Quote:
Originally Posted by pushedx View Post
What are you actually JMP'ing into in your first screenshot? The address the JMP points to is not shown in that second screenshot. If you dynamically load your DLL, you'll have to dynamically obtain the function address like I do to know where to call it. That doesnt seem like a problem since your code execution landed in your DLL, but I'll point it out anyways.

I'd start with that, a JMP is a forceful transfer of execution, with no register or stack changes other than EIP, so if you've made a C++ function in a DLL, and are trying to JMP directly into it, you'll crash because C++ functions expect to be called a certain way. Typically, you'd have a naked C++ function you jmp to, which then calls your normal C++ function inside. This sort of proxy function is how a lot of API hooking works.

If you look at my codecave in the client that jmps into my dll:


Then look at the codecave itself (notice the address 0x116F100 matches)


All that code is doing something like this in C++:
Code:
HMODULE g_hDLL;
FARPROC g_ShouldLoot;
..
if (!g_hDLL)
{
   g_hDLL = LoadLibraryA("BasicPetFilter.Backend.dll");
   if (!g_hDLL)
      return false;
   g_ShouldLoot = GetProcAddress(g_hDLL, "ShouldLoot");
   if (!g_ShouldLoot )
      return false;
}
if (!g_ShouldLoot( .. stuff from client ..))
   return false;

return true; // Although not quite return true, since it jumps to a different location to resume than where return false jmps to
So if you forgot about the exact way I did things, what you'd want to do is:
- Get an address to your ShouldLoot function
- Build the function call by getting all the info like I do from the client and pushing it into the stack in the right order
- Call your function and process the result to return to either the loop to check other items or the "should loot item" client code

See if that helps any, you might want to try to replicate my assembly as well (although it'd be in a different address in your client, so you'd have to carefully fix all the addresses) because you can call LoadLibrary on a DLL already loaded, you'll just get a handled to the existing DLL and increment it's reference count. As long as you then exported the ShouldLoot function correctly, GetProcAddress will find it, and the call will be properly executed like mine is because I build the call stack right.

Worse case, start simpler, and just get the client to call your hooked function (with no parameters, so no call building in asm) and make sure that works, then slowly build it up. This stuff is pretty niche and complicated as it's easy to mess up very tiny details expedically when transferring execution across modules. The way your C++ DLL is compiled can matter too, so make sure to use Release to avoid any corruption from debug mode as well.
I will try it tonight. Can you post a tutorial on how to get the character's skill info stored in the _reskill database by memory? like skill id, skill name that the character owns
thaidu0ngpr0 is offline  
Old 11/11/2021, 19:52   #9
 
elite*gold: 0
Join Date: Apr 2012
Posts: 263
Received Thanks: 267
the most important part is how to get drop item info I don't see you talking about
thaidu0ngpr0 is offline  
Old 11/17/2021, 19:57   #10
 
elite*gold: 0
Join Date: Apr 2012
Posts: 263
Received Thanks: 267


thanks ! it work perfectly
thaidu0ngpr0 is offline  
Old 02/09/2022, 14:05   #11
 
Lykie*'s Avatar
 
elite*gold: 0
Join Date: Jan 2020
Posts: 132
Received Thanks: 195
Thats alot of effort for real.
Lykie* is offline  
Old 04/03/2022, 05:09   #12
 
elite*gold: 0
Join Date: Oct 2011
Posts: 13
Received Thanks: 1
Can i have your discord? want to ask you about some stuff on private it's silkroad related thanks in advance
NONONO2 is offline  
Old 07/15/2022, 01:29   #13
 
Eckoro's Avatar
 
elite*gold: 20
Join Date: Apr 2008
Posts: 2,643
Received Thanks: 2,326
Just to say good effort writing these guides still, even though the audience for them might be smaller now. I’m just making my first post in a few years after my periodic forum lookup!

Definitely if the day ever comes I decide to pickup coding and/or Assembly as a hobby I will refer back to these. Best way to learn is with something you already know. Still a lot of this game is imprinted in my memory - I spent many hours in the UI of your packet sniffer.
Eckoro is offline  
Thanks
1 User
Old 11/13/2022, 13:25   #14
 
elite*gold: 0
Join Date: Dec 2021
Posts: 14
Received Thanks: 20
thats the reversed of the pickup func
Code:
//
// Created by Kurama on 9/19/2022.
//
#pragma once

#include "BSLib/_internal/custom_stl.h"

#include "Source/GlobalHelpersThatHaveNoHomeYet.h"

#include "d3d9types.h"

#include <Test/Test.h>

enum DROP_ITEM_TYPE_FLAG : int {
    DROP_ITEM_TYPE_FLAG_GOLD = 8,
    DROP_ITEM_TYPE_FLAG_GOLD_EQUIPITEM = 0x10,
    DROP_ITEM_TYPE_FLAG_ARCHEMY = 0x20,
    DROP_ITEM_TYPE_FLAG_AMMUNITION = 0x40,
    DROP_ITEM_TYPE_FLAG_TRADEGOODS = 0x80
};

struct stDropItemData {
public:
    int m_nRemainingItemTime; //0x0000
    DROP_ITEM_TYPE_FLAG m_nDroppedItemType; //0x0004
    uregion m_wRegionId; //0x0008
    D3DVECTOR m_d3dvLocation; //0x000C
    char pad_0018[4]; //0x0018
private:
BEGIN_FIXTURE()
        ENSURE_SIZE(0x1c)
        ENSURE_OFFSET(m_nRemainingItemTime, 0x0000)
        ENSURE_OFFSET(m_nDroppedItemType, 0x0004)
        ENSURE_OFFSET(m_wRegionId, 0x0008)
        ENSURE_OFFSET(m_d3dvLocation, 0x000C)
    END_FIXTURE()

    RUN_FIXTURE(stDropItemData)
};
Code:
//
// Created by Kurama on 9/20/2022.
//
#pragma once

#include "DropItemData.h"

#define g_CDropItemMgr (*(CDropItemMananger *) 0x0103639c)

class CDropItemManager {
public:
    virtual ~CDropItemManager() = 0;

public:
    const stDropItemData *GetDroppedItemData(DWORD dwGID);

    DWORD OnPickItemAbbilty(BYTE btPickupFlag, WORD wPetRegionId, D3DVECTOR *pd3dvPetPos, DWORD *dwOutItemGID);

private:
    std::n_map<DWORD, stDropItemData *> m_mapItemDropData; //0x0004
private:
BEGIN_FIXTURE()
        ENSURE_SIZE(0x10)
        ENSURE_OFFSET(m_mapItemDropData, 0x0004)
    END_FIXTURE()

    RUN_FIXTURE(CDropItemManager)
};
Code:
DWORD CDropItemManager::OnPickItemAbbilty(BYTE btPickupFlag, WORD wPetRegionId, D3DVECTOR *pd3dvPetPos, DWORD *dwOutItemGID) {
    std::n_map<DWORD, stDropItemData *>::const_iterator it = m_mapItemDropData.begin();
    for (; it != m_mapItemDropData.end(); it++) {
        const stDropItemData *pItemData = it->second;

        // there is checking for the pet mask flags and there is checking too the disctance bettween the item and the pet, check too if the entity "CItem"
        if (GetEntityObjectPtr(it->first) == NULL ||
            // Ex: make the pet only pick : Arrows
            pItemData->m_nDroppedItemType != DROP_ITEM_TYPE_FLAG_AMMUNITION)
            continue;

        *dwOutItemGID = it->first;

        return TRUE;
    }
    return FALSE;
}
kyuubi09 is offline  
Thanks
6 Users
Reply


Similar Threads Similar Threads
[GF][eXLib] - OpenBot | Level/Teleport/Pickup/Pickup
04/03/2024 - Metin2 Hacks, Bots, Cheats, Exploits & Macros - 490 Replies
Hi everyone, I present you the OpenBot. This is a project that i have been working on for the past year in my free time alongside the eXLib Module. Was made using my eXLib Module and is completely made in Python and open source. It was also created from m2kmod, you can see some similarity in the UI, but the core features were completely rebuild. Features: PathFinding (Even across maps) WaitDmg (Including bow)
Pickup bot mit großem pickup-Umkreis
11/17/2009 - Metin2 - 1 Replies
Ich habe im Metin2 forum gelesen, dass es einen neuen "Pickup_Bot" gibt. Mithilfe diesem Hack kannst du Items, die nicht in deiner Reichweite sind, aufheben. Giebt es sowas wirklich, oder haben die metin2 GMs sich das nur ausgedacht? (es gibt ja den Trick, um etwas mit Speedhack aufzuheben, obwohl es aussieht als wärest du noch ausser Reichweite...) Meinen die damit den Speedhack Trick? Oder gibt es wirklich so einen wunderbaren Hack?



All times are GMT +2. The time now is 10:21.


Powered by vBulletin®
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2024 elitepvpers All Rights Reserved.