Скачиваем архивы
P.S: Пишу диплом, поэтому на время разучился мыслить не по ГОСТ-у
Недавно пришлось решать задачу обновления программы. Алгоритм получился довольно банальный:
В качестве веб-сервера я поставил Apache, а программу писал на Си c использованием Winsock.
Основной фишкой проекта является то, что програмуля умеет скачивать и сама распаковывать архивы формата 7-zip (см. алгоритм LZMA), что может пригодиться для хитрого трояна, либо для обновления модулей ботнет-а.
Начнем с азов сетевого программирования.
Для получения данных заданной длинны я использую такую функцию:
Отправляем GET запрос и получаем данные.
Но перед этим необходимо упомянуть о функциях для работы с памятью memcpy и memmem, так как они нам очень пригодятся.
я использовал макросы для работы с кучей:
Распаковываем архив.
Разработчики архиватора 7-zip любезно предоставили LZMA SDK, в которой при желании не так уж и сложно разобраться. Немного посидев с исходником \lzmaXXX\C\Util\7z\7zMain.c родилась функция распаковки:
Заставить корректно работать программу без CRT мне не удалось, скорее всего это связано с вычислениями в алгоритме LZMA, но программа вышла ~36 Кбайт. С CRT ~ 82 Кбайта с включенным выводом.
P.S.S: TCP/IP - Connection people! Надеюсь, что вы не зря потратили время на прочтение моей белеберды :)
Недавно пришлось решать задачу обновления программы. Алгоритм получился довольно банальный:
- Получить текущую версию
- Сравнить с актуальной версией
- В случае несовпадения, скачать и заменить оригинальные файлы
В качестве веб-сервера я поставил Apache, а программу писал на Си c использованием Winsock.
Основной фишкой проекта является то, что програмуля умеет скачивать и сама распаковывать архивы формата 7-zip (см. алгоритм LZMA), что может пригодиться для хитрого трояна, либо для обновления модулей ботнет-а.
Начнем с азов сетевого программирования.
SOCKET tcp_client(char *host, unsigned short port) { struct sockaddr_in dest_addr; struct hostent *hst; SOCKET s; if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1; dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(port); if ((dest_addr.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) { if (hst = gethostbyname(host)) ((unsigned long *) & dest_addr.sin_addr)[0] = ((unsigned long **) hst->h_addr_list)[0][0]; else { close(s); return -1; } } if (connect(s, (struct sockaddr *) & dest_addr, sizeof (dest_addr))) return -1; return s; }Как видно из названия функции, она создает соединение с указанным хостом (это может быть, как IP, так и адрес сайта) возвращая нам дескриптор открытого сокета [на всякий случай проверьте, какие значения в случае неудачи возвращает socket и connect, так как я скопировал функцию из своего *nix-ового кода и кстати в winsock нет функции close, есть closesocket]. При желании функцию htons, можно заменить на макрос, который справедлив лишь для ОС Windows:
#define HTONS(a) (((0xFF&a)<<8) + ((0xFF00&a)>>8))Как известно TCP – потоковый протокол, и хотя данные передаются в IP-пакетах, размер пакета напрямую не связан с количеством данных переданных TCP. Поэтому нельзя с уверенностью сказать что при вызове recv мы получим заданное количество байт.
Для получения данных заданной длинны я использую такую функцию:
#define _SEND 0 #define _RECV 1 int (__stdcall *tcp_func)(SOCKET s,char* buf,int len,int flags); int tcp_rs(char type,SOCKET s, char *buf, int len, int flags) { int total = 0; int n; tcp_func=(int (__stdcall *)(SOCKET,char *,int,int)) (type==_SEND) ? &send:&recv; while(total < len) { n = tcp_func(s, buf+total, len-total, flags); if(n>0) { total += n; } else if(n == 0) { closesocket(s); return 0; } else { n=WSAGetLastError(); closesocket(s); return (!n+1); } } return total; }которая в случае успеха возвращает количество переданных байт равных len, 0 в случае, если соединение было разорвано либо закрыто и (минус) номер ошибки, в случае неудачи вызова функции send/recv.
Отправляем GET запрос и получаем данные.
Но перед этим необходимо упомянуть о функциях для работы с памятью memcpy и memmem, так как они нам очень пригодятся.
void * __cdecl my_memcpy (void * dst,const void * src,size_t count) { void * ret = dst; while (count--) { *(char *)dst = *(char *)src; dst = (char *)dst + 1; src = (char *)src + 1; } return(ret); } void * __cdecl my_memmem(const void *buf, const void *pattern, size_t buflen, size_t len) { size_t i, j; char *bf = (char *)buf, *pt = (char *)pattern; if (len > buflen) return (void *)NULL; for (i = 0; i <= (buflen - len); ++i) { for (j = 0; j < len; ++j) { if (pt[j] != bf[i + j]) break; } if (j == len) return (bf + i); } return NULL; }А для замены стандартных malloc и free
void free(void *ptr);Функция free() освобождает место в памяти, на которое указывает ptr
HANDLE hHeap=GetProcessHeap(); #define _alloc(len) HeapAlloc(hHeap,HEAP_ZERO_MEMORY,len) #define _free(p) HeapFree(hHeap,0,p) #define _realloc(p,len) HeapReAlloc(hHeap,HEAP_ZERO_MEMORY,p,len)Я долго переделывал эту функцию и в итоге пришел к такому варианту. Что-бы каждый раз не мучатся с определением конца HTTP заголовка, длинной и началом данных я придумал структуру:
// ------------------------- это все в h файле #pragma pack(push,1) // вырубаем выравнивание typedef struct _HTTP_DATA { unsigned char * header; unsigned char * data; unsigned int header_len; unsigned int data_len; } HTTP; #pragma pack(pop) #define _OUT 1 // отладочный вывод #define HTTP_PORT 80 //---------------------------------------------------------- BOOL http_get(const char *host,const char *get,HTTP * http) { SOCKET sock; int len; char buff[1024]; char * http_data,*c,*s; sock=tcp_client(host,HTTP_PORT); if(sock==INVALID_SOCKET) return FALSE; len = wsprintf(buff, "GET %s HTTP/1.0\r\n\ Host: %s\r\n\ User-agent: Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6\r\n\ Accept: */*;q=0.9\r\n\ Connection: close\r\n\r\n",get,host,host); http->data=NULL; http->header=NULL; http->data_len=0; http->header_len=0; #ifdef _OUT printf("HTTP GET http:\/\/%s%s...\n",host,get); #endif if(tcp_rs(_SEND, sock, buff, len, 0)>0) { // получаем заголовки или весь пакет len = recv(sock, buff, sizeof (buff), MSG_PEEK); // выделяем память под буффер http_data=http->header=_alloc(len); if(tcp_rs(_RECV, sock, http_data, len, 0)>0) { // разбираем заголовки c=my_memmem(http_data,"Content-Length: ",len,16); c+=16; s=c; while(*c++!=0x0d); *--c=0x00; // 0x0d 0x0a http->data_len=atoi(s); http->data=my_memmem(http_data,"\r\n\r\n",len,4); http->data+=4; http->header_len=http->data-http->header; #ifdef _OUT printf(" Content-Length: %d\n received: %d - %d%%\n",http->data_len,len,(len*100/(http->data_len+http->header_len))); #endif // если мы получили полностью пакет if(len==(http->data_len+http->header_len)) { closesocket(sock); return TRUE; } // если нет то увеличиваем память под пакетик http_data=http->header=_realloc(http_data,(http->data_len+http->header_len)); http->data=http->header+http->header_len; // и дополучаем данные if(tcp_rs(_RECV, sock, &http_data[len], (http->data_len+http->header_len)-len, 0)>0){ #ifdef _OUT printf(" received: %d - 100%%\n",http->data_len); #endif closesocket(sock); return TRUE;} } _free(http_data); } return FALSE; } //---------------------------------------------------------- // ну и сразу пример использования этой функции int main() { WSADATA ws; hHeap=GetProcessHeap(); WSAStartup( MAKEWORD( 2, 2 ), &ws ); HTTP file; if(http_get("ya.ru","/",&file)) { // тут делаешь все что угодно с данными _free(file.header); // и освобождаешь выделенную память } WSACleanup(); return 0; }Ну и наконец самое интересное
Распаковываем архив.
Разработчики архиватора 7-zip любезно предоставили LZMA SDK, в которой при желании не так уж и сложно разобраться. Немного посидев с исходником \lzmaXXX\C\Util\7z\7zMain.c родилась функция распаковки:
#include "\lzmaXXX\C\7z.h" #include "\lzmaXXX\C\7zAlloc.h" #include "\lzmaXXX\C\7zCrc.h" #include "\lzmaXXX\C\7zFile.h" #include "\lzmaXXX\C\7zVersion.h" static ISzAlloc g_Alloc = { SzAlloc, SzFree }; int unpack(char *arch_file) { CFileInStream archiveStream; CLookToRead lookStream; CSzArEx db; SRes res; ISzAlloc allocImp; ISzAlloc allocTempImp; UInt16 *temp = NULL; size_t tempSize = 0; allocImp.Alloc = SzAlloc; allocImp.Free = SzFree; allocTempImp.Alloc = SzAllocTemp; allocTempImp.Free = SzFreeTemp; if (InFile_Open(&archiveStream.file, arch_file)) { //PrintError("can not open input file"); return 1; } FileInStream_CreateVTable(&archiveStream); LookToRead_CreateVTable(&lookStream, False); lookStream.realStream = &archiveStream.s; LookToRead_Init(&lookStream); CrcGenerateTable(); SzArEx_Init(&db); res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp); if (res == SZ_OK) { //----------- UInt32 i; /* if you need cache, use these 3 variables. if you use external function, you can make these variable as static. */ UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */ Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */ size_t outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */ CSzFile outFile; size_t processedSize; for (i = 0; i < db.db.NumFiles; i++) { size_t offset = 0; size_t outSizeProcessed = 0; const CSzFileItem *f = db.db.Files + i; size_t len; // получаем имя файлика len = SzArEx_GetFileNameUtf16(&db, i, NULL); if (len > tempSize) { SzFree(NULL, temp); tempSize = len; temp = (UInt16 *)SzAlloc(NULL, tempSize * sizeof(temp[0])); if (temp == 0) { res = SZ_ERROR_MEM; break; } } SzArEx_GetFileNameUtf16(&db, i, temp); // -- получили распаковываем его в памяти res = SzArEx_Extract(&db, &lookStream.s, i, &blockIndex, &outBuffer, &outBufferSize, &offset, &outSizeProcessed, &allocImp, &allocTempImp); if (res != SZ_OK) break; // терь записываем на дисочек if (OutFile_OpenW(&outFile, temp)) { // can not open output file res = SZ_ERROR_FAIL; break; } processedSize = outSizeProcessed; if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed) { //can not write output file; res = SZ_ERROR_FAIL; break; } if (File_Close(&outFile)) { //can not close output file res = SZ_ERROR_FAIL; break; } } IAlloc_Free(&allocImp, outBuffer); //------ } SzArEx_Free(&db, &allocImp); SzFree(NULL, temp); File_Close(&archiveStream.file); if (res == SZ_OK) { //printf("\nEverything is Ok\n"); return 0; } return 1; }естественно для успешной компиляции исходники из LZMA SDK просто необходимы. Функция распаковывает архив в директорию из корой работает программа, при этом она не учитывает каталогов, а распаковывает лишь файлы из архива. Мне этого было вполне достаточно для обновления (замены) файлов в текущем каталоге.
Заставить корректно работать программу без CRT мне не удалось, скорее всего это связано с вычислениями в алгоритме LZMA, но программа вышла ~36 Кбайт. С CRT ~ 82 Кбайта с включенным выводом.
P.S.S: TCP/IP - Connection people! Надеюсь, что вы не зря потратили время на прочтение моей белеберды :)
Комментарии
Отправить комментарий