Questo sito utilizza cookies, anche di terze parti, per mostrare pubblicità e servizi in linea con il tuo account. Leggi l'informativa sui cookies.
Username: Password: oppure
C# / VB.NET - Aiuto su struttura C++ da inviare tramite socket
Forum - C# / VB.NET - Aiuto su struttura C++ da inviare tramite socket

Avatar
Thejuster (Member)
Guru^2


Messaggi: 1879
Iscritto: 04/05/2008

Segnala al moderatore
Postato alle 23:06
Lunedì, 05/11/2018
Salve ragazzi ho un problema che non riesco a risolvere in nessun modo.
Spero che potete aiutarmi a trovare una soluzione.

Dunque ho la necessità di inviare una struttura tramite pacchetto TCP ad un server C++.

Il problema e che:
O mi crasha il programma , o il server non intercetta correttamente i valori ricevuti.

Parto dal principio.

Struttura C++

Codice sorgente - presumibilmente C++

  1. struct login_session_data {
  2.         uint32 account_id;                      ///also GID
  3.         long login_id1;
  4.         long login_id2;
  5.         char sex;                       /// 'F','M','S'
  6.  
  7.         char userid[NAME_LENGTH];       /// account name
  8.         char passwd[PASSWD_LENGTH]; // 23+1 for plaintext, 32+1 for md5-ed passwords
  9.         int passwdenc;                  /// was the passwd transmited encrypted or clear ?
  10.         char md5key[20];                /// md5 key of session (each connection could be encrypted with a md5 key)
  11.         uint16 md5keylen;               /// len of the md5 key
  12.  
  13.         char lastlogin[24];             ///date when last logged, Y-M-D HH:MM:SS
  14.         uint8 group_id;                 ///groupid of account
  15.         uint8 clienttype;               /// ???
  16.  
  17.         uint8 client_hash[16];          ///hash of client
  18.         int has_client_hash;            ///client ha sent an hash
  19.  
  20.         int fd;                         ///socket of client
  21. };





Funzione dove il server riceve il socket e ne ricava i valori

Codice sorgente - presumibilmente Delphi

  1. /**
  2.  * Received a connection request.
  3.  * @param fd: file descriptor to parse from (client)
  4.  * @param sd: client session
  5.  * @param command: packet type sent
  6.  * @param ip: ipv4 address (client)
  7.  *  S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B
  8.  *  S 0277 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B
  9.  *  S 02b0 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B <g_isGravityID>.B
  10.  *  S 01dd <version>.L <username>.24B <password hash>.16B <clienttype>.B
  11.  *  S 01fa <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.B(index of the connection in the clientinfo file (+10 if the command-line contains "pc"))
  12.  *  S 027c <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.13B(junk)
  13.  *  S 0825 <packetsize>.W <version>.L <clienttype>.B <userid>.24B <password>.27B <mac>.17B <ip>.15B <token>.(packetsize - 0x5C)B
  14.  * @param fd: fd to parse from (client fd)
  15.  * @return 0 failure, 1 success
  16.  */
  17. static int logclif_parse_reqauth(int fd, struct login_session_data *sd, int command, char* ip){
  18.         size_t packet_len = RFIFOREST(fd);
  19.  
  20.         if( (command == 0x0064 && packet_len < 55)
  21.         ||  (command == 0x0277 && packet_len < 84)
  22.         ||  (command == 0x02b0 && packet_len < 85)
  23.         ||  (command == 0x01dd && packet_len < 47)
  24.         ||  (command == 0x01fa && packet_len < 48)
  25.         ||  (command == 0x027c && packet_len < 60)
  26.         ||  (command == 0x0825 && (packet_len < 4 || packet_len < RFIFOW(fd, 2))) )
  27.                 return 0;
  28.         else {
  29.                 int result;
  30.                 char username[NAME_LENGTH];
  31.                 char password[PASSWD_LENGTH];
  32.                 unsigned char passhash[16];
  33.                 uint8 clienttype;
  34.                 bool israwpass = (command==0x0064 || command==0x0277 || command==0x02b0 || command == 0x0825);
  35.  
  36.                 // Shinryo: For the time being, just use token as password.
  37.                 if(command == 0x0825) {
  38.                         char *accname = RFIFOCP(fd, 9);
  39.                         char *token = RFIFOCP(fd, 0x5C);
  40.                         size_t uAccLen = strlen(accname);
  41.                         size_t uTokenLen = RFIFOREST(fd) - 0x5C;
  42.  
  43.                         if(uAccLen > NAME_LENGTH - 1 || uAccLen == 0 || uTokenLen > NAME_LENGTH - 1  || uTokenLen == 0)
  44.                         {
  45.                                 logclif_auth_failed(sd, 3);
  46.                                 return 0;
  47.                         }
  48.  
  49.                         safestrncpy(username, accname, uAccLen + 1);
  50.                         safestrncpy(password, token, uTokenLen + 1);
  51.                         clienttype = RFIFOB(fd, 8);
  52.                 }
  53.                 else
  54.                 {
  55.                         safestrncpy(username, RFIFOCP(fd,6), NAME_LENGTH);
  56.                         if( israwpass )
  57.                         {
  58.                                 safestrncpy(password, RFIFOCP(fd,30), PASSWD_LENGTH);
  59.                                 clienttype = RFIFOB(fd,54);
  60.                         }
  61.                         else
  62.                         {
  63.                                 memcpy(passhash, RFIFOP(fd,30), 16);
  64.                                 clienttype = RFIFOB(fd,46);
  65.                         }
  66.                 }
  67.                 RFIFOSKIP(fd,RFIFOREST(fd)); // assume no other packet was sent
  68.  
  69.                 sd->clienttype = clienttype;
  70.                 safestrncpy(sd->userid, username, NAME_LENGTH);
  71.                 if( israwpass )
  72.                 {
  73.                         ShowStatus("Request for connection of %s (ip: %s)\n", sd->userid, ip);
  74.                         safestrncpy(sd->passwd, password, NAME_LENGTH);
  75.                         if( login_config.use_md5_passwds )
  76.                                 MD5_String(sd->passwd, sd->passwd);
  77.                         sd->passwdenc = 0;
  78.                 }
  79.                 else
  80.                 {
  81.                         ShowStatus("Request for connection (passwdenc mode) of %s (ip: %s)\n", sd->userid, ip);
  82.                         bin2hex(sd->passwd, passhash, 16); // raw binary data here!
  83.                         sd->passwdenc = PASSWORDENC;
  84.                 }
  85.  
  86.                 if( sd->passwdenc != 0 && login_config.use_md5_passwds )
  87.                 {
  88.                         logclif_auth_failed(sd, 3); // send "rejected from server"
  89.                         return 0;
  90.                 }
  91.  
  92.                 result = login_mmo_auth(sd, false);
  93.  
  94.                 if( result == -1 )
  95.                         logclif_auth_ok(sd);
  96.                 else
  97.                         logclif_auth_failed(sd, result);
  98.         }
  99.         return 1;
  100. }



ora leggendo i commenti in questa funzione, esattamente questo che vado ad utilizzare:

*  S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B

Interpreto questa riga in questo modo.


0064  è il comando  quindi  essendo come parametro 'int command'  ho fatto  0x64.

<version> è 0   ma .L  non ho capito cos'è..

<username>  ho impostato come char[] username.

da codice

Codice sorgente - presumibilmente C# / VB.NET

  1. char username[NAME_LENGTH];



24B   Leggendo il sorgente posso intuire che 24B stia come 24byte di grandezza per il campo.
sia per username che per password.

Clienttype è 0 posso intuire "Forse" che B stia per byte, ma leggendo la struttura login_session_data
clienttype è impostato come  "uint8 clienttype;"

quindi ho scritto ushort.

Ecco la mia classe C#.

Codice sorgente - presumibilmente C++

  1. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
  2.     struct DataPacket
  3.     {
  4.  
  5.         public int command;  //Conterrà 0x64
  6.  
  7.         public long version;   //Conterrà 0
  8.  
  9.         //dichiaro la variabile array char nella forma nativa LPStr
  10.        //Il server chiede che la sequenza sia di 24byte quindi imposto
  11.        //la costante per la lunghezza della variabile di 24
  12.         [MarshalAs(UnmanagedType.LPStr, SizeConst = 24)]
  13.         public char[] Username;
  14.        
  15.         [MarshalAs(UnmanagedType.LPStr, SizeConst = 24)]
  16.         public char[] Password;
  17.  
  18.         //Tipo di client che conterrà 0
  19.         public ushort clienttype;
  20.  
  21.    
  22.         // La chiamata a questo metodo restituirà un array di byte con i contenuti
  23.         // della struttura pronta per essere inviata tramite tcp.
  24.         public byte[] Serialize()
  25.         {
  26.            
  27.            // alloco un array di byte per i dati struct
  28.             var buffer = new byte[Marshal.SizeOf(typeof(DataPacket))];
  29.  
  30.             // Assegna un GCHandle e ottiene il puntatore dell'array
  31.             var gch = GCHandle.Alloc(buffer, GCHandleType.Pinned);
  32.             var pBuffer = gch.AddrOfPinnedObject();
  33.  
  34.             // copia i dati da struct a array e sblocca il puntatore gc
  35.             Marshal.StructureToPtr(this, pBuffer, false);
  36.             gch.Free();
  37.  
  38.             return buffer;
  39.         }
  40.  
  41.  
  42.         // questo metodo deserializza un array di byte nella struct.
  43.         public void Deserialize(ref byte[] data)
  44.         {
  45.             var gch = GCHandle.Alloc(data, GCHandleType.Pinned);
  46.             this = (DataPacket)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(DataPacket));
  47.             gch.Free();
  48.         }
  49.     }




Dal main

1° Tentativo ( Sicuramente Sbagliato)
credo che l'errore sia qui

public char[] Username;
dichiarare così queste due variabili.


Codice sorgente - presumibilmente C# / VB.NET

  1. //S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B
  2.             DataPacket packet = new DataPacket();
  3.  
  4.             packet.command = 0x64;  Console.WriteLine(packet.command);
  5.             packet.version = 0;
  6.             packet.Username = String.Format("{0}", "Thejuster").ToCharArray();
  7.             packet.Password = String.Format("{0}", "123456789").ToCharArray();
  8.             packet.clienttype = 0;
  9.  
  10.             var bytes = packet.Serialize();
  11.  
  12.             soc.Send(bytes);



Testo quotato


Additional information: Impossibile effettuare il marshalling del tipo DataPacket come struttura non gestita. Non è possibile calcolare una dimensione o un offset significativo.




2° Tentativo modificando le varibili di tipo char[] in string
e cambiando da LPStr  in ByValTStr

Anche qui dubito fortemente che inviare una stringa sia la cosa corretta.
Ma questa volta non c'è crash.

codice
Codice sorgente - presumibilmente C# / VB.NET

  1. //S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B
  2.             DataPacket packet = new DataPacket();
  3.  
  4.             packet.command = 0x64;
  5.             Console.WriteLine(packet.command);
  6.             packet.version = 0;
  7.             packet.Username = "Thejuster";
  8.             packet.Password = "123456789";
  9.             packet.clienttype = 0;
  10.  
  11.             var bytes = packet.Serialize();
  12.  
  13.             soc.Send(bytes);



Ecco cosa dice il server

Login tramite mio test:

https://i.postimg.cc/qqypskgp/Cattura.png

Login Tramite client

https://i.postimg.cc/mrCVWJct/Cattura-PNG-5d92eb58c14f593f0 ...

Quando effettuo un login con un client corretto appare

[Status]: Request for connection of "Thejuster" (ip: 127.0.0.1)  esempio

mentre qui, e come se il valore della stringa non viene inviato o in un qualche modo si sballa tutto.

Ma ho notato un problema proprio ora.


noto chiaramente che la variabile bytes che contiene tutta la struttura serializzata,
misura 75  mentre guardando il sorgente del server

Codice sorgente - presumibilmente Plain Text

  1. if( (command == 0x0064 && packet_len < 55)



Deve essere < di 55

ecco la screen

https://i.postimg.cc/CLR11gwr/Cattura.png

Dove sbaglio?

Ultima modifica effettuata da Thejuster il 05/11/2018 alle 23:17


PM Quote
Avatar
TheDarkJuster (Member)
Guru^2


Messaggi: 1544
Iscritto: 27/09/2013

Segnala al moderatore
Postato alle 2:20
Mercoledì, 07/11/2018
Tu in pratica hai creato una struct in c# che dovrebbe riflettere la struttura del C per poterla serializzare in stile C.

C'è un piccolo problema: il compilatore C ha riordinato i campi e aggiunto del padding tra i campi, e il compilatore c# l'ha fatto in un altro modo, quindi le due strutture sono uguali sulla carta, ma completamente diverse fra loro, probabilmente non hanno niente in comune.

PM Quote
Avatar
Thejuster (Member)
Guru^2


Messaggi: 1879
Iscritto: 04/05/2008

Segnala al moderatore
Postato alle 9:11
Giovedì, 08/11/2018
mi sembra strano juster.
anche perché la stessa operazione che sto tentando di fare in c#
è stata fatta ed eseguita correttamente in javascript.


PM Quote
Avatar
TheDarkJuster (Member)
Guru^2


Messaggi: 1544
Iscritto: 27/09/2013

Segnala al moderatore
Postato alle 10:59
Giovedì, 08/11/2018
Si, è fattibilissimo in ogni linguaggio se conosci gli offset dei campi dati della struttura. Se vuoi puoi farlo anche in cobol o assembly, ma non otterrai niente se non capisci come sono disposti i dati.

PM Quote