Скачиваем архивы
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 и freevoid 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! Надеюсь, что вы не зря потратили время на прочтение моей белеберды :)
Комментарии
Отправить комментарий