我想为一款不受欢迎的游戏制作作弊。每次游戏调用WSASend函数时,在lpBuffers变量中都有一个一定长度的缓冲区。我需要这样做,当按住我的侧边鼠标按钮时,缓冲区被写入某种数组。然后,当按钮被释放时,检查我的数组中是否有内容,如果有,则逐个发送每个缓冲区。作弊的本质是,当快速发送记录的数据时,我的角色会突然从一个点移动到另一个点,或者非常快速地执行一些其他动作。我写了下面的代码,但是它不能正常工作。
std::vector<std::string> records;
WSABUF buffer;
/*
The WSASend function leads to __WSASend. The original WSASend function is in _WSASend.
*/
int WSAAPI __WSASend(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
if (GetAsyncKeyState(VK_XBUTTON1) < 0) // If the side mouse button is pressed when calling WSASend, write the buffer data to my array of strings
{
records.emplace_back(lpBuffers->buf);
/*
While the mouse button is held down, the game will think that the data was sent because * lpNumberOfBytesSent = lpBuffers-> len; This is necessary in order for the game to send new data.
*/
*lpNumberOfBytesSent = lpBuffers->len;
return 0;
}
/* I need to send data with a delay, but I cannot use the Sleep () function since WSASend and WSARecv are on the same thread. Since the game tries to send the same data if it just returns 0 (without setting * lpNumberOfBytesSent = lpBuffers-> len) I decided to take advantage of this. */
if (records.size() > 0)
{
buffer.buf = &records[0][0];
buffer.len = records[0].length();
_WSASend(s, &buffer, 1, 0, 0, 0, 0);
records.erase(records.begin());
/* No delay because I decided to check if it works at all. And as it turned out, it works, but very rarely. When the saved data was small - 50 to 50 that will go and everything will be ok, but when there is a lot, it always kicks from the server with a data read error. */
return 0; // I am returning 0 without setting * lpNumberOfBytesSent = lpBuffers-> len; so that the game calls the same function again with the same data to send next recorded buffer data
}
return _WSASend(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpOverlapped, lpCompletionRoutine); // When there is no data recorded
}
以正确的顺序(先记录到最后)写入和发送数据是非常重要的,因为游戏在两边都使用加密+计数器。从理论上讲,如果你正确地记录并以正确的顺序发送,然后允许游戏继续发送未记录的数据,那么一切都将顺利进行。但就像我说的,有时它适用于小数据,但不适用于大数据。我正在从服务器抛出一个读取错误。
我发现你的代码有很多问题。
-
当鼠标按钮为DOWN时,在将呼叫方的新数据添加到列表中时,您完全忽略了
WSASend()
的dwBufferCount
参数。如果呼叫方试图同时发送多个WSABUF
,将导致数据丢失。 -
您根本没有将
lpBuffers->len
保存到您的列表中,您假设lpBuffers->buf
是一个以null结尾的字符串,这很可能不是99.99999%的情况。所以,你的代码发送你的列表没有办法知道适当的长度给_WSASend()
。 -
您在发送列表中的数据时完全忽略了
_WSASend()
的输出。 -
当鼠标不按下时,如果你的列表不是空的,你只发送列表中的第一条记录,并且完全忽略调用者的新数据,永远丢失它。您需要:
-
如果调用
_WSASend()
后列表不为空,则将调用方的新数据附加到列表末尾。 -
发送列表中的所有内容,然后将调用者的新数据发送给
_WSASend()
。
-
说了这么多,试试这样做:
int WSAAPI __WSASend(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
*lpNumberOfBytesSent = 0;
if (GetAsyncKeyState(VK_XBUTTON1) < 0)
{
for(DWORD i = 0; i < dwBufferCount; ++i)
{
auto &buffer = lpBuffers[i];
records.emplace_back(buffer.buf, buffer.len);
*lpNumberOfBytesSent += buffer.len;
}
return 0;
}
if (!records.empty())
{
WSABUF buffer;
DWORD numSent;
auto &rec = records[0];
do
{
buffer.buf = rec.data();
buffer.len = rec.size();
if (_WSASend(s, &buffer, 1, &numSent, 0, nullptr, nullptr) == SOCKET_ERROR)
return SOCKET_ERROR;
rec.erase(0, numSent);
}
while (!rec.empty());
records.erase(records.begin());
return 0;
}
return _WSASend(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpOverlapped, lpCompletionRoutine);
}
或:
int WSAAPI __WSASend(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
*lpNumberOfBytesSent = 0;
if (GetAsyncKeyState(VK_XBUTTON1) < 0)
{
for(DWORD i = 0; i < dwBufferCount; ++i)
{
auto &buffer = lpBuffers[i];
records.emplace_back(buffer.buf, buffer.len);
*lpNumberOfBytesSent += buffer.len;
}
return 0;
}
while (!records.empty())
{
WSABUF buffer;
DWORD numSent;
auto &rec = records[0];
do
{
buffer.buf = rec.data();
buffer.len = rec.size();
if (_WSASend(s, &buffer, 1, &numSent, 0, nullptr, nullptr) == SOCKET_ERROR)
return SOCKET_ERROR;
rec.erase(0, numSent);
}
while (!rec.empty());
records.erase(records.begin());
}
return _WSASend(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpOverlapped, lpCompletionRoutine);
}