Post by X on Jan 24, 2017 18:12:15 GMT
Öncelikle herkese merhabalar. Bugün, kısa bir süre önce karşılaşıp bir arkadaşımdan rica ederek size sunduğum, lakin uzaklaştırılmış olduğum için size açıklama fırsatı bulamadığım, basit ve saçma bir uygulamayı sizlere sunacağım. Öncelikle hemencecik sorunsalımıza ve cevabımıza birlikte göz atalım,
+ Arkadaşlar, bir programın kendini silmesini nasıl sağlarım?
- Ahanda böyle,
Hazırlık evresini anlattığımıza göre, şimdi mimarileri daha yakından inceleyebiliriz. Öncelikle "_SELFEX" ismine sahip veritipimizi biraz inceleyelim. Göründüğü üzere, "struct" veritipimiz 4 adet düzenlenmeyi bekleyen bellek bölgelerine sahiptir. Bunlar,
olarak sıralanabilir. ShExecInfo, daha sonra kullanacağımız "ShellExecuteEx" tarafından kullanılacak gerekli önayarları saklar iken; szMax, szCline ve szFlags sırasıyla dosya yolunu, komut satırının bulunduğu dizini ve komut satırı argümanlarını barındırır. Sonrasında veritipimiz, kabaca bir tabir ile "SELDEL" isminde yoluna devam eder.
Diyelim ki normal olarak uygulama çalıştı, arkaplanda herhangi bir hata gerçekleşmedi. Böyle bir durumda kontrol blogumuzun içerisindeki koda giriş yaptık. Bizi, aşağıdaki kod parçacığı karşılayacaktır,
Yukarıdaki açıklamadan yola çıkarak, "ModifyStru (&SELFEX);" satırının önceden tanımladığımız "SELFEX" değişken adresini alıp, gerekli ayarlamalar ile işleyerek şekillendirdiğini söyleyebiliriz.
Hemen uygulamamızı derleyip (MinGW) işlevini kontrol edelim. Evett, çalışıyor - e doğal olarak, boşuna yazmadık o kadar satırı. Lakin bir sıkıntı var, dosya boyutu o kadar büyük ki - rahatlıkla fark edilebilir, tamı tamına 32.342 bayt. Bu biraz da C dilinin azizliğinden kaynaklı, hadi şimdi de C dilinden farklı olarak, kardeşi olan Assembly ile benzer bir uygulama yazalım. Mantık aynı, sadece sözdizimi farklı. Bakalım bu ne kadar alan tutacak,
Sonuç? Şekildeki gibidir, hemen küçük bir hatırlatma yapalım - MessageBox herhangi bir thread oluşturmayıp, main-thread üzerinden çalışıp buna göre değer döndürdüğünden dolayı, "OK" butonuna tıklayana kadar diğer işlemlere geçilmez, demedi demeyin.. Boyutuna baktığımız zaman, tadaa.. 2.560 bayt.
Bir de "zztri" üstadımızın dediği gibi, bir "batch" (*.bat) dosyası oluşturup dosyamızı sildirelim. Bunun için, tekrardan masm32 sözdizimine göre gerekli kütüphanemizden yola çıkarak dosyamızı oluşturup (*.bat), içerisine bir kontrol blogu ekleyip, dosya var ise sildireceğiz. Diğer türlü etiketimize gidip duracak. Assembly masm32 kaynak kodumuz şekil A'da gözüktüğü gibidir,
+ Arkadaşlar, bir programın kendini silmesini nasıl sağlarım?
- Ahanda böyle,
/*
* Self-Execution Payload (C) 2015
* % fname: selfexec.cpp
*/
#define WINVER 0x0501 //WindowsXP
#include <Windows.h>
#include <Shlobj.h>
#define PATH MAX_PATH //Maximum [Path + FName]
typedef struct _SELFEX
{
SHELLEXECUTEINFO ShExecInfo;
TCHAR szMax [PATH],
szCline [PATH],
szFlags [PATH];
} SELFDEL, *PSELFDEL;
Dick ModifyStru (_SELFEX *);
int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
SELFDEL SELFEX;
if( (GetModuleFileName (0, SELFEX.szMax, PATH)!=0 ) &&
(GetShortPathName (SELFEX.szMax, SELFEX.szMax, PATH)!=0 ) &&
(GetEnvironmentVariable ("COMSPEC", SELFEX.szCline, PATH)!=0 ) )
{
lstrcpy (SELFEX.szFlags,"/c del ");
lstrcat (SELFEX.szFlags, SELFEX.szMax);
lstrcat (SELFEX.szFlags, " >> NUL");
ModifyStru (&SELFEX);
if( ShellExecuteEx (&SELFEX.ShExecInfo) )
{
SetPriorityClass (SELFEX.ShExecInfo.hProcess,IDLE_PRIORITY_CLASS);
SetThreadPriority (GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);
SetPriorityClass (GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
SHChangeNotify (SHCNE_DELETE,SHCNF_PATH,SELFEX.szMax,0);
return 1;
}
}
return 0;
}
Dick ModifyStru(_SELFEX *struExec)
{
(*struExec).ShExecInfo.cbSize = sizeof((*struExec).ShExecInfo);
(*struExec).ShExecInfo.lpVerb = "Open";
(*struExec).ShExecInfo.lpFile = (*struExec).szCline;
(*struExec).ShExecInfo.lpParameters = (*struExec).szFlags;
(*struExec).ShExecInfo.lpDirectory = 0;
(*struExec).ShExecInfo.nShow = SW_HIDE;
(*struExec).ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
(*struExec).ShExecInfo.hwnd = 0;
}
/*
__END__
*/
Sorunsalımız, gördüğümüz üzere tamamen bir dosyanın kendini imha etmesinden ibaret. Pekii, bu nasıl gerçekleştirilir? Aslında Windows kernelinin bizlere bu noktada izin vermediğini biliyoruz, ancak aynı zamanda bize bunun için püf noktaları da sağlıyor. Hepsinden önce, sadece WINAPI kullanılarak göründüğü üzere bu tarz bir uygulama tasarlanabilir, gelin birlikte uygulamamıza göz atalım. Kullanacağımız işletim sistemi, uygulamamızın işlenen ilk satırından da belli olacağı üzere (#define WINVER 0x0501) Windows XP olacaktır. Gerekli olan kütüphanelerimizi,
#include <Windows.h>
#include <Shlobj.h>
içe aktararak uygulamamızda kullanacağımız temel WINAPI fonksiyonlarına erişmiş olacağız. Bazı parametrelerimizi saklayan bir "struct" yapısı veritipi ile fonksiyon prototipi tanımlayarak, "WinMain" fonksiyonunu tanımladıktan sonra bunu kullanabilme şansı kazanıp, kod okunabilirliğini artırdık. Bundan evvel de, "PATH" adındaki bir sabite MAX_PATH değişkenini atayarak, harften tasarruf ettik (:Hazırlık evresini anlattığımıza göre, şimdi mimarileri daha yakından inceleyebiliriz. Öncelikle "_SELFEX" ismine sahip veritipimizi biraz inceleyelim. Göründüğü üzere, "struct" veritipimiz 4 adet düzenlenmeyi bekleyen bellek bölgelerine sahiptir. Bunlar,
[*]ShExecInfo (SHELLEXECUTEINFO)
[*]szMax[MAX_PATH] (TCHAR)
[*]szCline[MAX_PATH] (TCHAR)
[*]szFlags[MAX_PATH] (TCHAR)
olarak sıralanabilir. ShExecInfo, daha sonra kullanacağımız "ShellExecuteEx" tarafından kullanılacak gerekli önayarları saklar iken; szMax, szCline ve szFlags sırasıyla dosya yolunu, komut satırının bulunduğu dizini ve komut satırı argümanlarını barındırır. Sonrasında veritipimiz, kabaca bir tabir ile "SELDEL" isminde yoluna devam eder.
typedef struct _SELFEX
{
SHELLEXECUTEINFO ShExecInfo;
TCHAR szMax [PATH],
szCline [PATH],
szFlags [PATH];
} SELFDEL, *PSELFDEL;
Geldik ana yapıyaa.. "WINAPI WinMain" içerisinde yer alan bloga baktığımız zaman, ilk satırda bizi "SELFDEL SELFEX;" şeklinde bir tanımlama karşılıyor. Böylece, önceden hazırladığımız veritipine sahip bir yapı oluşturuyoruz. Bu satırı da atladığımızda, karşımıza bir kontrol blogu çıkıyor.SELFDEL SELFEX;
if( (GetModuleFileName (0, SELFEX.szMax, PATH)!=0 ) &&
(GetShortPathName (SELFEX.szMax, SELFEX.szMax, PATH)!=0 ) &&
(GetEnvironmentVariable ("COMSPEC", SELFEX.szCline, PATH)!=0 ) )
{
.. kod
}
Böylelikle, hem az önce bahsettiğimiz "struct" değişkenlerine verileri atayıp, döndürdükleri değerlere göre (0->False) kontrolümüzü gerçekleştiriyoruz. Herhangi bir hatanın gerçekleşmesi durumunda 0 değerini döndüreceğinden, hata gerçekleşmesi durumunda direkt blogun atlanmasını sağlıyoruz.Diyelim ki normal olarak uygulama çalıştı, arkaplanda herhangi bir hata gerçekleşmedi. Böyle bir durumda kontrol blogumuzun içerisindeki koda giriş yaptık. Bizi, aşağıdaki kod parçacığı karşılayacaktır,
lstrcpy (SELFEX.szFlags,"/c del ");
lstrcat (SELFEX.szFlags, SELFEX.szMax);
lstrcat (SELFEX.szFlags, " >> NUL");
Bu kod parçacığını Türkçeye dökmek ister isek, "SELFEX" struct değişkenimizin içerisinde yer alan değişkenlerden birisi olan "szFlags", yani komutsatırı argümanlarını barındıran değişkene gerekli değerleri atıyor. Bu değerlerin şekillenmiş hali, /c del <szMax> >> NUL
misali olacaktır. Sonraki satıra göz attığımız zaman görüyoruz ki, bizim önceden tanımladığımız fonksiyon prototipi karşımıza çıkıyor. Hemen derleyicinin yaptığı gibi, fonksiyonu barındıran satıra geçici olarak atlıyoruz.ModifyStru (&SELFEX);
Dick ModifyStru(_SELFEX *struExec)
{
(*struExec).ShExecInfo.cbSize = sizeof((*struExec).ShExecInfo);
(*struExec).ShExecInfo.lpVerb = "Open";
(*struExec).ShExecInfo.lpFile = (*struExec).szCline;
(*struExec).ShExecInfo.lpParameters = (*struExec).szFlags;
(*struExec).ShExecInfo.lpDirectory = 0;
(*struExec).ShExecInfo.nShow = SW_HIDE;
(*struExec).ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
(*struExec).ShExecInfo.hwnd = 0;
}
Bu fonksiyonumuzun amacı, gerektirdiği argümanına da bakıldığı üzere (_SELFEX *struExec) yazımızın başında tanımladığımız veritipimize sahip herhangi bir değişkeni alıp, şekillendirmek. Bu fonksiyonumuzun veritipimiz üzerinde şekillendireceği değişkenimiz de, koda baktığımız zaman "ShellExecuteEx" fonksiyonunda kullanacağımız, gerekli ayarlamaları saklayan "ShExecInfo" yapımızın olduğunu görüyoruz. Bu ayarlamaların detaylarını ve daha fazlasını, açık kütüphane olan MSDN üzerinden öğrenebilirsiniz. Benim şekillendirmemi kısaca tanımlamak ister isek, "lpFile" parametresinde yer alan dosyayı açıp, gerekli argümanları açılan dosyaya verip daha sonra kullanacağımız "hProcess" elemanına "process"in işlenmesini belirterek, pencereyi saklıyor.Yukarıdaki açıklamadan yola çıkarak, "ModifyStru (&SELFEX);" satırının önceden tanımladığımız "SELFEX" değişken adresini alıp, gerekli ayarlamalar ile işleyerek şekillendirdiğini söyleyebiliriz.
ModifyStru (&SELFEX);
Bu kısmı da atladığımıza göre, doğal olarak karşımıza yeni bir kontrol blogu çıkmakta. "ShellExecuteEx (&SELFEX.ShExecInfo)" kontrolüne bakıyoruz ki, önayarlamalarımızdan yola çıkarak, "ShellExecuteEx" fonksiyonunu kullanıp belirtilen dosya üzerinde işlem yaparak döndürdüğü değere göre işlem yapıyor. Herhangi bir hata vermemesi durumunda da, blogun içerisindeki kodu çalıştırıyor.if( ShellExecuteEx (&SELFEX.ShExecInfo) )
{
SetPriorityClass (SELFEX.ShExecInfo.hProcess,IDLE_PRIORITY_CLASS);
SetThreadPriority (GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);
SetPriorityClass (GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
SHChangeNotify (SHCNE_DELETE,SHCNF_PATH,SELFEX.szMax,0);
return 1;
}
Yukarıdaki kodumuza da bakacak olur isek, önce "SELFEX" yapımızdaki ayarlamaların bir elemanı olan "hProcess"ın öncelik sınıfını bir nevi aylak olarak tanımlıyor. Sonrasında çağrılan "thread"ın önceliğini "THREAD_PRIORITY_TIME_CRITICAL" olarak tanımlıyor, ki öncelik sırasında ön safhalara gelsin. Sonrasında şu anki işlemin önceliğini olası en yüksek safhaya getirip, yaptığı işlemi sisteme bildiriyor. Ve mutlu son.Hemen uygulamamızı derleyip (MinGW) işlevini kontrol edelim. Evett, çalışıyor - e doğal olarak, boşuna yazmadık o kadar satırı. Lakin bir sıkıntı var, dosya boyutu o kadar büyük ki - rahatlıkla fark edilebilir, tamı tamına 32.342 bayt. Bu biraz da C dilinin azizliğinden kaynaklı, hadi şimdi de C dilinden farklı olarak, kardeşi olan Assembly ile benzer bir uygulama yazalım. Mantık aynı, sadece sözdizimi farklı. Bakalım bu ne kadar alan tutacak,
.386
option casemap:none
include \masm32\include\masm32rt.inc
.data
align 4
szDel db "/c DEL %s", 0
szCSPC db "COMSPEC", 0
szNull db " >> NULL", 0
szInfo db "Varsa sekerin patlayalim sekerim.", 0
szInfoT db "Elveda Zalim Dunya", 0
.data?
szName db MAX_PATH dup(?)
szCmd dd ?
szCmdP db MAX_PATH dup(?)
szParam db MAX_PATH dup(?)
pInfo PROCESS_INFORMATION <>
sInfo STARTUPINFO <>
.code
start:
invoke MessageBox, 0, addr szInfo, addr szInfoT, MB_OK
xor eax, eax
invoke GetEnvironmentVariable, addr szCSPC, addr szCmdP, MAX_PATH
invoke GetCommandLine
mov szCmd, eax
invoke wsprintf, addr szParam, addr szDel, szCmd
invoke RtlZeroMemory, addr sInfo, sizeof sInfo
mov sInfo.cb, sizeof sInfo
invoke CreateProcess, addr szCmdP, addr szParam, NULL, NULL,\
FALSE, CREATE_NO_WINDOW or CREATE_SUSPENDED, NULL,\
NULL, addr sInfo, addr pInfo
invoke SetPriorityClass, pInfo.hProcess, IDLE_PRIORITY_CLASS
invoke SetThreadPriority, pInfo.hThread, THREAD_PRIORITY_TIME_CRITICAL
invoke GetCurrentProcess
push eax
invoke SetPriorityClass, eax, REALTIME_PRIORITY_CLASS
invoke GetCurrentThread
invoke SetThreadPriority, eax, THREAD_PRIORITY_HIGHEST
invoke ResumeThread, pInfo.hThread
pop eax
invoke TerminateProcess,eax,0
end start
Kodu kısaca açıklamak gerekir ise, aynen C örneğimizde veritipi üzerindeki değişkenlerimizi tanımladığımız gibi, ".data" segmentinde değişkenlerimizi tanımlıyoruz, bunun dışında ayrıyeten "masm32" built-in kütüphanelerinden birisi olan, WINAPI elemanlarına erişmemizi sağlayan "masm32rt.inc"i de içe aktarmayı unutmuyoruz.
.data
szDel db "/c DEL %s", 0
szCSPC db "COMSPEC", 0
szNull db " >> NULL", 0
szInfo db "Varsa sekerin patlayalim sekerim.", 0
szInfoT db "Elveda Zalim Dunya", 0
.data?
szName db MAX_PATH dup(?)
szCmd dd ?
szCmdP db MAX_PATH dup(?)
szParam db MAX_PATH dup(?)
pInfo PROCESS_INFORMATION <>
sInfo STARTUPINFO <>
Sonraki kısımlarda ise, aynen değişkenlerimize gerekli değerlerini atayıp, tekrardan açıkladığımız adımları uyguluyoruz, bir de bu örneğe küçük bir mesaj kutucuğu da ekledik. Hemen masm32 ile derleyip, çalıştıralım bakalım.Sonuç? Şekildeki gibidir, hemen küçük bir hatırlatma yapalım - MessageBox herhangi bir thread oluşturmayıp, main-thread üzerinden çalışıp buna göre değer döndürdüğünden dolayı, "OK" butonuna tıklayana kadar diğer işlemlere geçilmez, demedi demeyin.. Boyutuna baktığımız zaman, tadaa.. 2.560 bayt.
Bir de "zztri" üstadımızın dediği gibi, bir "batch" (*.bat) dosyası oluşturup dosyamızı sildirelim. Bunun için, tekrardan masm32 sözdizimine göre gerekli kütüphanemizden yola çıkarak dosyamızı oluşturup (*.bat), içerisine bir kontrol blogu ekleyip, dosya var ise sildireceğiz. Diğer türlü etiketimize gidip duracak. Assembly masm32 kaynak kodumuz şekil A'da gözüktüğü gibidir,
.386
option casemap:none
include \masm32\include\masm32rt.inc
.data
szNBuf db 0ffh dup(0)
szFile db "infaz.bat", 0
szSrc db ":loop", 13, 10, \
"del ""%s""", 13, 10, \
"if exist ""%s"" goto :loop", 0
.data?
hFile HANDLE ?
szPath LPSTR ?
szSPath LPSTR ?
szTotal DWORD ?
.code
@Quit:
invoke ExitProcess, NULL
start:
invoke GetModuleFileName, NULL, addr szPath, MAX_PATH
invoke GetShortPathName ,addr szPath ,addr szSPath , 0
invoke CreateFile, addr szFile, GENERIC_WRITE, 0, NULL, \
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
mov hFile, eax
cmp eax, 0FFFFFFh
jz @Quit
invoke wsprintf, addr szNBuf, addr szSrc,addr szPath ,addr szPath
invoke lstrlen, addr szNBuf
invoke WriteFile, hFile, addr szNBuf, eax, addr szTotal, NULL
invoke CloseHandle, hFile
invoke WinExec, addr szFile, SW_HIDE
end start
Oluşturulan dosyamızın içeriği de, aşağıdaki gibidir (infaz.bat);:loop
del <dosya dizini>
if exist <dosya dizini> goto :loop
Evet, bugün sizlerle birlikte basit bir örnek üzerinden, en kolay hali ile hiç bellek mimarisine vesaire girmeden bu olayın nasıl gerçekleştirildiğini öğrendik. Bu her ne kadar kolay olsa da, özünde çok zor bir şeydir. Unutmamak gerekli.. Ayrıca bunu da unutmayın, mapushanede hayat bir başkadır. Bu örnekte kullanılan her türlü tekniğe, MSDN kütüphanesinden ulaşabilirsiniz. Bu yazımda elimden geldiğince konuyu herkesin anlayabileceği seviyede tutmaya çalıştım. Sevgiler..