代碼,實現某個功能,直接Ctrl+C和Ctrl+V就可以解決問題。但是寫了驅動能加嗎?
載入內核是另壹回事,能不能存在別人硬盤上又是另壹回事。
因為很多殺毒軟件(尤其是像360這種沒有技術含量的)直接刪除後綴為sys的文件,
我甚至沒有機會打電話給NtLoadDriver。對於壹般的軟件,給出壹個語句來解釋解決方案。
別管解決方案了。但是對於惡意程序,妳無法給出壹個說法。所以許多惡意軟件作者找到了另壹種方法。
用大公司寫的,數字簽名的驅動幹壞事。
有人說,大公司做的驅動怎麽能用來幹壞事呢?其實這很好理解。
很多安全或者系統優化軟件,甚至是與系統無關的軟件(比如迅雷)都自帶驅動。
這些驅動器都是通用的。Q_lai_a_qu網友在博客中說:“ComputerZ.sys……...沒什麽。
反向是魯大師的驅動,發現這個驅動功能齊全,沒有經過調用者驗證!可以讀寫Msr。
寄存器,還可以用in和out指令讀寫端口,char/short/long數據長度齊全!"。這是
個人認為,請自行猜測可信度。先說壹個比較可信的例子:曾經有壹個病毒用了360。
antirk.dll刪除文件的殺毒軟件(請自己谷歌“360 antirk.dll”,妳會有驚喜。
AntiRK.dll不是司機,但也是非法使用)。破壞殺毒軟件的病毒是小兒科。
其實用壹些驅動也可以破壞硬件!最近在筆記本裏折騰硬件,“友協”上的網友給我推的。
推薦幾個軟件:SetFSB,ThrottleStop,NvFlash,WinFlash。他們正在修改CPU外部頻率,設置
設置CPU倍頻(CPU電壓可調)、讀寫圖形BIOS和讀寫主板BIOS的軟件。用壹句話概括他們的特點,
就是都支持NT x86/x64,驅動都有正式的數字簽名(尤其是最後兩個,分別有英偉達和華碩的數字簽名)。
最重要的是,他們的驅動程序沒有花花殼蓋,調用方也沒有經過驗證。
如果利用這些驅動和壹點逆向知識,就可以做出壹個破壞性的病毒(以下摘自我的紫水
水晶編程論壇帖子):
1.SetFSB可以調整處理器的外部頻率。如果直接把外接頻率調到600MHz,電腦會瞬間黑屏,可能是
會損壞CPU或主板;
2.ThrottleStop可以調整CPU的倍頻(如果CPU沒有鎖定倍頻),如果直接把倍頻調整到31,
電腦會瞬間黑屏,可能會損壞CPU或主板;ThrottleStop還可以調整CPU的核心電壓,如果
把CPU的核心電壓調到3V可以直接燒CPU甚至主板;
3.NvFlash、WinFlash等軟件可以直接讀寫BIOS(顯卡BIOS和主板BIOS),我們可以把
BIOS寫全零;
4.如果妳是病毒,先寫壞顯卡BIOS和主板BIOS,再通過調電壓燒顯卡和CPU。
(可能和主板壹起損壞);
解決辦法
可見,不驗證來電司機的危害是很大的。我最近被學院委任為
需要驅動程序的軟件(該驅動程序將被數字簽名)。為了防止上述悲劇發生,我決定正式下筆。
開車前,解決如何防止自己的司機被惡意利用。我以前在amethyst編程論壇上問過這個問題。
網友對這個問題的回答各種各樣,但大概可以分為三類:第壹類是信息驗證,比如應用程序。
按順序給司機發消息,驗證是“自己人”;第二類是外殼保護,比如添加到驅動和應用中。
在極其堅硬的外殼上,通信部分被VMP加密(類似於XueTr的做法);其他人提出混合應用、合成
第壹類和第二類相結合的做法。
這三個想法似乎都不錯,但我認為它們都不對。第壹種:別人只需要把所有的驅動都反過來就可以了。
的;第二種:雖然VMP保護和保護殼使破解變得困難,但並沒有使破解成為不可能。和
我不喜歡VMP和保護殼會降低程序執行的效率。最糟糕的是,殺毒軟件增加了壹個外殼(甚至)
甚至包括UPX和VMP在內的項目都被報道中毒,這是得不償失的。所以我想出了第三個主意:檢查呼叫者的
特色。如果是,執行函數語句,否則不執行。如何核對來電者的簽名?很多人都想
使用CRC32或MD5。不是不能用他們,而是我有自己的想法。我的想法來自
設計了壹套驗證算法,其規則如下:
1.獲取呼叫者的電子流程
2.通過調用者的EPROCESS獲取調用者的文件路徑。
3.獲取調用者文件的所有內容,並將其放入字節數組緩沖區。
4.依次加減buff中的所有元素(fb1+fb2-fb3...)才能得到y1。
5.依次異或buff中的所有元素(0 XOR fb1 XOR fb2 XOR fb3...)來獲得y2。
將y1和y2與計算值進行比較。如果都壹樣,執行功能代碼;如果它們不壹樣,它們就不壹樣。
執行功能代碼
用PsGetCurrentProcess()直接獲取調用者的EPROCESS,獲取調用者的文件路徑。
路徑比較麻煩,可以用我之前從高手那裏買的代碼(已經封裝成函數方便調用):
//根據e process獲取進程的完整路徑。
VOID getfullpathbyprocess(ULONG e process,PCHAR ProcessImageName)
{
ULONG對象;
PFILE _ OBJECT FileObject
UNICODE_STRING文件路徑;
UNICODE _ STRING DosName
字符串重排;
FileObject = NULL
文件路徑。Buffer = NULL
文件路徑。長度= 0;
* process imagename = 0;
//e process-& gt;section object(offset _ section object)
if(MmIsAddressValid((PULONG)(e process+offset _ section object)))
{
object =(*(PULONG)(e process+offset _ section object));
//KD print(("[GetProcessFileName]section object:0x % x \ n ",object));
if(MmIsAddressValid((PULONG)((ULONG)object+0x 014)))
{
object = *(PULONG)((ULONG)object+0x 014);
//KD print(("[GetProcessFileName]Segment:0x % x \ n ",object));
if(MmIsAddressValid((PULONG)((ULONG)object+0x 0)))
{
object = *(PULONG)((ULONG _ PTR)object+0x 0);
//KD print(("[GetProcessFileName]
ControlAera :0x%x\n ",object));
if(MmIsAddressValid((PULONG)((ULONG)object+0x 024)))
{
object = *(PULONG)((ULONG)object+0x 024);
if(NtBuildNumber & gt;= 6000)object =((ULONG)object & amp;
0x fffffff 8);
//KD print(("[GetProcessFileName]
FilePointer :0x%x\n ",object));
}
其他
返回;
}
其他
返回;
}
其他
返回;
}
其他
返回;
FileObject=(PFILE_OBJECT)對象;
文件路徑。buffer = ExAllocatePool(page pool,0x 200);
文件路徑。MaximumLength = 0x200
//KD print(("[GetProcessFileName]
文件指針:%wZ\n ",& amp文件指針-& gt;文件名));
ObReferenceObjectByPointer((PV oid)file object,0,NULL,kernel mode);
RtlVolumeDeviceToDosName(file object-& gt;設備對象。dos name);
RtlCopyUnicodeString(& amp;文件路徑。dos name);
rtlapendunicodestringtostring(& amp;文件路徑。file object-& gt;文件名);
ObDereferenceObject(file object);
rtlunicodestringtansigning(& amp;分配和分配。FilePath,TRUE);
如果(正在註冊。長度& gt= 216 )
{
memcpy(ProcessImageName,AnsiString。緩沖區,0x 100 u);
*(process imagename+215)= 0;
}
其他
{
memcpy(ProcessImageName,AnsiString。緩沖,分配。長度);
ProcessImageName[AnsiString。長度]= 0;
}
rtlfreeansigning(& amp;ansi ssing);
ExFreePool(DosName。緩沖);
ExFreePool(文件路徑。緩沖);
}
上面的代碼需要三個硬代碼,分別是NtBuildNumber(系統版本號)和EPROCESS。
SectionObject項和UniqueProcessId項的偏移量。我測試的操作系統是Windows 2003。因此
我在代碼中將其定義如下:
# define offset _ section object 0x 124
# define offset _ UniqueProcessId 0x 94
ULONG NtBuildNumber = 3790
獲取流程路徑後檢查簽名。因為流程已經說清楚了,所以直接給出代碼:
VOID CalcChar(puni code _ STRING logFileUnicodeString,LONG *XorChar,LONG
*AnSChar)
{
OBJECT_ATTRIBUTES對象屬性;
IO _ STATUS _ BLOCK iostatus
處理hfile
NTSTATUS ntStatus
文件_標準_信息;
普查爾·普弗;
ULONG i=0,y1=0,y2 = 0;
//初始化對象屬性
InitializeObjectAttributes(& amp;對象屬性,
logFileUnicodeString,
OBJ不區分大小寫,//區分大小寫。
空,
NULL);
//創建文件
ntStatus = ZwCreateFile(& amp;hfile,
GENERIC_READ
& amp對象屬性,
& ampiostatus,
空,
文件_屬性_正常,
文件共享讀取,
FILE_OPEN,//即使存在於這個文件中,也是創建的。
文件_同步_ IO _非警報,
空,
0 );
如果(!NT_SUCCESS(ntStatus))
{
dprintf("文件不存在!\ n ");
返回;
}
//讀取文件長度
ntStatus = ZwQueryInformationFile(hfile,
& ampiostatus,
& ampfsi,
sizeof(文件標準信息),
文件標準信息);
dprintf("程序想讀取%d字節\n ",fsi。endo ffile . quad part);
//為讀取的文件分配緩沖區
pBuffer =(PUCHAR)ExAllocatePool(paged pool,
(長)fsi。endo ffile . quad part);
//讀取文件
ZwReadFile(hfile,NULL,
空,空,
& ampiostatus,
p緩沖器,
(長)fsi。四部分,
NULL、NULL);
dprintf("程序確實讀取了%d字節\n ",iostatus。信息);
//XOR計算
for(I = 0;我& ltiostatus。信息;i++)
y1=y1^(long)(*(pbuffer+i));
* xor char = y 1;
//加減運算
for(I = 0;我& ltiostatus。信息;i++)
{
如果(i%2==0)
y2 = y2+(LONG)(*(p buffer+I));
其他
y2 = y2-(LONG)(*(p buffer+I));
}
* AnSChar = y2
//關閉文件句柄
zw close(hfile);
//釋放緩沖區
ex free pool(p buffer);
}
它將被稱為next。我們需要寫壹個函數VerifyCaller,這個函數裏有兩個值。
固化在驅動程序中的是合法調用者的兩個特征值。為了方便計算這兩個特征值,我特意寫了壹個
應用程序,核心代碼如下:
選項顯式
私有函數ReadFile(ByVal strFileName作為字符串,可選ByVal
lngStartPos As Long = 1,可選ByVallngFileSize As Long =-1)As Byte()
Dim FilNum As Long
FilNum = FreeFile
以#FilNum格式打開二進制文件的strFileName
如果lngFileSize = -1,則
ReDim ReadFile(LOF(FilNum)-lngStartPos)
其他
ReDim ReadFile(lngFileSize-1)
如果…就會結束
Get #FilNum,lngStartPos,ReadFile
關閉#FilNum
結束功能
私有函數WriteFile(ByVal strFileName為字符串,bytData()為字節,
可選ByVal lngStartPos As Long =-1,可選ByVal OverWrite As Boolean =
真實)
出錯時轉到erx
Dim FilNum As Long
FilNum = FreeFile
If OverWrite = True並且Dir(strFileName)& lt;& gt”“那麽
殺死strFileName
如果…就會結束
以#FilNum格式打開二進制文件的strFileName
如果lngStartPos = -1,則
把#FilNum,LOF(FilNum) + 1,bytData
其他
將#FilNum,lngStartPos,bytData
如果…就會結束
關閉#FilNum
erx:
結束功能
私有子命令1_Click()
Dim buff()為字節,I為Long,y為Long,ub為Long
text1.text是文件名
buff = ReadFile(Text1。Text,1,-1)
ub = UBound(buff)
計算異或字符
y = 0
對於i = 0至ub
y = y異或緩沖器(I)
然後
文本2。Text = CLng(y)
多項活動
計算添加/子字符
y = 0
對於i = 0至ub
如果我模2 = 0,那麽
y = y + CLng(buff(i))
其他
y = y - CLng(buff(i))
如果…就會結束
然後
短信3。Text = CLng(y)
末端接頭
私有子窗體_Load()
我。Icon = LoadPicture(" ")
末端接頭
驅動程序中的VerifyCaller代碼如下:
長驗證呼叫者(無效)
{
PEPROCESS cur _ ep
char cur _ PP[260];
char * nt _ cur _ pp
ANSI _ STRING asCur _ pp
UNICODE _ STRING usCur _ pp
LONG xorc,ansc
cur _ EP = PsGetCurrentProcess();
getfullpathbyprocess((ULONG)cur _ EP,cur _ PP);
//添加\?在文件名之前。\
nt_cur_pp=cs("\\?\\ ",cur _ PP);
DbgPrint("%s ",nt _ cur _ PP);
rtlinitansigning(& amp;asCur_pp,nt _ cur _ PP);
rtlansistringunicodestring(& amp;us cur _ PP & amp;asCur_pp,TRUE);
DbgPrint("%wZ ",& ampus cur _ PP);
卡爾查爾(& ampus cur _ PP & amp;xorc & amp;ansc);
dbg print(" xor char:% LD;AnSChar: %ld ",xorc,ansc);
//這是合法程序預先計算好的特征碼,必須固化在驅動裏!
if(xorc = = 186 & amp;& ampansc==136176)
返回1;
其他
返回0;
}
在執行DispatchIoctl函數的每個函數之前,調用VerifyCaller()來檢查調用者:
開關(uIoControlCode)
{
案例IOCTL_VERIFY:
{
dbg print("[my driver]DispatchIoctl-IOCTL _ VERIFY ");
if(VerifyCaller()==1)
dbg print("[my driver]{ IOCTL _ VERIFY }函數代碼現在運行!");
其他
dbg print("[my driver]{ IOCTL _ VERIFY }您是非法調用者!");
狀態=狀態_成功;
打破;
}
//下面省略
}
試探性試驗
3.首先把合法調用者和非法調用者(用eXeScope隨便給合法調用者打補丁,
例如刪除程序的版本信息)並將驅動程序復制到虛擬機。
4.用合法的調用者加載驅動程序並執行它。
5.用非法調用者加載驅動程序並執行它。
6.在DbgView中比較以上兩者的輸出。
當呼叫者合法時:
當呼叫者是非法的:
寫在最後
寫完這篇文章,我必須重申,只有當司機攜帶正式的數字簽名,才能調用方的。
代碼是有用的。為什麽這麽說?因為其他人不能用正式的數字簽名修補驅動程序(壹次
當驅動是補丁的時候,簽名就會失效,就像壹個被打碎的女人,壹文不值。這個比喻雖然俗,但是很
合適)。沒有簽名驅動,就沒有使用價值。就算別人想用,扔司機就行了。
在IDA中,所有的代碼都出來了。