[Linux] Journey of finding bSendPacket on the stack
This is more of an informational post on how to get things done in a similar situation and where I failed. I am releasing the process, because the process allows you to learn, not the expected result. If you just came here for a TL;DR it’s right here:
So first we need to understand what we are dealing with. Let’s take a look at an old aged source code of an Aimtux fork. Here’s what’s inside hooker.cpp:
bSendPacket = reinterpret_cast
And the sig is in hooker.h:
Okay, so we are dealing with a not really good thing – .text bytepatching. This basically asks Valve to give you a VAC ban. If you search for this pattern in IDA, you will end up on this line inside CL_Move:
This makes it evident of what the current way does. It modifies the value that bSendPacket gets set to. You can also take a look at the 2007 SDK to get an idea of what bSendPacket is intended for. I did not really know the reason why it existed in the first place before doing all of this. But wait, what else does CL_Move do? It also calls CreateMove, after setting bSendPacket:
So now we know exactly what happens: bSendPacket gets set, CreateMove gets called and then, if bSendPacket is not equal to 0, the move is sent, else, the current command gets stored to be sent out later on. Here comes the second catch of the original method: the change is not instant. When you set bSendPacket to whatever value you set it to, it is going to take a full tick until the value becomes effective.
So, on windows people have 2 other options, 1) Grab the offset from the stack and 2) Naked hook CreateMove, read bSendPacket from a register and pass it over to your hook. The second one not only already has too much of asm, but while I am not sure if Clang supports it, but GCC will definitely only support naked attribute on x86 platform in the upcoming GCC 8. So I will stick to the option number one.
Here comes the first real problem, I have never done this, nor I have a broad understanding how things work on a very low level.
So, let’s start from my dumbest try – LLDB attach to csgo, set a breakpoint inside our CreateMove, check whats the value of bSendPacket, and start printing the offset from the frame pointer (register RBP) from 0x1 to 0x5f and make educated guesses on which offset might be right. Fun huh? Not really, this will never lead to anywhere. And sure it didn’t.
Okay, so another try was to make my cheat print the values of each offset while flipping bSendPacket. This lead me to some really interesting results. So at first, I would check for value equality to bSendPacket, and I got these offsets that there valid when it was flipping between 0 and 1: 0x8 0x18 0x30, okay, so none of these worked, lets try to check for inequality, and the offsets are 0x8 0x18 0x30 (LMAO what?!). Of course we gotta do something else.
So, lets take a deeper look on our stuff. Stepping out of our hook (finish command in LLDB) leads us somewhere (CreateMove inheritance chain) inside client_client.so, finish the second time to land somewhere on client_client.so as well. Finish out of that and we land inside engine_client.so, and if we disassemble the function inside LLDB we sure land right after CreateMove call. Okay, if we scroll up to the place where bSendPacket is set (match it with IDA) we see that it is stored inside register r13d. I just set a breakpoint on the instruction to use later.
Okay, so skipping 20 minutes of wasted time, I finally got back on the right track. I set the bSendPacket register to a random number (like 420):
And then I would break on the cheat’s CreateMove hook and then print the offsets from *$rbp to check which one is correct. And, none of the offsets from 0x0 to 0x5f (or maybe more) was correct. So I got back to check what I did before. Remember that I had to exit out of 2 client_client.so subroutines to get back to CL_Move? So this is what I did, I tried to triple dereference rbp and then try the offsets again. Okay, it didn’t work, let’s try the double dereference, and sure enough, offset 0x1 was correct! First try! Kind of.
Just convert it to the code, grabbing rbp the same way people grab ebp on Windows, and you are done! This was it. This might help you in a similar situation, I don't know. But this was how I got bSendPacket from the stack.
Anyways, it's good to be back on the Linux side after a short 2 months break.