L2Bot icon indicating copy to clipboard operation
L2Bot copied to clipboard

PacketSend

Open dev-spoiler opened this issue 4 years ago • 4 comments

Hey, just got to PacketSend function and've got some question. Was that actually working?

char* format = (char*) buffer;
size_t formatLength = strlen(format);
auto pidx = formatLength + 1;

if (formatLength > MAX_PARAM) {
    // We can only handle at most 10 parameters
    return;
}

// Setup parameters buffer
for (size_t i = 0; i < formatLength; i++)
{
    parameters[i] = *(DWORD*)(buffer + pidx + i * 4);
}

Do not understand where I'm mistaken, but maybe you can clarify that.

Usual call in c++ is with next params:
[socket_info], [format], [opcode], [other call specific params]

Dont taking into account these two: [socket_info], [format]. They seem to be correct. socket_info is hardcoded and you get format from the base pointer to the buffer. Reading format as string allows you to find the zero code which is the start of rest of params.

But the further stuff I cant understand.

parameters[i] = *(DWORD*)(buffer + pidx + i * 4);

Next param is opcode and it is 1 byte length, but it seems for me like you are interpreting it as a dword (4 bytes). The next param could be a string with completely unpredictable length and it will be considered as dword as well. Is the way I'm thinking correct? Or what I'm missing in my understanding? Thanks

dev-spoiler avatar Dec 11 '21 22:12 dev-spoiler

Yes opcode is one byte length, but it is actually aligned on 4 bytes as you can see in this screenshot: https://user-images.githubusercontent.com/95100304/143779786-fa55b398-589a-48e4-8d9d-8064f27a1e2b.png 0x49 is written as 0x00000049 As for the strings, it is actually a pointer to a wide string (here L"Hello") which is 4 bytes long so everything's fine.

xarkes avatar Dec 11 '21 23:12 xarkes

Yeah I agree, that's how it is in the game memory. But if I'm not mistaken that code is about translating the BYTE buffer that comes from the GUI through the pipe. GUI app has its own address space, and from what I understand you can't pass a DWORD pointer to address in GUI app that will be used by game (which is different process with its own address space) to get a string by that pointer value. Or am I not correct? (Couldn't find any example from MainPlayer.cpp cause all actions implemented there dont use string params and they all are 4 bytes per param). Also now I see that you are probably aligning opcode to 8byte by using uint8_t for it. But anyway if the upper is correct and if we need to pass more than 8 bytes as a param (i.e. wide string) our option is to serialize it and add serialized bytes to buffer. And that will mean that buffer array will stop being 4 bytes per param. What do you think?

dev-spoiler avatar Dec 12 '21 09:12 dev-spoiler

Yeah I agree, that's how it is in the game memory. But if I'm not mistaken that code is about translating the BYTE buffer that comes from the GUI through the pipe. GUI app has its own address space, and from what I understand you can't pass a DWORD pointer to address in GUI app that will be used by game (which is different process with its own address space) to get a string by that pointer value. Or am I not correct? (Couldn't find any example from MainPlayer.cpp cause all actions implemented there dont use string params and they all are 4 bytes per param).

Oh yes you are 100% correct. The current logic works only because I didn't have to send packets which contain specific data such as strings. For now the GUI sends only the raw DWORD information, so if one of them is a pointer, it's going to break at some point :) https://github.com/xarkes/L2Bot/blob/c8bbef4fe17f6689dd02d7068352ac2067db6f9e/L2Bot/IPCSocket.cpp#L81-L83 You can confirm I only use packets with a c or d format specifier in https://github.com/xarkes/L2Bot/blob/c8bbef4fe17f6689dd02d7068352ac2067db6f9e/L2Bot/MainPlayer.cpp so everything is fine... for now. At some point if you want to introduce "S" format specifier, you would have to improve the communication logic between the DLL and the GUI so that the DLL actually allocates a string in the game's address space, and then refer to this pointer.

Also now I see that you are probably aligning opcode to 8byte by using uint8_t for it. But anyway if the upper is correct and if we need to pass more than 8 bytes as a param (i.e. wide string) our option is to serialize it and add serialized bytes to buffer. And that will mean that buffer array will stop being 4 bytes per param. What do you think?

I'm not sure what you are referring to when mentioning "aligning opcode". Anyways for the rest I think I answered above, if you want to introduce anything that is not c or d (let's say you want to automate the sending of messages in the game chat) you would have to add some serialization logic between the GUI and the DLL.

xarkes avatar Dec 12 '21 10:12 xarkes

I'm not sure what you are referring to when mentioning "aligning opcode".

Well, that's how I started to do things, serialization specifically (in dotnet app). I thought I could save few bytes on pipes communication using only 1 byte for opcode. And also I wasn't doing any aligning on the receiving side (game).

I'm not sure what you are referring to when mentioning "aligning opcode".

Well and again I messed up, just have googled uint8_t and it is 1 byte, I thought it was 4 for some reason (I thought using 4 bytes type made it aligned automatically)... melting brain.. Anyway.

add some serialization logic between the GUI and the DLL

Yeap, in progress of that. However I want to try to avoid allocating anything in the game address space as I'm not completely sure how it will be garbage collected, just dont want to get some allocation errors eventually. First I want to try doing all aligning work on the sending side in order to be able to use the raw buffer+offset as pointer to string. Dont know if it will work though. Also there is a challenge of correctly passing the params to send func as for the c and d we need to pass them by value and for the S by pointer. Trying to deal with that right now.

UPD: And that seems to work!

dev-spoiler avatar Dec 12 '21 10:12 dev-spoiler