Discussione Hook Send e Recv (Leggere i pacchetti tra client e server)

Hastro

Utente Emerald
14 Febbraio 2012
423
60
171
355
Salve,

Nel corso della mia carriera da sviluppatore e da videogiocatore, mi sono ritrovato spesso a dovermi interfacciare con cheat che includevano l'edit di alcuni pacchetti scambiati tra client e server, così da exploitare alcune funzioni: un esempio comune è l'integer exploit (overflow/underflow).

Intrigato da questo argomento, stavo pensando di scrivere una dll, che se injectata mi consentisse di "sviare" il normale corso di "send" e "recv" così da leggere il traffico di pacchetti e gestirlo come meglio credo (al netto di crittazioni di cui sono già a conoscenza), e per l'occasione, ho deciso di affidarmi direttamente alla libreria Microsoft Detour la quale dovrebbe consentirmi di eseguire l'hook delle suddette funzioni, senza dovermi scervellare più di tanto.

Ebbene, sono riuscito ad includere l'header nel progetto senza troppi problemi, ma sto avendo alcuni problemi con la compilazione.... vi mostro il codice!

CODE:
C++:
#include <windows.h>
#include <fstream>                                                                                                        // Required to output logs to files
#include <iomanip>                                                                                                        // Required to display the hex properly
#include "pch.h"

// Detours: https://www.microsoft.com/en-us/research/project/detours/
#include "E:\Informatica\Detours-4.0.1\src\detours.h"                                                                    // Version 3.0 use for this hook. Be sure to include the library and includes to your project in visual studio


#pragma comment(lib,"detours.lib")                                                                                        // Need to include this so we can use Detours
#pragma comment(lib,"ws2_32.lib")                                                                                        // Required to hook Send and Recv since they both reside in this library


extern "C"
{                                                                                                                        // Pointers to the original functions
    int (WINAPI* originalSend)(SOCKET s, const char* buf, int len, int flags) = send;                                    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms740149(v=vs.85).aspx
    int (WINAPI* originalRecv)(SOCKET s, char* buf, int len, int flags) = recv;                                            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms740121(v=vs.85).aspx
}
HMODULE hModule;

std::ofstream sendLog;
std::ofstream recvLog;

int WINAPI newSend(SOCKET s, char* buf, int len, int flags)                                                                // Dumps each buffer to a new line in the "send.txt" file in the games directory
{
    sendLog.open("send.txt", std::ios::app);                                                                            // Opens a handle to the send file
    for (int i = 0; i < len; i++) {                                                                                        // For each byte:
        sendLog << std::hex << std::setfill('0') << std::setw(2) << (unsigned int)(unsigned char)buf[i] << " ";            //        Log the hex of the byte with a width of 2 (leading 0 added if necessary) and a space after to separate bytes
    }
    sendLog << std::endl;                                                                                                // Add a newline to the text file, indicating the end of this request
    sendLog.close();                                                                                                    // Close the text file
    return originalSend(s, buf, len, flags);                                                                            // Send the buffer to the original send function
}

int WINAPI newRecv(SOCKET s, char* buf, int len, int flags)                                                                // Dumps each buffer to a new line in the "recv.txt" file in the games directory
{
    len = originalRecv(s, buf, len, flags);                                                                                // Send the request with a pointer to the buffer for recv to store the response
    recvLog.open("recv.txt", std::ios::app);                                                                            // Opens a handle to the recv file

    for (int i = 0; i < len; i++)                                                                                        // For each byte in the response:
    {                                                                                       
        recvLog << std::hex << std::setfill('0') << std::setw(2) << (unsigned int)buf[i] << " ";                        // Log the hex of the byte with a width of 2 (leading 0 added if necessary) and a space after to separate bytes
    }
    recvLog << std::endl;                                                                                                // Add a newline to the text file, indicating the end of this request
    recvLog.close();                                                                                                    // Close the text file

    return len;                                                                                                            // Returns the output from the original recv call
}


void hook()                                                                                                             // Basic detours
{                                                                                                           
    DisableThreadLibraryCalls(hModule);
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&(PVOID&)originalSend, newSend);
    DetourAttach(&(PVOID&)originalRecv, newRecv);
    DetourTransactionCommit();
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD Reason, LPVOID reserved)
{
    switch (Reason)
    {
    case DLL_PROCESS_ATTACH:
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)hook, NULL, 0, NULL);
        break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

Purtroppo è uno dei miei primi progetti in C++ e quasi sicuramente avrò sbagliato qualcosa, allego anche la lista degli errori che il compilatore mi da:
1647282075241.png


Il problema fondamentale, è che l'IDE non mostra alcun segno rosso se tento di cliccare sull'errore, "come se in verità la sintassi fosse corretta, ma fosse sbagliata la semantica", allego un esempio:
1647282221572.png
 

Kode

Utente Emerald
10 Dicembre 2013
1,225
80
369
623
Non so quale IDE stai utilizzando, sembra IntelliJ o Eclipse, fatto sta che credo che non hai configurato correttamente l'ambiente C++ oppure hai qualche problema di configurazione su G++. Fatto sta che potresti risolvere tranquillamente i problemi di visualizzazione degli errori di compilazione se usassi Visual Studio Code con estensioni C++ o IDE simili più "minimal".

Ovviamente è una mia opinione, nel caso tu non voglia prova a vedere un pò se ci sono problemi nel build path della configurazione del tuo IDE.
 

CrazyMonk

Utente Electrum
24 Dicembre 2021
421
14
212
159
Non so quale IDE stai utilizzando, sembra IntelliJ o Eclipse, fatto sta che credo che non hai configurato correttamente l'ambiente C++ oppure hai qualche problema di configurazione su G++. Fatto sta che potresti risolvere tranquillamente i problemi di visualizzazione degli errori di compilazione se usassi Visual Studio Code con estensioni C++ o IDE simili più "minimal".

Ovviamente è una mia opinione, nel caso tu non voglia prova a vedere un pò se ci sono problemi nel build path della configurazione del tuo IDE.
Anche Sublime Text e Notepad++ non sono male
 

Hastro

Utente Emerald
14 Febbraio 2012
423
60
171
355
Hey code! Grazie per la risposta, ho inserito l'header, ma restano sempre gli stessi :/ (ho anche migrato il progetto in una nuova soluzione)

1647305855800.png
 

CrazyMonk

Utente Electrum
24 Dicembre 2021
421
14
212
159
Non vorrei dire cazzate...non ho mai usato queste funzionalità in C++, ma sembrerebbe che tu non abbia dichiarato il tipo di socket: solitamente si deve inizializzare con un record, poi bisogna passargli il server/client con cui deve comunicare e anche la porta. Nel tuo codice, invece, non c'è nessuna di queste operazioni.
 

JunkCoder

Moderatore
5 Giugno 2020
1,065
20
906
458
Hey code! Grazie per la risposta, ho inserito l'header, ma restano sempre gli stessi :/ (ho anche migrato il progetto in una nuova soluzione)

Allora è chiaro, non ti trova nemmeno ofstream in <fstream>, il problema è il precompiled-header. Devi includerlo per primo altrimenti VS farà casino, questo genere di cose è il motivo per cui preferisco evitarli. Prova con:

Codice:
#include "pch.h"
#include <windows.h>
#include <WinSock2.h>
#include <fstream>                                                                                                        // Required to output logs to files
#include <iomanip>                                                                                                        // Required to display the hex properly

SOCKET dovrebbe essere definito come typedef UINT_PTR SOCKET; in winsock.
 

Hastro

Utente Emerald
14 Febbraio 2012
423
60
171
355
Ultima modifica:
Non vorrei dire cazzate...non ho mai usato queste funzionalità in C++, ma sembrerebbe che tu non abbia dichiarato il tipo di socket: solitamente si deve inizializzare con un record, poi bisogna passargli il server/client con cui deve comunicare e anche la porta. Nel tuo codice, invece, non c'è nessuna di queste operazioni.
Esatto! È la stessa identica cosa che ho pensato anch'io ... ma poi mi son detto, se injecto la DLL per sovrascrivere la funzione recv/send e poi faccio tornare il flusso di codice a quello originale, non dovrebbe essere un problema: la dll è injectata, quando la funzione send/recv viene chiamata il flusso viene unicamente deviato "temporaneamente" per poi tornare a quello originale, i parametri sono forniti direttamente dall'applicazione vittima!

@JunkCoder Grazie mille, sei una garanzia come sempre! Sono riuscito a risolvere il "misfatto", tuttavia, l'applicazione mi disconnette quando la dll viene injectata. Il gioco "è mio", ho il codice sorgente e non ci sono protezioni!
1647337745411.png


UPDATE:
Ho trovato i due fogli di testo, e qualcosa comunque c'è!
1647338468185.png
 
  • Mi piace
Reazioni: CrazyMonk

JunkCoder

Moderatore
5 Giugno 2020
1,065
20
906
458
Esatto! È la stessa identica cosa che ho pensato anch'io ... ma poi mi son detto, se injecto la DLL per sovrascrivere la funzione recv/send e poi faccio tornare il flusso di codice a quello originale, non dovrebbe essere un problema: la dll è injectata, quando la funzione send/recv viene chiamata il flusso viene unicamente deviato "temporaneamente" per poi tornare a quello originale, i parametri sono forniti direttamente dall'applicazione vittima!

@JunkCoder Grazie mille, sei una garanzia come sempre! Sono riuscito a risolvere il "misfatto", tuttavia, l'applicazione mi disconnette quando la dll viene injectata. Il gioco "è mio", ho il codice sorgente e non ci sono protezioni!


UPDATE:
Ho trovato i due fogli di testo, e qualcosa comunque c'è!

Ho notato un paio di problemi: nella DllMain usi CreateThread sulla funzione hook, ma usando il cast esplicito non ti sei accorto che la signature di hook non coincide con quella che dovrebbe essere cioè DWORD WINAPI hook(LPVOID args), con la calling convention e i parametri sbagliati rischi di corrompere lo stack e le conseguenze sono difficili da calcolare. Sostituisci la dichiarazione di hook, togli il cast a LPTHREAD_START_ROUTINE quando la chiami e sposta DisableThreadLibraryCalls(hModule); fuori da hook alla DllMain, prima della CreateThread.

Inoltre potresti avere una race-condition quando usi il metodo open dell'ofstream in quanto se il file è bloccato da un thread che fa send o recv, un altro thread riceverà un eccezione C++, se la catcha la chiamata fallisce e il software gestirà l'evento, altrimenti crasha il programma. Sarebbe meglio scrivere tutto in una std::string in memoria e scriverla su disco solo periodicamente da un altro thread nella tua dll (accedi alla std::string da dentro una critical section).
 
  • Mi piace
Reazioni: DanyDollaro

Hastro

Utente Emerald
14 Febbraio 2012
423
60
171
355
Ultima modifica:
Ho notato un paio di problemi: nella DllMain usi CreateThread sulla funzione hook, ma usando il cast esplicito non ti sei accorto che la signature di hook non coincide con quella che dovrebbe essere cioè DWORD WINAPI hook(LPVOID args), con la calling convention e i parametri sbagliati rischi di corrompere lo stack e le conseguenze sono difficili da calcolare. Sostituisci la dichiarazione di hook, togli il cast a LPTHREAD_START_ROUTINE quando la chiami e sposta DisableThreadLibraryCalls(hModule); fuori da hook alla DllMain, prima della CreateThread.

Inoltre potresti avere una race-condition quando usi il metodo open dell'ofstream in quanto se il file è bloccato da un thread che fa send o recv, un altro thread riceverà un eccezione C++, se la catcha la chiamata fallisce e il software gestirà l'evento, altrimenti crasha il programma. Sarebbe meglio scrivere tutto in una std::string in memoria e scriverla su disco solo periodicamente da un altro thread nella tua dll (accedi alla std::string da dentro una critical section).
Grazie mille per la svista Junk! Sto imparando davvero moltissimo! Allora, sono riuscito a sistemare praticamente tutto, ho spostato DisableThreadLibraryCalls(hModule); esattamente sotto case DLL_PROCESS_ATTACH:!

Ho eliminato la parte in cui si scrive su file, tornerò alla programmazione concorrenziale appena ho più tempo, per ora mi sono limitato a "spaware" una console e a sfruttare il ciclo per scrivere direttamente su di essa (cosa decisamente più semplice). Il problema è che il server continua a darmi timeout: ogni qualvolta injecto la dll, è come se mi disconnettessi dal server, tuttavia il primo pacchetto viene comunque letto (e il client NON crasha) ... che ci sia un errore nel valore di ritorno? (In figura il pacchetto che viene inviato con la funzione send in fase di login)
1647370277957.png


Aggiorno il codice sorgente per chiarezza:
C++:
#include "pch.h"
#include <windows.h>
#include <WinSock2.h>
#include <fstream>                                                                                                        // Required to output logs to files
#include <iomanip>                                                                                                        // Required to display the hex properly
#include <iostream> //cout ...

// Detours: https://www.microsoft.com/en-us/research/project/detours/
#include "E:\Informatica\Detours-4.0.1\src\detours.h"                                                                    // Version 3.0 use for this hook. Be sure to include the library and includes to your project in visual studio

#pragma comment(lib,"detours.lib")                                                                                        // Need to include this so we can use Detours
#pragma comment(lib,"ws2_32.lib")                                                                                        // Required to hook Send and Recv since they both reside in this library

                                                                                                                        // Pointers to the original functions
static int (WINAPI* originalSend)(SOCKET s, const char* buf, int len, int flags) = send;                                // https://msdn.microsoft.com/en-us/library/windows/desktop/ms740149(v=vs.85).aspx
static int (WINAPI* originalRecv)(SOCKET s, char* buf, int len, int flags) = recv;                                        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms740121(v=vs.85).aspx


typedef UINT_PTR SOCKET; // Socket redefinition

HMODULE hModule;
std::ofstream sendLog;
std::ofstream recvLog;

bool CreateConsole()
{
    FILE* file = NULL;

    SetConsoleTitleA("Pack Viewer [ALPHA]");

    if (!AllocConsole())
        return false;

    if (freopen_s(&file, "CONIN$", "r", stdin))
        return false;

    if (freopen_s(&file, "CONOUT$", "w", stdout))
        return false;

    if (freopen_s(&file, "CONERR$", "w", stderr))
        return false;
}

int WINAPI newSend(SOCKET s, char* buf, int len, int flags)                                                                // Dumps each buffer to a new line in the "send.txt" file in the games directory
{
    //sendLog.open("send.txt", std::ios::app);                                                                            // Opens a handle to the send file
    printf("> SEND:\n");
    for (int i = 0; i < len; i++)
    {                                                                                                                    // For each byte:
        //sendLog << std::hex << std::setfill('0') << std::setw(2) << (unsigned int)(unsigned char)buf[i] << " ";        // Log the hex of the byte with a width of 2 (leading 0 added if necessary) and a space after to separate bytes
        std::cout << std::hex << std::setfill('0') << std::setw(2) << (unsigned int)(unsigned char)buf[i] << " ";
    }
    std::cout << std::endl;
    // sendLog << std::endl;                                                                                            // Add a newline to the text file, indicating the end of this request
    // sendLog.close();                                                                                                    // Close the text file
    return originalSend(s, buf, len, flags);                                                                            // Send the buffer to the original send function
    //return send(s,buf,len,flags);
}

int WINAPI newRecv(SOCKET s, char* buf, int len, int flags)                                                                // Dumps each buffer to a new line in the "recv.txt" file in the games directory
{
    len = originalRecv(s, buf, len, flags);                                                                                // Send the request with a pointer to the buffer for recv to store the response
    // recvLog.open("recv.txt", std::ios::app);                                                                            // Opens a handle to the recv file
    printf("> RECV:\n");
    for (int i = 0; i < len; i++)                                                                                        // For each byte in the response:
    {
        //recvLog << std::hex << std::setfill('0') << std::setw(2) << (unsigned int)buf[i] << " ";                        // Log the hex of the byte with a width of 2 (leading 0 added if necessary) and a space after to separate bytes
        std::cout << std::hex << std::setfill('0') << std::setw(2) << (unsigned int)(unsigned char)buf[i] << " ";
    }
    std::cout << std::endl;
    // recvLog << std::endl;                                                                                            // Add a newline to the text file, indicating the end of this request
    // recvLog.close();   
    //                                                                                                                     // Close the text file
    //return len; why len [original]???                                                                                    // Returns the output from the original recv call
    return originalRecv(s, buf, len, flags);
    //return recv(s,buf,len,flags);
}


DWORD WINAPI hook(LPVOID args) // Basic detours
{
    CreateConsole();
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&(PVOID&)originalSend, newSend);
    DetourAttach(&(PVOID&)originalRecv, newRecv);
    DetourTransactionCommit();
    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD Reason, LPVOID reserved)
{
    switch (Reason)
    {
    case DLL_PROCESS_ATTACH:
        DisableThreadLibraryCalls(hModule);
        CreateThread(NULL, 0, hook, NULL, 0, NULL);
        break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

(A proposito, che valore dovrebbe ritornare la funzione hook se da void la metto "DWORD"? )
Messaggio unito automaticamente:

Trovato l'errore, newRecv doveva ritornare "len" e non la funzione originale. Funziona perfettamente, pubblicherò qui su Inforge un packeditor generale non appena finisco di apportare alcune modifiche, grazie mille a tutti!
 
  • Mi piace
Reazioni: DanyDollaro

DanyDollaro

Utente Electrum
14 Luglio 2018
141
39
51
113
Per correttezza, ricordati di chiudere l'handle aperto dalla funzione CreateThread:
C++:
CloseHandle(CreateThread(NULL, 0, hook, NULL, 0, NULL));