Giriş
Proqram təminatlarının zərərli olub olmadığını müəyyən etmək üçün ilk öncə faylı statik analiz etmək lazımdır. Sadə statik analiz, fayl icra olunmadan aparılır və proqram təminatı haqqında ilkin məlumatlar əldə olunur. Analiz zamanı kriptoqrafik xeş summaların hesablanması yayılmış üsullardan biridir. Xeş summa hər bir fayl üçün unikaldır və əldə olunandan sonra internetdə araşdırma edib faylın daha öncə müəyyən edilib edilmədiyini öyrənmək olar. Bu məqaləmizdə xeş summalarla axtarışın nece aparıldığı və bu axtarışdan hakerlərin yayılma üsullarından məlumat veriləcək.
Xeş summa nədir?
Xeş-funksiya - ixtiyari uzunluğda X məlumatı, riyazi üsullarla müəyyən edilmiş uzunluqda H=f(X) dəyərə çevirən funksiyadır. Əldə olunan çıxış məlumatı xeş summa adlanır. Xeşləmə funksiyasının əsas xüsusiyyəti giriş məlumatlarının uzunluğundan asılı olmayaraq, çıxış məlumatının qeyd edilmiş olçüdə olmasıdır.
Xeş summaların hesablanmasının bir neçə sxemi var. Ən sadə variantda, faylın məzmununun MD5, SHA256, SHA1 və s. xeş alqoritmlərlə əldə olunmasıdır. Faylın məzmununda olunan hər hansı bir dəyişiklik, hash summanın fərqli nəticə verəcəyini görəcəyik.
- MD5 xeş
MD5 ( Message Digest Method 5 ) daxil edilən məlumatı, ölçüsündən aslı olmayaraq, 128 bit hexadecimal ədədlərə çevirir. Tək istiqamətli şifrələmə alqoritmidir. “Tək istiqamətli şifrələmə” məlumatların xeş summadan geri bərpa edilə oluna bilməməsi mənasına gəlir. Əgər daxil olan məlumatda bir simvol belə, alqoritm başqa bir summa yaradacaq. Misal üçün, aşağdakı şəkildə “Salam” və “salam” sözlərinin MD5 summaları hesablanır:
- SHA xeş
SHA xeş MD5-in dəyişdirilmiş versiyasıdır. Əsasən məlumatların və sertifikatların xeş olunması üçün istifadə edilir. Tək istiqamətli şifrələmə alqoritmidir. SHA alqoritminin bir neçə növləri var: SHA1, SHA2, SHA256 və s. SHA1 alqoritm daxil olunmuş məlumatdan 160 bit hexadecimal dəyər əldə edir. Uzunluğu 40 simvoldur. SHA2 əhəmiyyətli dərəcədə SHA1-dən fərqlənir. SHA2, 224, 256, 384 və 512 bit hexadecimal xeş qenerasiya edən 6 funksiyadan ibarətdir. Bunlar SHA224, SHA256, SHA384, SHA512, SHA512/224, SHA512/256 funksiyalarıdır. SHA256 – 32 bit, SHA512 – 64 bit hexadecimal ədədlərə qenerasiya edir. SHA2 xeş funksiyası TLS, SSL, PGP, SSH kimi protokollarda istifadə olunur.
Fuzzy (qeyri-səlis) xeş
Rəqəmsal məlumat əsrində oxşar məlumatları və ya faylları müqayisə etmək bacarığı vacib hala gəldi. Oxşarlıq xeşi kimi tanınan qeyri səlis xeş bu axtarışda dəyərli bir vasitə kimi ortaya çıxır. MD5, SHA-256 kimi alqorimtlər faylların oxşar olub-olmadığını müəyyən etmək üçün istifadə edilə bilməz, çünki bu alqoritmlərdən əsas tələblərdən biri girişdə edilən kiçik dəyişiklik xeş dəyəri o qədər geniş şəkildə dəyişməlidir ki, yeni xeş dəyəri ilə əlaqəsiz görsənsin. Qeyri-səlis xeş eyni olmayan, lakin oxşar olan məlumatların aşkar edilməsi probleminin həll etmək üçün mövcuddur. Nümunə üçün balaca bir mətn qenerasiya edək:
”Hey User1:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ultrices vehicula risus, id dictum sem dictum vel. Nulla vel turpis in eros fringilla mollis. Mauris maximus turpis sem, eu pellentesque dolor ornare vitae. Duis mauris ex, luctus eget scelerisque ac, faucibus faucibus diam. Proin in vehicula lectus. Fusce ac enim vitae diam sollicitudin faucibus sed eu felis. Nunc semper malesuada eros, vel dapibus metus fermentum vel. Nulla porta libero eget eros scelerisque, sed convallis ante placerat. Nam sed ultrices erat, finibus finibus erat. Fusce eu felis nec ligula tincidunt dapibus.”
Qenerasiya olunan mətnin qeyri-səlis xeş dəyəri: 12:mkPvtKgMPYO96ul4XOpnvz9xI2BvMUC7Kbsu8RpQGhFK1FosZhW7ddwly:mkPoY3GRz9xf7GuosZSdwQ.
İndi isə mətnin əvvəlində “User1” sözünü “All Users” dəyişdirək və qeyri-səlis xeşi hesablayaq:
12:GAkPvtKgMPYO96ul4XOpnvz9xI2BvMUC7Kbsu8RpQGhFK1FosZhW7ddwly:xkPoY3GRz9xf7GuosZSdwQ.
Alınan nəticələri müqayisə etsək oxşarlıq 94.5% təşkil etdiyin görürük:
12:mkPvtKgMPYO96ul4XOpnvz9xI2BvMUC7Kbsu8RpQGhFK1FosZhW7ddwly:mkPoY3GRz9xf7GuosZSdwQ
12:GAkPvtKgMPYO96ul4XOpnvz9xI2BvMUC7Kbsu8RpQGhFK1FosZhW7ddwly:xkPoY3GRz9xf7GuosZSdwQ
Qeyri-səlis xeş zərərvericilərlə mübarizədə güclü vasitədir. O, zərərvericilərin variasiyalarını aşkar etməyə imkan verir. Əvvəllər görünməmiş zərərli proqram variantlarını müəyyən etməyə kömək edir.
Imphash alqoritmi
İlk olaraq, PE faylda olan Import Table haqqında qısa məlumat verək. Import Table PE faylda istifadə olunan modulları və modulların istifadə etdiyi funksiyaları haqqında məlumatları özündə əks etdirir. Bunun üçün kiçik bir örnək göstərək. C++ dilində içərisində GetModuleHandle və CreateProcess olan 2 funksiya yazaq.
int get_module_handle() {
GetModuleHandle((LPCWSTR)"kernel32.dll");
return 0;
}
int create_process() {
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
BOOL bRet = FALSE;
wchar_t cmd[] = L"c:\\Windows\\notepad.exe";
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
bRet = CreateProcessW(
NULL,
cmd,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&pi);
return 0;
}
int main() {
create_process();
get_module_handle();
return 0;
}
Hazırlanan faylın Import Table nəzər yetirək.
Import Table-da Kernel32.dll moduluna aid CreateProcessW və GetModuleHandleW funksiyalarını görürük.
Imphash hesablamaq üçün, öncə Import Table-da olan bütün modulların adları götürülür, kiçik hərflərlə dəyişdirilir və modulun uzantısı ləğv edilir. Məsələn Kernel32.dll dəyişdirilir və kernel32 olur. Daha sonra hər bir Import Table-da hər bir modulun istifadə olunduğu funksiya adı modulun adina kiçik hərflərlə birləşdirilir. Məsələn kernel32.getmodulehandlew. Bu əməliyyat bütün modullara və onun funksiyalarına tətbiq olunur və bir sətrə əlavə olunur. Sonda isə MD5 xeş funksiya hesablanır. Növbəti şəkildə imphash alqoritminin psevdo koduna baxaq:
Python-da imphash hesablamaq üçün pefile modulu var. Bu modulun köməkliyi ilə şəkil 4-də gostərilən proqramın imphash hesablayaq.
>>> import pefile
>>> pefile.PE("Project1.exe").get_imphash()
'761c94d79e2ac46a2b3ec558de80113f'
Imphash alqoritmi ilə bir ailəyə aid zərərli proqram təminatlarını təyin etmək üçün unikal vasitə hesab edilə bilər. Lakin xakerlər mənbə kodu dəyişərək və ya import table tərtib edilən zaman funksiaların və ya modulların yeri dəyişdirilməklə imphash zamanı başqa nəticə əldə etmək olar. Yuxarıda yazılan proqramın import table-da (şəkil 5) GetModuleHandleW və CreateProcessW funksialarının ardıcıllığını dəyişək və imphash hesablayaq.
Pythonda imphash yenidən hesablayaq:
>>> import pefile
>>> pefile.PE("Project1.exe").get_imphash()
'458a581018ba31e2368092e3f4c13fe6'
Şəkil 7 və 9-da nəticələrin fərqli olduğunu görə bilərik.
Packing və unpacking
Zərərli proqram təminatını antivirus sistemlərindən gizlətmək üçün hakerlər packing üsulundan istifadə edirlər. Burada əsas məqsəd proqram təminatınn həcmini azaltmaq və statik analizi çətinləşdirməkdir. Bir çox packer-in istifadəsi cox asandır və pulsuzdur. Statik analizə başlamazdan əvvəl pack olunmuş fayl unpack olunmalıdır. Pack olunmamış PE fayl əməliyyat sistemi tərəfindən yaddaşa yüklənir, lakin pack olunmuş faylda isə əməliyyat sistemi tərəfindən yaddaşa unpacking stub yüklənir. Unpacking stub daha sonra orijinal kodu yaddaşa yükləyir. Statik analiz zamanı pack olunmuş faylda biz unpacking stub analiz etmiş oluruq.
Unpacking stub 3 addımı yerinə yetirir:
- Orijinal icra olunan faylı yaddaşa yükləyir
- İcra olunan faylın Import Table-nı həll edir
- İcra olunan nöqtəni faylın OEP (Original Entry Point) yönəldir
Import Table həlli yolu packerdən aslıdır. Hər bir packer ayrı metodlarla məsələni həll edir. Geniş yayılmış üsullardan biri LoadLibrary və GetProcessAddress funksialarının köməkliyi ilə lazım olan modulları və funksiaları yaddaşa yükləməkdir. Modulu yükləmək üçün LoadLibrary funksiyası və hər bir funksiyanın adresini əldə etmək üçün GetProcAddress çağırılır. Başqa bir metod isə Import Table dəyişiklik olunmadan saxlanılır. Unpacking stub üçün import table həll etməyə ehtiyac olmur və analiz zamanı bütün import table görünür. Bu qeyri optimal pack metoddu. 3-cü yanaşma isə, hər bir moduldan bir funksiyanı import table-da saxlamaqdır. Bu zaman birinci yanaşmadan fərqli olaraq LoadLibrary funksiyasından istifadə etmək ehtiyacı qalmır. Lakin modulda istifadə olunan funksialar yenədə həll olunmalıdır. Sonuncu yanaşma isə bütün import olunan modulların gizlədilməsidir (LoadLibrary və GetProcAddress funksiaları daxil olmaqla). Unpacker stub lazım olan modulları LoadLibrary və GetProcAddress funksiaları işə salmadan tapmalıdır. Şəkil 4-də göstərilən proqramı UPX alqoritmi ilə pack edək və alınan nəticəyə baxaq. İlk öncə section başlıqlara baxaq:
Section adları UPX0, UPX1 kimi qeyd olunduğunu görürük. UPX0 başlığının RawSize-nın 0 ancaq Virtual Size-in isə 8000 olduğu diqqətimizi cəkir. oldu İndi isə İmport table nəzər salaq:
Kernel32.dll modulunun sadecə 4 funksiya import eldədiyini görürük. Normal icra olunan faylda 4 funksiyanın import olunmasl normal deyil. UPX alqoritmi ilə pack olunmush faylı tool vasitəsi ilə decompress edə bilərik.
Packing müəyyən etməyin daha bir yolu binar analizdir. Bu işdə bizə YARA aləti kömək olacaq. YARA – zərərli proqram təminatının müəyyən etmək və onların təsnifini müəyyən etməyə kömək edən alətdir. YARA aləti Windows, Linux və Mac OS platformalarında işləyən multi-platform proqramdır. Gəlin UPX alqoritmi ilə pack olunmuş faylı YARA aləti ilə müəyyən etməyə çalışaq. İlk öncə proqramın ( şəkil 4) UPX alqoritmi ilə pack edək və daha sonra binar koda baxaq:
Binar kodun analizi zamanı şəkil 14-də seçilmiş UPX1, UPX! və UPX0 yazılarını görürük. Bu yazıları, YARA aləti ilə, proqramın kodunda axtarmaq üçün qaydanı yazaq.
İlk öncə qaydanın adını qeyd edirik: rule upx. Daha sonra meta bölməsində qaydanın təsfirini description dəyişəninə yazırıq: “UPX packed file”.
Şəkil 14-də seçilmiş, UPX0 və UPX1, yazıların binar kodlarını və UPX! yazısnı string formatda dəyişənlərə mənimsədirik. “strings” bölməsində qeyd olunan bütün dəyişənlərin condition bölməsində tam ödənilməli olduğunu “and” operatoru ilə qeyd edirik. İndi isə bu qaydanı Pythonda yara modulu ilə işə salaq:
import yara
yara_file = open('rules')
rules = yara.compile(file=yara_file)
yara_file.close()
with open("Project1.exe", "rb") as f:
matches = rules.match(data=f.read())
print(matches)
İlk olaraq yara modulunu import edirik. Daha sonra şəkil 15-də qeyd olunan qaydalar kompilasiya olunub rules dəyişəninə yazılır. Növbəti addımda rules dəyişənini, pack edilmiş, TestApp.exe faylı üzərində tətbiq edirik. Proqram işə salınandan sonra şərtləri ödəyən qaydaların adlarını görəcəyik:
Nəzarət olunan maşın öyrənməsi (Machine Learning) təsnifat alqoritmi ilə packing aşkarlanması
Bu bölmədə maşın öyrənməsinin necə işlədiyi və bu alqoritmlərin həyata keçirilməsi barədə qısa məlumatlar veriləcək. Nəzarət edilə bilən öyrənmə, həm giriş, həmdə çıxış parametrlərinin verildiyi bir maşın öyrənmə sahəsidir. Nəzarət edilən öyrənmə əsasən 2 hissədən ibarətdir: öyrənmə və öyrədilmiş modelə daxil olunan yeni nümunələr. Bizim vəziyyətdə nümunə binar fayldır və onun bütün xüsusiyyətləri çixarılaraq təsvir edilir. Çıxarılan məlumatlar giriş parametrləri kimi daxil olunur. Öyrənmə prosesi üçün çoxlu sayda zərərli proqram təminatı və ya pack olunmuş fayllar tələb olunur. Bu fayllar əsasən əlçatan olmadığından və faylların bütün xüsusiyyətlərinin çıxarılması çox vaxt apardığından, giriş məlumatlarının hazırlanmasında çətinlik yaranır. Əsas təsnifat alqoritmləri bunlardır: Gaussian Naive Bayes, Decision Tree, Random Forest. Bu alqortmlərin effektiflik dərəcəsi F-qiyməti (F-score) ilə ölçülür. Gəlin “real” məlumatlar çoxluğunu (nümunə;giriş parametrləri) A kimi işarələyək. “Proqnozlaşdırılmış” cütlərin ( nümunə; çıxış parametrləri) B çoxluğu kimi işarələyək. F-qiyməti, düzgün etiketlənmiş faylların “proqnozlaşdırılmış” fayllara olan nisbətidir:
F(A, B) = (| A ∩ B |)/(|B|)
Qeyd edək ki, bu orta hesablama metodu ilə geriçağırma (recall) və dəqiqlik (precision) üst-üstə düşür.
Yara
YARA (Yet Another Recursive Acronym) mətn və ya binar faylı təsnif etmək üçün istifadə olunan alətdir. YARA qaydalarının hazırlanması üçün xüsusi şablon (pattern) və şərtlər (condition) istifadə olunur. Bu alət Virustotal sisteminin yaradıcılarından biri olan Viktor Alvarez tərəfindən hazırlanıb. Alətin hazırlanmasında əsas məqsən zərərli proqram təminatının növünü və ya təsnifatını təyin etmək üçün nəzərdə tutulub. YARA qaydaları Regex ilə müqayisə edilə bilər, lakin Regex əsasən mətn axtarışında, YARA isə zərərli proqramların aşkarlanması və təhlili üçün istifadə olunur.
YARA qaydaları necə yaradılır? Hər bir qayda başlığ (header), metadata (istəyə bağlı), strings və şərt bölmələrindən ibarətdir. Fayl daxilində xüsusi sətirlə uyğun gələn sadə YARA qaydası nümunəsinə baxaq:
rule example_rule
{
strings:
$my_string = "example string"
condition:
$my_string
}
Göstərilən nümunədə “example_rule” qaydanın başlığıdır. Gövdə hissəsində “strings” bölməsi $my_string adlı dəyişənə “example string” dəyəri mənimsədilib. Şərt bölməsi faylda $my_string sətrinin tapıldığı halda qaydanın uyğun olmasını müəyyən edir. Daha kompleks misala nəzər salaq:
rule complex_rule
{
meta:
description = "Complex YARA rule example"
strings:
$string1 = "pattern1"
$string2 = "pattern2"
$regex1 = /abc[a-z]{3}xyz/
condition:
$string1 and ($string2 or $regex1)
}
Bu qaydanı təsvir etmək üçün metadata bölməsini meta açar sözü ilə əlavə etdik. Strings bölməsində 3 ayrı dəyişən elan edildi: $string1 və $string2 adi sətir, $regex1 regular expression. Şərt bölməsi müəyyən edir ki, əgər $string1 tapılarsa və $string2 və ya $regex1 tapılarsa qaydaya uyğun olmalıdır. YARAnın üstün cəhətlərindən biri onaltılıq (hex) sətirlərin axtarışını apara bilməsidir. Məsələn “salam” sözünün əvəzinə “73 61 6C 61 6D” sətrini axtara bilərik. Onaltılıq sətirlərin axtarışını daha effektiv etmək üçün “wildcard” xüsusiyyətindən istifadə etmək olar. Bəzi baytlar dəyişsə belə “wildcard” xüsusiyyəti bizə onları təyin etməyə kömək edir. Yenə “salam” sözünü nümunə gətirərək “73 61 ?? 61 6D” sətiri ilə axtarış edə bilərik, bu halda 3cü baytın nə olduğu bizim üçün əhəmiyyət daşımır və baytların qalan hissəsi uyğun gəlirsə YARA nəticəni göstərir. Digər “wildcard” xüsusiyyəti naməlum bayt diapazonunu müəyyən etməyə imkan verir. “73 61 [1-3] 6C 61 6D” istifadə edərək “sa” və “lam” arasında 1 və ya 3 bayt ölçüsündə məlumat yerləşsədə, bu uyğundur. Zərərli proqramların axtarışını asanlaşdırmaq üçün bu xüsusiyyətlər çox faydalıdır.
Yaddaşda zərərvericinin axtarışı
Məqalənin bu hissəsində proses yaddaşında YARA qaydaları istifadə edərək zərərli kod nümunələrinin axtarışını həyata keçirəcəyik. Proses başlayanda Windows əməliyyat sistemi proqram təminatına lazım olan qədər yaddaşda yer ayırır. Lakin istifadəçi rejimində (user mode) bu ayrılan yaddaş “bütöv blok” şəklində olmur, mövcud boş yaddaşı ayırmağa çalışır .
Bu ünvanları tapmaq üçün bir necə funksiyalardan istifadə olunacaq. İlk öncə istifadəçi rejimi üçün əməliyyat sistemi tərəfindən yaddaşa ayrılan aşağı və yuxarı sərhədlari müəyyən etmək lazımdır. Bunun üçün GetSystemInfo() funksiyasından istifadə olunacaq. Bu funksiya mövcud sistem haqqında məlumatları alır və SYSTEM_INFO strukturunda saxlayır. Bu struktur içərisində lpMinimumApplicationAddress və lpMaximumApplicationAddress atributları müvafiq olaraq user rejimi üçün ayrılmış yaddaşın aşağı və yuxarı sərhədlərini göstərir. VirtualQueryEx funksiyası ilə hədəf prosesə aid olan adresləri təyin edib, ReadProcessMemory funksiyası ilə yaddaş dəyərlərini əldə etmiş olacayıq. Yuxarıda qeyd olunan funksiyaları praktikada tətbiq edək. Test üçün icra olunan zərərlinin SHA256 xeşi: c5b6985cc8e63c04d1ee674684e6362353c00002fdfaff33e15affae9c005b00. Faylı yükləmək üçün ünvan: https://bazaar.abuse.ch/sample/c5b6985cc8e63c04d1ee674684e6362353c00002fdfaff33e15affae9c005b00. Qeyd: göstərilən eksperiment izolasiya olunmuş sistemdə aparılıb. Zərərverici C# proqramlaşdırma dilində hazırlanıb. Şəkil 4 və 5 müvafiq olaraq mənbə kodunun C# və IL görüntülərini təqdim edir.
5ci şəkildə mavi düzbucaqlının içində opkodlar, qırmızı düzbucaqlının içində opkod arqumentlər, sarı düzbucaqlıda isə opkodun adları yerləşir. Nəzərə almaq lazımdır ki bəzi opkodlar 2 bayt həcmində ola bilər. Məsələn “ceq” opkkodu onaltılıq say sistemində “FE 01” kimi təsvir olunur. YARA qaydalar yazılan zaman zərərli proqramın statik və ya sərt (hard) kodlu hissələrdən istifadə etmək daha uyğundur. Bizim analiz etdiyimiz kodda 5ci şəkildə 1141-1147ci sətrlərin YARA qaydaları yazılacaq.
$pattern = { 7B [4] 72 [4] 6F [4] 02 22 [4] 22 }
Bütün deyilənləri birləşdirib yekun axtarış kodun yazaq.
Kod icra olunduqdan aşağıda göstərilən nəticəni görürük:
İstinadlar
[1] https://en.wikipedia.org/wiki/MD5
[2] https://en.wikipedia.org/wiki/SHA-1
[3] https://www.varonis.com/blog/yara-rules
[4] https://www.mandiant.com/resources/blog/tracking-malware-import-hashing