最近因為來了台新server 主機需要把原本的sql server 2008 上的資料
移轉到新主機上,因為database 有20多個,如果一個一個用 restore 的方式
還要一個一個建立database 再 restore 有些麻煩
於是決定使用直接copy .mdf .ldf 資料庫檔案的方式來移轉
但因為資料庫檔案也是分散在不同disk 上,用手動copy 又怕有漏掉的
網路上查了一下找到一個可以查出資料庫檔案的路徑及檔名的sql 指令
SELECT physical_name from FROM sys.master_files
我再修改一下這個sql ,將結果存成一個 old2new.bat 檔
SELECT 'copy "'+ physical_name + '" "\\192.168.0.100\e$\db files"' AS current_file_location
FROM sys.master_files s order by physical_name
old2new.bat 的內容
copy "C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\DB_COLLECT.mdf" "\\192.168.0.100\e$\db files"
copy "C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\DB_COLLECT_log.ldf" "\\192.168.0.100\e$\db files"
....
在原主機上執行 old2new.bat
把檔案copy 到新主機(sql sevrer 的服務要先停掉)
再使用下列sql ,將結果存成 new2path.bat,
select 'copy "e:\db files\'+file_name+'" "'+ physical_name +'"' AS current_file_location from (
SELECT physical_name ,
LTRIM( RTRIM( REVERSE( SUBSTRING( REVERSE(physical_name), 0, CHARINDEX('\', REVERSE(physical_name),0) ) ) )) as file_name
FROM sys.master_files s ) as a
order by physical_name
new2path.bat 內容
copy "e:\db files\DB_COLLECT.mdf" "C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\DB_COLLECT.mdf"
copy "e:\db files\DB_COLLECT_log.ldf" "C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\DB_COLLECT_log.ldf"
copy "e:\db files\master.mdf" "C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\master.mdf"
copy "e:\db files\mastlog.ldf" "C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\mastlog.ldf"
....
停止新主機上的sql server 服務,相關的目錄建立好,再於新主機上執行 new2path.bat 將資料庫檔案copy 到對應的目錄,copy 完成後,啟動 sql server 服務,新的sql server 主機上的資料就與原主機相同,經測試移轉 18 G 的資料庫檔案,只花了 10分鐘完成此次移轉
不過移轉後發現,原主機上的維護計劃排程,會有無法正常執行情形
必需要重新新增維護計劃才會正常執行
搜尋此網誌
2011年11月9日 星期三
2011年11月1日 星期二
如何在android 中查詢來電記錄使用日期過濾(CallLog filter DATE)
如何在android 中查詢來電記錄使用日期過濾
cursor = this.managedQuery(
android.provider.CallLog.Calls.CONTENT_URI,
strFields,
android.provider.CallLog.Calls.DATE+" >='"+sdate+"'",
null,
strOrder
);
sdate='2011-11-1'
之前使用上列的字串日期來查詢,但總是傳回0筆資料
為了解決這個問題,google查了好久,但始終找不到解法
最後決定由sqlite3 資料庫來查看要如何解決
來電記錄的 db 檔在 cd /data/data/com.android.providers.contacts/databases/contacts2.db 裏
先用 adb shell 進入 command shell
cd /data/dara/android.provider.contacts/databases/
sqlite3 contacts2.db
進入 sqlite3
先看一下來電記錄裏的資料長得怎樣
sqlite> select * from calls;
select * from calls;
1|0387232|1319211664323|0|1|1||0|
2|0387232|1319211858582|0|1|1||0|
3|0388709|1319715842190|8|1|1|huang|1|
4|0832413|1320148395215|2|1|1||0|
單獨select 出 date
sqlite> select date from calls;
select date from calls;
1319211664323
1319211858582
1319715842190
1320148395215
發現date 存在資料庫是 long 的數字型態,代表1970/1/1 至今的毫秒數
查了一下 sqlite3 的日期 function
可以用下列指令轉換為字串
SELECT datetime(date/1000, 'unixepoch') from calls;
2011-10-21 15:41:04
2011-10-21 15:44:18
2011-10-27 11:44:02
2011-11-01 11:53:15
終於得到正確的日期字串
再配合substr總算可以用字串日期過濾了
SELECT * from calls where substr(datetime(date/1000, 'unixepoch'),1,10)='2011-11-01';
把之前的query 條件改成下列
cursor = this.managedQuery(
android.provider.CallLog.Calls.CONTENT_URI,
strFields,
"substr(datetime("+android.provider.CallLog.Calls.DATE+"/1000, 'unixepoch'),1,10) ='"+sdate+"'",
null,
strOrder
);
成功的傳回資料了
cursor = this.managedQuery(
android.provider.CallLog.Calls.CONTENT_URI,
strFields,
android.provider.CallLog.Calls.DATE+" >='"+sdate+"'",
null,
strOrder
);
sdate='2011-11-1'
之前使用上列的字串日期來查詢,但總是傳回0筆資料
為了解決這個問題,google查了好久,但始終找不到解法
最後決定由sqlite3 資料庫來查看要如何解決
來電記錄的 db 檔在 cd /data/data/com.android.providers.contacts/databases/contacts2.db 裏
先用 adb shell 進入 command shell
cd /data/dara/android.provider.contacts/databases/
sqlite3 contacts2.db
進入 sqlite3
先看一下來電記錄裏的資料長得怎樣
sqlite> select * from calls;
select * from calls;
1|0387232|1319211664323|0|1|1||0|
2|0387232|1319211858582|0|1|1||0|
3|0388709|1319715842190|8|1|1|huang|1|
4|0832413|1320148395215|2|1|1||0|
單獨select 出 date
sqlite> select date from calls;
select date from calls;
1319211664323
1319211858582
1319715842190
1320148395215
發現date 存在資料庫是 long 的數字型態,代表1970/1/1 至今的毫秒數
查了一下 sqlite3 的日期 function
可以用下列指令轉換為字串
SELECT datetime(date/1000, 'unixepoch') from calls;
2011-10-21 15:41:04
2011-10-21 15:44:18
2011-10-27 11:44:02
2011-11-01 11:53:15
終於得到正確的日期字串
再配合substr總算可以用字串日期過濾了
SELECT * from calls where substr(datetime(date/1000, 'unixepoch'),1,10)='2011-11-01';
把之前的query 條件改成下列
cursor = this.managedQuery(
android.provider.CallLog.Calls.CONTENT_URI,
strFields,
"substr(datetime("+android.provider.CallLog.Calls.DATE+"/1000, 'unixepoch'),1,10) ='"+sdate+"'",
null,
strOrder
);
成功的傳回資料了
2011年9月2日 星期五
2011年6月23日 星期四
使用C++ Builder 來開發 Amibroker 的 Plugin
經過最近的收集資料及測試,終於成功寫出一個基本架構
首先第一步要先使 CBB 建立出來的DLL export function 能夠被 Amibroker 正確識別出來
CBB 與 VC 做出來的DLL 在export function 的 name 有所不同
使用 __cdecl 的 function 在 CBB 會有底線而 VC 沒有
這樣一來Amibroker 無法找到要用的 function name 而無法載入plugin DLL
解決方法是把CBB 的DLL 用 CBB 的 tool impdef 建立 .def 檔再去修改,把修改後的 def 加入CBB Project 中,就可以做出 有底線跟沒底線 function name
例如:
下指令 impdef plugin3.def plugin3.dll
plugin3.def 內容如下
_Configure @1
改為
ConFigure = _Configure
就可做出Amibroker 正確識別 export function name
做好之後.如果有新增的 export function ,就要再做一次全部對應的修改
不可以像這樣
_Configure @1
Configure @2
Init = _init
必需全部都改成
ConFigure = _Configure
Init = _init
才會正確對應,否則 Amibroker 還是會捉不到
另外 Amibroker ADK 附的 pluging.h 在 export 的宣告,都要多加 _cdecl
傳入參數方式對應才會正確
首先第一步要先使 CBB 建立出來的DLL export function 能夠被 Amibroker 正確識別出來
CBB 與 VC 做出來的DLL 在export function 的 name 有所不同
使用 __cdecl 的 function 在 CBB 會有底線而 VC 沒有
這樣一來Amibroker 無法找到要用的 function name 而無法載入plugin DLL
解決方法是把CBB 的DLL 用 CBB 的 tool impdef 建立 .def 檔再去修改,把修改後的 def 加入CBB Project 中,就可以做出 有底線跟沒底線 function name
例如:
下指令 impdef plugin3.def plugin3.dll
plugin3.def 內容如下
_Configure @1
改為
ConFigure = _Configure
就可做出Amibroker 正確識別 export function name
做好之後.如果有新增的 export function ,就要再做一次全部對應的修改
不可以像這樣
_Configure @1
Configure @2
Init = _init
必需全部都改成
ConFigure = _Configure
Init = _init
才會正確對應,否則 Amibroker 還是會捉不到
另外 Amibroker ADK 附的 pluging.h 在 export 的宣告,都要多加 _cdecl
傳入參數方式對應才會正確
2011年6月22日 星期三
冰箱除霜定時器發出怪聲,DIY 修復
上週起,家中的一台小東元冰箱一直發出怪聲,把冰箱推出來後仔細聽之後
冰箱後頭,拆下紅圈2個螺絲,定時器在裏頭
找到怪聲來源在冰箱後面,由於之前也曾找過服務站的人處理過別的問題,但只要叫人來看,就要付600檢測費,想說先上網查一下是否有人也有同樣情形,還真的找到了
原來怪聲是來自定時器的齒輪,只是換一個新的定時器就可以了
一個白色的零件,接了4條線,還韓國製的
到拍賣網站及一些購物網站找了一下,找到一個適用於東元冰箱的
買到這個型號
等了2天,東西到了,開始進行拆換
買到的零件長這個樣子,但上頭竟沒產地,沒辦法,還是只有換了
首先要拆故障的定時器,觀察了一下,發現上頭還被一個電容器擋住,要先拆電容器
才能拆出定時器
被一個黑色電容器擋住取出的路,要先拆下電容器
電容器上有2條接線,要先記住接的對應顏色,等會才能正確接回
固定的螺絲在這個位置
電容拆下後,定時器就很好拆了,定時器上也有4條線,分成2個接頭,一個一條線,一個3條線,這樣不容易弄錯
換上新的定時器,並接上接頭
把定時器及電容再鎖回去,電容注意一下接線顏色,冰箱接電測試
怪聲消失了,大功告成 !
冰箱後頭,拆下紅圈2個螺絲,定時器在裏頭
找到怪聲來源在冰箱後面,由於之前也曾找過服務站的人處理過別的問題,但只要叫人來看,就要付600檢測費,想說先上網查一下是否有人也有同樣情形,還真的找到了
原來怪聲是來自定時器的齒輪,只是換一個新的定時器就可以了
一個白色的零件,接了4條線,還韓國製的
到拍賣網站及一些購物網站找了一下,找到一個適用於東元冰箱的
買到這個型號
等了2天,東西到了,開始進行拆換
買到的零件長這個樣子,但上頭竟沒產地,沒辦法,還是只有換了
首先要拆故障的定時器,觀察了一下,發現上頭還被一個電容器擋住,要先拆電容器
才能拆出定時器
被一個黑色電容器擋住取出的路,要先拆下電容器
電容器上有2條接線,要先記住接的對應顏色,等會才能正確接回
固定的螺絲在這個位置
電容拆下後,定時器就很好拆了,定時器上也有4條線,分成2個接頭,一個一條線,一個3條線,這樣不容易弄錯
換上新的定時器,並接上接頭
把定時器及電容再鎖回去,電容注意一下接線顏色,冰箱接電測試
怪聲消失了,大功告成 !
2011年6月17日 星期五
成功將一個程式 使用的 lib 轉為 IDA signature 檔
在pediy 看到相關的ida sig 檔製作資料
剛好在追踪的的一個程式有一個 lib 檔
便試著轉看看
首先要有 ida pro 及 Ida flair util
因為要用到裏頭的 dumpsig.exe , sigmake.exe
再用由pediy 下載的 lib2sig.bat 相關檔案
執行前設定command 模式下的 path,設定指向 vc6\bin 中放 link.exe 的位置
再執行 lib2sig 成功做出 sig 檔
再放到 ida 下的sig目錄 ,重新使用flirt 便可
剛好在追踪的的一個程式有一個 lib 檔
便試著轉看看
首先要有 ida pro 及 Ida flair util
因為要用到裏頭的 dumpsig.exe , sigmake.exe
再用由pediy 下載的 lib2sig.bat 相關檔案
執行前設定command 模式下的 path,設定指向 vc6\bin 中放 link.exe 的位置
再執行 lib2sig 成功做出 sig 檔
再放到 ida 下的sig目錄 ,重新使用flirt 便可
2011年2月28日 星期一
SSDT Hook的妙用-對抗ring0 inline hook
最近開始研究怎麼寫外掛,開始要看一些較底層的東西
把一些網路上收集到的文章整理起始
標 題: 【原創】SSDT Hook的妙用-對抗ring0 inline hook
作 者: 墮落天才
時 間: 2007-03-10,15:18:32
鏈 接: http://bbs.pediy.com/showthread.php?t=40832
*******************************************************
*標題:【原創】SSDT Hook的妙用-對抗ring0 inline hook *
*作者:墮落天才 *
*日期:2007年3月10號 *
*聲明:本文章的目的僅為技術交流討論 *
*******************************************************
1,SSDT
SSDT即系統服務描述符表,它的結構如下(參考《Undocument Windows 2000 Secretes》第二章):
typedef struct _SYSTEM_SERVICE_TABLE
{
PVOID ServiceTableBase; //這個指向系統服務函數位址表
PULONG ServiceCounterTableBase;
ULONG NumberOfService; //服務函數的個數,NumberOfService*4 就是整個位址表的大小
ULONG ParamTableBase;
}SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
SYSTEM_SERVICE_TABLE ntoskrnel; //ntoskrnl.exe的服務函數
SYSTEM_SERVICE_TABLE win32k; //win32k.sys的服務函數,(gdi.dll/user.dll的內核支持)
SYSTEM_SERVICE_TABLE NotUsed1;
SYSTEM_SERVICE_TABLE NotUsed2;
}SYSTEM_DESCRIPTOR_TABLE,*PSYSTEM_DESCRIPTOR_TABLE;
內核中有兩個系統服務描述符表,一個是KeServiceDescriptorTable(由ntoskrnl.exe匯出),一個是 KeServieDescriptorTableShadow(沒有匯出)。兩者的區別是,KeServiceDescriptorTable僅有 ntoskrnel一項,KeServieDescriptorTableShadow包含了ntoskrnel以及win32k。一般的 Native API的服務位址由KeServiceDescriptorTable分派,gdi.dll/user.dll的內核API調用服務位址由 KeServieDescriptorTableShadow分派。還有要清楚一點的是win32k.sys只有在GUI執行緒中才載入,一般情況下是不加 載的,所以要Hook KeServieDescriptorTableShadow的話,一般是用一個GUI程式通過IoControlCode來觸發 (想當初不明白這點,藍屏死機了N次都想不明白是怎麼回事)。
2,SSDT HOOK
SSDT HOOK 的原理其實非常簡單,我們先實際看看KeServiceDescriptorTable是什麼樣的。
lkd> dd KeServiceDescriptorTable
8055ab80 804e3d20 00000000 0000011c 804d9f48
8055ab90 00000000 00000000 00000000 00000000
8055aba0 00000000 00000000 00000000 00000000
8055abb0 00000000 00000000 00000000 00000000
在windbg.exe中我們就看得比較清楚,KeServiceDescriptorTable中就只有第一項有資料,其他都是0。其中804e3d20就是
KeServiceDescriptorTable.ntoskrnel.ServiceTableBase,服務函數個數為0x11c個。我們再看看804e3d20地址裡是什麼東西:
lkd> dd 804e3d20
804e3d20 80587691 805716ef 8057ab71 80581b5c
804e3d30 80599ff7 80637b80 80639d05 80639d4e
804e3d40 8057741c 8064855b 80637347 80599539
804e3d50 8062f4ec 8057a98c 8059155e 8062661f
如上,80587691 805716ef 8057ab71 80581b5c 這些就是系統服務函數的位址了。比如當我們在ring3調用 OpenProcess時,進入sysenter的ID是0x7A(XP SP2),然後系統查KeServiceDescriptorTable,大概 是這樣 KeServiceDescriptorTable.ntoskrnel.ServiceTableBase(804e3d20) + 0x7A * 4 = 804E3F08, 然後804E3F08 ->8057559e 這個就是OpenProcess系統服務函數所在,我們再跟蹤看看:
lkd> u 8057559e
nt!NtOpenProcess:
8057559e 68c4000000 push 0C4h
805755a3 6860b54e80 push offset nt!ObReferenceObjectByPointer+0x127 (804eb560)
805755a8 e8e5e4f6ff call nt!InterlockedPushEntrySList+0x79 (804e3a92)
805755ad 33f6 xor esi,esi
原來8057559e就是NtOpenProcess函數所在的起始位址。
嗯,如果我們把8057559e改為指向我們函數的位址呢?比如 MyNtOpenProcess,那麼系統就會直接調用MyNtOpenProcess,而不是原來的NtOpenProcess了。這就是SSDT HOOK 原理所在。
3, ring0 inline hook
ring0 inline hook 跟ring3的沒什麼區別了,如果硬說有的話,那麼就是ring3發生什麼差錯的話程式會掛 掉,ring0發生什麼差錯的話系統就掛掉,所以一定要很小心。inline hook的基本思想就是在目標函數中JMP到自己的監視函數,做一些判斷然 後再JMP回去。一般都是修改函數頭,不過再其他地方JMP也是可以的。下面我們來點實際的吧:
lkd> u nt!NtOpenProcess
nt!NtOpenProcess:
8057559e e95d6f4271 jmp f199c500
805755a3 e93f953978 jmp f890eae7
805755a8 e8e5e4f6ff call nt!InterlockedPushEntrySList+0x79 (804e3a92)
...
同時打開“冰刃”跟“Rootkit Unhooker”我們就能在NtOpenProcess函數頭看到這樣的“奇觀”,第一個jmp是“冰 刃”的,第二個jmp是“Rootkit Unhooker”的。他們這樣是防止被惡意程式通過TerminateProcess關閉。當然“冰刃”還 Hook了NtTerminateProcess等函數。
×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
好了,道理就說完了,下面就進入本文正題。
對付ring0 inline hook的基本思路是這樣的,自己寫一個替換的內核函數,以NtOpenProcess為例,就是 MyNtOpenProcess。然後修改SSDT表,讓系統服務進入自己的函數MyNtOpenProcess。而MyNtOpenProcess要做 的事就是,實現NtOpenProcess前10位元組指令,然後再JMP到原來的NtOpenProcess的十字節後。這樣NtOpenProcess 函數頭寫的JMP都失效了,在ring3直接調用OpenProcess再也毫無影響。
***************************************************************************************************************************
#include
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
PVOID ServiceTableBase;
PULONG ServiceCounterTableBase;
ULONG NumberOfService;
ULONG ParamTableBase;
}SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE; // 由於KeServiceDescriptorTable只有一項,這裡就簡單點了
extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;//KeServiceDescriptorTable為匯出函數
/////////////////////////////////////
VOID Hook();
VOID Unhook();
VOID OnUnload(IN PDRIVER_OBJECT DriverObject);
//////////////////////////////////////
ULONG JmpAddress;//跳轉到NtOpenProcess裡的地址
ULONG OldServiceAddress;//原來NtOpenProcess的服務位址
//////////////////////////////////////
__declspec(naked) NTSTATUS __stdcall MyNtOpenProcess(PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PCLIENT_ID ClientId)
{
DbgPrint("NtOpenProcess() called");
__asm{
push 0C4h
push 804eb560h //共十個位元組
jmp [JmpAddress]
}
}
///////////////////////////////////////////////////
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = OnUnload;
DbgPrint("Unhooker load");
Hook();
return STATUS_SUCCESS;
}
/////////////////////////////////////////////////////
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
DbgPrint("Unhooker unload!");
Unhook();
}
/////////////////////////////////////////////////////
VOID Hook()
{
ULONG Address;
Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;//0x7A為NtOpenProcess服務ID
DbgPrint("Address:0x%08X",Address);
OldServiceAddress = *(ULONG*)Address;//保存原來NtOpenProcess的位址
DbgPrint("OldServiceAddress:0x%08X",OldServiceAddress);
DbgPrint("MyNtOpenProcess:0x%08X",MyNtOpenProcess);
JmpAddress = (ULONG)NtOpenProcess + 10; //跳轉到NtOpenProcess函數頭+10的地方,這樣在其前面寫的JMP都失效了
DbgPrint("JmpAddress:0x%08X",JmpAddress);
__asm{//去掉記憶體保護
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
*((ULONG*)Address) = (ULONG)MyNtOpenProcess;//HOOK SSDT
__asm{//恢復記憶體保護
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
//////////////////////////////////////////////////////
VOID Unhook()
{
ULONG Address;
Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;//查找SSDT
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
*((ULONG*)Address) = (ULONG)OldServiceAddress;//還原SSDT
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
DbgPrint("Unhook");
}
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
就這麼多了,或許有人說,沒必要那麼複雜,直接恢復NtOpenProcess不就行了嗎?對於象“冰刃”“Rookit Unhooker”這 些“善良”之輩的話是沒問題的,但是象NP這些“窮凶極惡”之流的話,它會不斷檢測NtOpenProcess是不是已經被寫回去,是的話,嘿嘿,機器馬 上重啟。這也是這種方法的一點點妙用。
作 者: 墮落天才
時 間: 2007-03-10,15:18:32
鏈 接: http://bbs.pediy.com/showthread.php?t=40832
*******************************************************
*標題:【原創】SSDT Hook的妙用-對抗ring0 inline hook *
*作者:墮落天才 *
*日期:2007年3月10號 *
*聲明:本文章的目的僅為技術交流討論 *
*******************************************************
1,SSDT
SSDT即系統服務描述符表,它的結構如下(參考《Undocument Windows 2000 Secretes》第二章):
typedef struct _SYSTEM_SERVICE_TABLE
{
PVOID ServiceTableBase; //這個指向系統服務函數位址表
PULONG ServiceCounterTableBase;
ULONG NumberOfService; //服務函數的個數,NumberOfService*4 就是整個位址表的大小
ULONG ParamTableBase;
}SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
SYSTEM_SERVICE_TABLE ntoskrnel; //ntoskrnl.exe的服務函數
SYSTEM_SERVICE_TABLE win32k; //win32k.sys的服務函數,(gdi.dll/user.dll的內核支持)
SYSTEM_SERVICE_TABLE NotUsed1;
SYSTEM_SERVICE_TABLE NotUsed2;
}SYSTEM_DESCRIPTOR_TABLE,*PSYSTEM_DESCRIPTOR_TABLE;
內核中有兩個系統服務描述符表,一個是KeServiceDescriptorTable(由ntoskrnl.exe匯出),一個是 KeServieDescriptorTableShadow(沒有匯出)。兩者的區別是,KeServiceDescriptorTable僅有 ntoskrnel一項,KeServieDescriptorTableShadow包含了ntoskrnel以及win32k。一般的 Native API的服務位址由KeServiceDescriptorTable分派,gdi.dll/user.dll的內核API調用服務位址由 KeServieDescriptorTableShadow分派。還有要清楚一點的是win32k.sys只有在GUI執行緒中才載入,一般情況下是不加 載的,所以要Hook KeServieDescriptorTableShadow的話,一般是用一個GUI程式通過IoControlCode來觸發 (想當初不明白這點,藍屏死機了N次都想不明白是怎麼回事)。
2,SSDT HOOK
SSDT HOOK 的原理其實非常簡單,我們先實際看看KeServiceDescriptorTable是什麼樣的。
lkd> dd KeServiceDescriptorTable
8055ab80 804e3d20 00000000 0000011c 804d9f48
8055ab90 00000000 00000000 00000000 00000000
8055aba0 00000000 00000000 00000000 00000000
8055abb0 00000000 00000000 00000000 00000000
在windbg.exe中我們就看得比較清楚,KeServiceDescriptorTable中就只有第一項有資料,其他都是0。其中804e3d20就是
KeServiceDescriptorTable.ntoskrnel.ServiceTableBase,服務函數個數為0x11c個。我們再看看804e3d20地址裡是什麼東西:
lkd> dd 804e3d20
804e3d20 80587691 805716ef 8057ab71 80581b5c
804e3d30 80599ff7 80637b80 80639d05 80639d4e
804e3d40 8057741c 8064855b 80637347 80599539
804e3d50 8062f4ec 8057a98c 8059155e 8062661f
如上,80587691 805716ef 8057ab71 80581b5c 這些就是系統服務函數的位址了。比如當我們在ring3調用 OpenProcess時,進入sysenter的ID是0x7A(XP SP2),然後系統查KeServiceDescriptorTable,大概 是這樣 KeServiceDescriptorTable.ntoskrnel.ServiceTableBase(804e3d20) + 0x7A * 4 = 804E3F08, 然後804E3F08 ->8057559e 這個就是OpenProcess系統服務函數所在,我們再跟蹤看看:
lkd> u 8057559e
nt!NtOpenProcess:
8057559e 68c4000000 push 0C4h
805755a3 6860b54e80 push offset nt!ObReferenceObjectByPointer+0x127 (804eb560)
805755a8 e8e5e4f6ff call nt!InterlockedPushEntrySList+0x79 (804e3a92)
805755ad 33f6 xor esi,esi
原來8057559e就是NtOpenProcess函數所在的起始位址。
嗯,如果我們把8057559e改為指向我們函數的位址呢?比如 MyNtOpenProcess,那麼系統就會直接調用MyNtOpenProcess,而不是原來的NtOpenProcess了。這就是SSDT HOOK 原理所在。
3, ring0 inline hook
ring0 inline hook 跟ring3的沒什麼區別了,如果硬說有的話,那麼就是ring3發生什麼差錯的話程式會掛 掉,ring0發生什麼差錯的話系統就掛掉,所以一定要很小心。inline hook的基本思想就是在目標函數中JMP到自己的監視函數,做一些判斷然 後再JMP回去。一般都是修改函數頭,不過再其他地方JMP也是可以的。下面我們來點實際的吧:
lkd> u nt!NtOpenProcess
nt!NtOpenProcess:
8057559e e95d6f4271 jmp f199c500
805755a3 e93f953978 jmp f890eae7
805755a8 e8e5e4f6ff call nt!InterlockedPushEntrySList+0x79 (804e3a92)
...
同時打開“冰刃”跟“Rootkit Unhooker”我們就能在NtOpenProcess函數頭看到這樣的“奇觀”,第一個jmp是“冰 刃”的,第二個jmp是“Rootkit Unhooker”的。他們這樣是防止被惡意程式通過TerminateProcess關閉。當然“冰刃”還 Hook了NtTerminateProcess等函數。
×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
好了,道理就說完了,下面就進入本文正題。
對付ring0 inline hook的基本思路是這樣的,自己寫一個替換的內核函數,以NtOpenProcess為例,就是 MyNtOpenProcess。然後修改SSDT表,讓系統服務進入自己的函數MyNtOpenProcess。而MyNtOpenProcess要做 的事就是,實現NtOpenProcess前10位元組指令,然後再JMP到原來的NtOpenProcess的十字節後。這樣NtOpenProcess 函數頭寫的JMP都失效了,在ring3直接調用OpenProcess再也毫無影響。
***************************************************************************************************************************
#include
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
PVOID ServiceTableBase;
PULONG ServiceCounterTableBase;
ULONG NumberOfService;
ULONG ParamTableBase;
}SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE; //
extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;//KeServiceDescriptorTable為匯出函數
/////////////////////////////////////
VOID Hook();
VOID Unhook();
VOID OnUnload(IN PDRIVER_OBJECT DriverObject);
//////////////////////////////////////
ULONG JmpAddress;//跳轉到NtOpenProcess裡的地址
ULONG OldServiceAddress;//原來NtOpenProcess的服務位址
//////////////////////////////////////
__declspec(naked) NTSTATUS __stdcall MyNtOpenProcess(PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PCLIENT_ID ClientId)
{
DbgPrint("NtOpenProcess() called");
__asm{
push 0C4h
push 804eb560h //共十個位元組
jmp [JmpAddress]
}
}
///////////////////////////////////////////////////
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = OnUnload;
DbgPrint("Unhooker load");
Hook();
return STATUS_SUCCESS;
}
/////////////////////////////////////////////////////
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
DbgPrint("Unhooker unload!");
Unhook();
}
/////////////////////////////////////////////////////
VOID Hook()
{
ULONG Address;
Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;//0x7A為NtOpenProcess服務ID
DbgPrint("Address:0x%08X",Address);
OldServiceAddress = *(ULONG*)Address;//保存原來NtOpenProcess的位址
DbgPrint("OldServiceAddress:0x%08X",OldServiceAddress);
DbgPrint("MyNtOpenProcess:0x%08X",MyNtOpenProcess);
JmpAddress = (ULONG)NtOpenProcess + 10; //跳轉到NtOpenProcess函數頭+10的地方,這樣在其前面寫的JMP都失效了
DbgPrint("JmpAddress:0x%08X",JmpAddress);
__asm{//去掉記憶體保護
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
*((ULONG*)Address) = (ULONG)MyNtOpenProcess;//HOOK SSDT
__asm{//恢復記憶體保護
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
//////////////////////////////////////////////////////
VOID Unhook()
{
ULONG Address;
Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;//查找SSDT
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
*((ULONG*)Address) = (ULONG)OldServiceAddress;//還原SSDT
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
DbgPrint("Unhook");
}
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
就這麼多了,或許有人說,沒必要那麼複雜,直接恢復NtOpenProcess不就行了嗎?對於象“冰刃”“Rookit Unhooker”這 些“善良”之輩的話是沒問題的,但是象NP這些“窮凶極惡”之流的話,它會不斷檢測NtOpenProcess是不是已經被寫回去,是的話,嘿嘿,機器馬 上重啟。這也是這種方法的一點點妙用。
訂閱:
文章 (Atom)