Sicro hat geschrieben:Vielen Dank für deine aufgewendete Zeit und Mühe. Die direkte Vorgehensweise über die
WinAPI ist mir bekannt - so wird das wohl ablaufen. Anstelle von gethostbyname() wird
aber sicherlich seit der neuen Beta von PureBasic mit IPv6-Support getaddrinfo() verwendet.
Ich habe den Flag #PB_Network_IPv6 nicht übergeben und in dem Fall wird immer noch gethostbyname() verwendet. Mein Pseudocode ist aus dem disassemblierten Netzwerkcode von der Version 5.10 Beta 1 für Windows x86 (da OllyDbg kein 64 bit kann) entstanden und nicht anhand der normalen Vorgehensweise. Und dies sind 1:1 die API Befehle (mit den ausgelesen Parametern) die PureBasic verwendet. Hier ist auch noch mal die Import Tabelle von der erstellten Executable:
Code: Alles auswählen
DLL Name: WSOCK32.DLL
vma: Hint/Ord Member-Name Bound-To
41f0 0 closesocket
41fe 0 WSACleanup
420c 0 WSAStartup
421a 0 connect
4224 0 socket
422e 0 inet_addr
423a 0 gethostbyname
424a 0 htons
4252 0 bind
425a 0 ioctlsocket
4268 0 select
4272 0 __WSAFDIsSet
Wie du siehst kommt getaddrinfo() gar nicht vor, jedoch gethostbyname() (Für IPv6 wird getaddrinfo() wahrscheinlich per LoadLibrary() und GetProcAddress() nachgeladen, da es erst ab Windows 2000 Prof. verfügbar ist). Würde gethostbyname() im IPv4 Fall nicht mehr verwendet, wäre der Import irgendwie sinnlos. Du kannst dies ja auch gerne selbst überprüfen, in dem du den Debugger deiner Wahl nimmst, einen Breakpoint auf gethostbyname() in WSOCK32 setzt und dann schaust ob der Befehl aufgerufen wird.
Ich habe mal schnell die IPv6 Variante unter Linux dissasembliert (da ich normalerweise Linux verwende und jetzt nicht wieder in Windows booten wollte). Folgender PB Code:
Code: Alles auswählen
EnableExplicit
InitNetwork()
Define.i Connection = OpenNetworkConnection("google.de", 80, #PB_Network_IPv6)
If Connection
CloseNetworkConnection(Connection)
EndIf
und mal nachgeschaut welche Netzwerke Befehle im IPv6 Fall zum Einsatz kommen:
Code: Alles auswählen
40214a: e8 b1 e8 ff ff callq 400a00 <close@plt>
402166: e8 95 e8 ff ff callq 400a00 <close@plt>
40222a: e8 71 e8 ff ff callq 400aa0 <connect@plt>
40223f: e8 5c e8 ff ff callq 400aa0 <connect@plt>
4022ef: e8 cc e7 ff ff callq 400ac0 <socket@plt>
40230a: e8 11 e7 ff ff callq 400a20 <inet_addr@plt>
4023b0: e8 3b e6 ff ff callq 4009f0 <ioctl@plt>
4023c3: e8 38 e6 ff ff callq 400a00 <close@plt>
4023f3: e8 48 e6 ff ff callq 400a40 <inet_pton@plt>
40240f: e8 9c e6 ff ff callq 400ab0 <getaddrinfo@plt>
402496: e8 e5 e5 ff ff callq 400a80 <bind@plt>
4024c5: e8 26 e5 ff ff callq 4009f0 <ioctl@plt>
402557: e8 94 e4 ff ff callq 4009f0 <ioctl@plt>
4025e4: e8 67 e4 ff ff callq 400a50 <select@plt>
402637: e8 e4 e3 ff ff callq 400a20 <inet_addr@plt>
402653: e8 d8 e3 ff ff callq 400a30 <gethostbyname@plt>
und da sehen wir dann auch getaddrinfo(). Schauen wir doch mal schnell ob hier auch nur die erste IP verwendet wird.
Code: Alles auswählen
40240f: e8 9c e6 ff ff call 400ab0 <getaddrinfo@plt>
402414: 85 c0 test eax,eax
402416: 75 a8 jne 4023c0 <PB_CloseNetworkServer+0x2f0>
402418: 48 8b 84 24 70 01 00 mov rax,QWORD PTR [rsp+0x170]
40241f: 00
402420: 48 8b 50 18 mov rdx,QWORD PTR [rax+0x18]
402424: 48 8b 42 08 mov rax,QWORD PTR [rdx+0x8]
402428: 48 89 84 24 40 01 00 mov QWORD PTR [rsp+0x140],rax
40242f: 00
402430: 48 8b 42 10 mov rax,QWORD PTR [rdx+0x10]
402434: 48 89 84 24 48 01 00 mov QWORD PTR [rsp+0x148],rax
40243b: 00
40243c: e9 da fe ff ff jmp 40231b <PB_CloseNetworkServer+0x24b>
Es handelt sich um ein 64 Bit System, also ist (in C) ein int 4 Byte groß und ein long 8 byte. Als erstes wird der Rückgabewert von getaddrinfo überprüft. Ist dieser ungleich 0 wird weiter ausgeführt. Dann müssen wir noch wissen das rsp+0x170 unser Rückgabeparameter ist (also die API schreibt an diese Stelle die Liste mit den Addressen, siehe
http://linux.die.net/man/3/getaddrinfo. Den Block wo die Parameter vorbereitet werden habe ich jetzt nicht mitkopiert). Es wird also aus der Struktur addrinfo an der Stelle 0x18 gelesen. 0x18 ist Dezimal 24. Werfen wir mal einen Blick auf die Offsets dieser Struktur:
Code: Alles auswählen
struct addrinfo {
int ai_flags; - 0
int ai_family; - 4
int ai_socktype; - 8
int ai_protocol; - 16
size_t ai_addrlen; - 20
struct sockaddr *ai_addr; - 24
char *ai_canonname;
struct addrinfo *ai_next;
};
Kannst du auch gerne mit folgendem C++ Code überprüfen:
Code: Alles auswählen
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <iostream>
#include <stdio.h>
#include <stddef.h>
int main(){
std::cout << offsetof(addrinfo, ai_addr) << std::endl;
}
Ergibt ebenfalls 24. So nun wird in dieser Struktur an dem Offset 0x8 (also 8 ) und 0x10 (also 16) ein Wert ausgelesen. Also werfen wir auch mal einen Blick auf die Struktur:
Code: Alles auswählen
struct sockaddr_in6 {
short sin6_family; - 0
u_short sin6_port; - 2
u_long sin6_flowinfo; - 4
struct in6_addr sin6_addr{
union {
u_char Byte[16]; - 8
u_short Word[8]; - 8
} u;
};
u_long sin6_scope_id; - 24
};
Und wie wir sehen wird mit zwei MOVQ Befehlen die erste zurück gelieferte IPv6 Addresse nach rsp+0x140 kopiert. Da dies die einzige Stelle im Code ist wo getaddrinfo() verwendet wird und hier hard coded die erste Adresse ausgelesen wird, zeigt dies das auch nur die erste IP verwendet wird. Schaut man sich die Imports und des Programms an fällt auf das die Liste (welche von der API im Befehl getaddrinfo() allocated wird) niemals per freeaddrinfo() freigegeben wird

. Mit Hilfe des Tools ltrace kann man sich alle Bibliotheksaufrufe anzeigen und erhält:
Code: Alles auswählen
__libc_start_main(0x402000, 1, 0x7fff59cb8b68, 0x402b50, 0x402be0 <unfinished ...>
malloc(16) = 0x0101d010
malloc(64) = 0x0101d030
memset(0x0101d030, '\000', 64) = 0x0101d030
malloc(136) = 0x0101d080
memset(0x0101d080, '\000', 136) = 0x0101d080
socket(10, 1, 0) = 3
inet_pton(10, 0x6040b5, 0x7fff59cb89d0, -1, 0) = 0
getaddrinfo("google.de", NULL, NULL, 0x7fff59cb8a00) = 0
connect(3, 0x7fff59cb89a0, 28, 0x7fff59cb89a0, 3) = -1
close(3) = 0
free(0x0101d010) = <void>
exit(0 <unfinished ...>
Hier fehlt auch jede Spur von freeaddrinfo(). Es scheint wohl ein Memoryleak in der Funktion zu existieren. Wenn du eine DNS findest, welche mehrere IPv6 Adressen zurück liefert, kann ich dir mit dem Programm auch zeigen das jeweils nur ein connect() aufgerufen wird. Hier schlägt das connecten zwar auch fehl (Rückgabewert -1), jedoch ist in dem DNS Eintrag von Google nur eine IPv6 Adresse angegeben.
Dark