註冊表啊 註冊表

QQ:121121606 E-main:[email protected]  
Http://blog.csdn.net/chinafe  冷風於2008-5-9


木馬編程DIY第6篇之註冊表管理   文/圖 冷風



前幾期分別討論了,系統服務,文件傳輸,網絡文本語音和其它方面的內容,現在我們來討論在木馬中註冊表的實現,就我自己感覺在木馬中對註冊表的使用並不多,寫其它程序時反倒用的不少.不過註冊表還是比較重要的學會它不會吃虧呵呵,我們自己實現的效果如圖所示

一些基礎

開始前先了解一些基礎知識,對後面的工作會方便不少,其碼不會出現散暈的現像,對註冊錶的歷史也就不再說了有興趣可以
查查新華字典呵呵.註冊表的組織方式跟文件目錄比較相似,主要分爲 跟鍵,子鍵,鍵值項 三部分跟文件目錄對應的話就是
跟目錄,子目錄,和文件.分別介紹一下,跟鍵爲分5個分別爲HKEY_CLASSES_ROOT,HKEY_CURRENT_USER,HKEY_LOCAL_MACHINE
HKEY_USERS,HKEY_CURRENT_CONFIG把它們理解成磁盤的五個分區可以了,子鍵可以有多個子鍵和鍵值項,就像一個目錄中可
以有多個子目錄和多個文件一樣,而鍵值項可以理解爲文件它由三部分組成,分別爲 名稱,類型,數據. 類型又分爲多種主要
包括如下:

REG_BINARY        二進制數據

REG_DWORD        32位雙字節數據

REG_SZ            以0結尾的字符串

REG_DWORD_BIG_ENDIAN    高位排在底位的雙字

REG_EXPAND_SZ        擴展字符串,可以加入變量如%PATH%

REG_LINK        UNICODE 符號鏈接

REG_RESOURCE_LIST    設備驅動程序資源列表

REG_MULTI_SZ        多字符串

註冊表數據項的數據類型有8種但最常用的主要是前3種而以,有了這些基礎下面我們討論如何編程實現對註冊表的操作




打開/關閉註冊表句柄



在對註冊表操作前應該先打開指定的鍵,然後通過鍵的句柄進行操作,打開鍵句柄可以用API RegOpenKeyEx來實現其原形如下

RegOpenKeyEx(
        hKey,        //父鍵句柄
        lpSubKey,    //子鍵句柄
        dwOptions,    //系統保留,指定爲0
        samDesired,    //打開權限
        phkResult,    //返回打開句柄
        )

其中打開權限有多種 想方便的話可以指定爲KEY_ALL_ACCESS 這樣什麼權限都有了,當函數執行成功時返回ERROR_SUCCESS

其實例代碼如下:

    HKEY key;
    LPCTSTR data="SOFTWARE/Microsoft/Windows/CurrentVersion/Run";
    if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,data,0,KEY_ALL_ACCESS,&key)==ERROR_SUCCESS)
    ...{
        /**//*需要執行的操作...*/
    }
    ::RegCloseKey(key);
要注意的是在使用後應該調用RegCloseKey();函數爲關閉句柄.



木馬編程DIY之註冊表管理   文/圖 冷風
獲取子鍵/鍵值信息


在現實的編程操作中我們常常需要獲取 子鍵/鍵值的信息比如:子鍵/鍵值的數量,長度,以及數據的最大長度等等這些信息可以
通過RegQueryInfoKey函數來獲取

它的原型如下:

RegQueryInfoKey(
        hkey,            //要獲取信息的句柄
        lpClass,        //接受創建健時的Class字符串
        lpcbClass,        //lpClass的長度
        lpReserved,        //系統保留,指定爲0
        lpcSubKeys,        //子鍵數量
        lpcbMaxSubKeyLen,    //子鍵中最長名稱的長度
        lpcbMaxClassLen,    //子鍵中最長Class字符串長度

        lpcVlaues,        //鍵值數量
        lpcbMaxValueNameLen,    //鍵值項中最長名稱的長度
        lpcbMaxValueLen,    //鍵值項數據最大長度
        lpcbSecurityDescriptor,    //安全描述符長度
        lpftLastWriteTime,    //FILETIME結構,最後修改時間
        )
哈哈是不是挺嚇人的?其實看實際情況接受自己需要的就好了,不需要的可以放個NULL就OK了,還有一點需要注意就是它所返回
的長度都不包括結尾的0字符,所以在使用時應該用長度+1

其實例代碼如下

DWORD dwIndex=0,NameSize,NameCnt,NameMaxLen,Type;
DWORD KeySize,KeyCnt,KeyMaxLen,DateSize,MaxDateLen;

if(RegQueryInfoKey(key,NULL,NULL,NULL,&KeyCnt,&KeyMaxLen,NULL,&NameCnt,&NameMaxLen,&MaxDateLen,NULL,NULL)!=ERROR_SUCCESS)
...{
    printf("RegQueryInfoKey錯誤");
    return;
}

用的時候套用格式就成了,不然會很麻煩的.....有了這些信息我們就可以枚舉子鍵和鍵值的信息了



枚舉子鍵信息


枚舉子鍵可以用API函數 RegEnumKeyEx來實現 調用RegEnumKeyEx時將返回子鍵的名稱,長度和一些相關數據,如果想得到一個
鍵下的全部子鍵的話應該循環調用,直到返回ERROR_NO_MORE_ITEMS爲至,就說明以枚舉完了所有數據其函數原型如下

RegEnumKeyEx(
        hkey,        //被枚舉的鍵句柄
        dwIndex,    //子鍵索引編號
        lpName,        //子鍵名稱
        lpcbName,    //子鍵名稱長度
        lpReserved,    //系統保留,指定爲0
        lpClass,    //子鍵類名
        lpcbClass,    //子鍵類名長度
        lpftLastWriteTime//最後寫入時間
        )
因爲在之前我們以通過RegQueryInfoKey函數獲取了鍵的有關數據,所以在這裏不再跟據ERROR_NO_MORE_ITEMS來實現了

其實現代碼如下:

for(dwIndex=0;dwIndex<KeyCnt;dwIndex++)        //枚舉子鍵
...{
    KeySize=KeyMaxLen+1;            //因爲RegQueryInfoKey得到的長度不包括0結束字符,所以應加1
    szKeyName=(char*)malloc(KeySize);
    /**//*參數定義請參照獲取子鍵/鍵值信息部分...*/
    RegEnumKeyEx(hKey,dwIndex,szKeyName,&KeySize,NULL,NULL,NULL,NULL);//枚舉子鍵
    printf(szKeyName);
}

最後需要注意的是在每次調用RegEnumKeyEx前必須重新將KeySize的值設爲KeyMaxLen緩衝區的大小,因爲每次函數返回時
KeySize的值會變成返回的鍵值的名稱長度,隨着循環次數這個值會變小,而可能出現無法枚舉所有鍵值項的情況.



枚舉鍵值信息

枚舉鍵值信息的方法與枚舉子鍵信息極爲相似,可以用RegEnumValue函數實現,其函數原型如下:

RegEnumValue(
        hkey,        //被枚舉的鍵句柄
        dwIndex,    //子鍵索引編號
        lpValueName,    //鍵值名稱
        lpcbValueName,    //鍵值名稱長度
        lpReserved,    //系統保留,指定爲0
        lpType,        //鍵值數據類型
        lpDate,        //鍵值數據
        lpcbDate    //鍵值數據長度
        )
其實現代碼如下:

for(dwIndex=0;dwIndex<NameCnt;dwIndex++)    //枚舉鍵值
...{
    DateSize=MaxDateLen+1;
    NameSize=NameMaxLen+1;
    szValueName=(char *)malloc(NameSize);
    szValueDate=(LPBYTE)malloc(DateSize);
    /**//*參數定義請參照獲取子鍵/鍵值信息部分...*/
    RegEnumValue(hKey,dwIndex,szValueName,&NameSize,NULL,&Type,szValueDate,&DateSize);//讀取鍵值

    if(Type==REG_SZ)
    ...{
        /**//*判斷鍵值項類型並做其它操作......*/
    }
    if(Type==REG_DWORD)
    ...{

    }

}

與枚舉子鍵相似,在每次循環中應該重新設置    數據長度DateSize=MaxDateLen+1鍵值名稱長度NameSize=NameMaxLen+1


創建/刪除子鍵

創建子鍵跟打開子鍵差不多,要以用RegCreateKeyEx函數來實現,其原型如下

   
RegCreateKeyEx(
        hkey,            //父鍵句柄
        lpSubKey,        //子鍵句柄
        Reserved,        //系統保留,指定爲0           
        lpClass,        //定義子鍵類名,通常設爲NULL
        dwOptions,        //創建子鍵時的選項
        samDesired,        //創建後操作權限
        lpSecurityAttributes,    //指向SECURITY_ATTRIBUTES結構,指定鍵句柄的繼承性
        phkResult,        //返回創建句柄
        lpdwDisposition        //通常設爲NULL
        )

個人感覺這個API 羅哩羅嗦不好使大多參數在大多數時候都放NULL,還不如16位下的API函數RegCreateKey用着方便

其實例代碼如下:

HKEY KEY;

if (ERROR_SUCCESS!=RegCreateKey(HKEY_LOCAL_MACHINE,"SOFTWARE/Microsoft/Windows/MyKey",&KEY))
...{
    MessageBox("創建失敗!");
}else
...{
    MessageBox("創建成功!");
}

嘿嘿 是不是簡單多了?

不關什麼事破壞總比創建要容易,刪除一個鍵可以用RegDeleteKey()實現,它有兩個參數原型如下

RegDeleteKey(
        hkey,        //主鍵句柄
        lpSubKey,    //子鍵名稱字符串

        )
如果想刪除上面創建的MyKey子鍵可以用下面的代碼實現:

if(ERROR_SUCCESS==RegDeleteKey(HKEY_LOCAL_MACHINE,"SOFTWARE/Microsoft/Windows/MyKey"))
...{
    AfxMessageBox("刪除成功!");
}else
...{
    AfxMessageBox("刪除失敗!");
}
      
需要注意的是 在創建子鍵時可以創建多級子鍵,比如

RegCreateKey(HKEY_LOCAL_MACHINE,"SOFTWARE/Microsoft/Windows/MyKey1/MyKey2",&KEY)

如果MyKey1不存在的話那麼它將先創建MyKey1再創建MyKey2,這一點與文件系統中創建目錄是不同的

但是刪除的時候卻不能刪除多級子鍵比如我想刪除MyKey1那麼我必須先刪除MyKey2纔可以,不過一個子鍵下面的多個鍵值
可以一次刪除.

木馬編程DIY之註冊表管理   文/圖 冷風

創建/刪除鍵值項


創建鍵值可以用RegSetValueEx函數來實現,它的原型如下:

RegSetValueEx(
        hkey,        //鍵句柄,鍵值項將保存在此鍵下
        lpValueName,    //鍵值項名稱
        Reserved,    //系統保留,指定爲0
        dwType,        //鍵值項類型
        lpDate,        //鍵值項數據
        cbDate        //鍵值項長度
        )
使用這個函數的時個有一點需要注意,其中參數lpDate和cbDate的值要跟據dwType的值來設定,按常用設置我們分三種情況

1當dwType爲REG_SZ時,這時跟通常一樣,lpDate爲要設置的數據, cbDate爲數據的長度

2當dwType爲REG_DWORD 時,cbDate應該設爲4,爲什麼?因爲不設爲4就不對

3當dwType爲REG_BINARY 時,cbDate也應該設爲4,沒有爲什麼了吧呵呵

如果調用時,鍵值項名稱以經存在了會怎麼樣呢?答案是:覆蓋 用新的鍵值項數據覆蓋原來的,如果沒有就新建一個

我們來看一下實現功能的實例代碼:

void CreateValue::OnCreate()
...{
    HKEY    key;
    UpdateData(true);
    if(m_type=="REG_SZ")
    ...{
        if(RegOpenKeyEx(MKEY,SubKey,0,KEY_ALL_ACCESS,&key)==ERROR_SUCCESS)   
        ...{
            if(::RegSetValueEx(key,m_name,0,REG_SZ,(const unsigned char *)m_date,MAX_PATH)==ERROR_SUCCESS)
            ...{
                MessageBox("創建成功!");
            }
        }
    }

    if(m_type=="REG_DWORD")
    ...{
        if(RegOpenKeyEx(MKEY,SubKey,0,KEY_ALL_ACCESS,&key)==ERROR_SUCCESS)   
        ...{
            if(::RegSetValueEx(key,m_name,0,REG_DWORD,(const unsigned char *)m_date,4)==ERROR_SUCCESS)//注意數據長度應該設爲4
            ...{
                MessageBox("創建成功!");
            }
        }
    }

    /**//*其它類型的設置......*/
}

刪除鍵值可以用RegDeleteValue來實現它的函數原型如下

RegDeleteValue(
        hkey,        //父鍵句柄
        lpValueName    //要刪除的鍵值項名稱
        )
其實例代碼如下:

HKEY key;
char value[MAX_PATH]="LengFeng"            //鍵值
LPCTSTR data="SOFTWARE/Microsoft/Windows/CurrentVersion/Run";//路徑

RegOpenKeyEx(HKEY_LOCAL_MACHINE,data,0,KEY_WRITE,&key);        //打開

if(ERROR_SUCCESS==::RegDeleteValue(key,value))            //刪除
...{
    MessageBox("刪除成功!");
}


備份/恢復註冊表


備份和恢復註冊表相對來說用的不是太多,我在程序中也沒有加入這項功能,但爲了這這篇文章完整一些,就用一個運行在CONSOLE
下的小程序來討論一下它們的實現

備份註冊表可以用RegSaveKey函來實現 它的原形如下

RegSaveKey(
        hkey,            //要備份的鍵句柄
        lpFile,        //保存信息的文件名稱
        lpSecurityAttributes    //文件安全屬性
        )

hkey爲要備份的鍵句柄,可以是系統預定義的,也可以是用RegOpenKey()打開或是RegCreateKeyEx()創建的
lpFile爲保存信息的文件名稱,注意這個文件必須是不存在的,而且也不能有擴展名(否則RegRestoreKey()函無法讀取)
lpSecurityAttributes,它在NT系統中用來設置新文件的安全屬性,通常設置爲NULL

在使用這個函數時需要有SE_BACKUP_NAME權限,而這個權限是不可以在RegOpenKey()或是RegCreateKeyEx()指定的
要做到這一點我們需要提升自己的權限,其具體實現如下代碼如示:

#include <windows.h>            
#include <stdio.h>
#include <stdlib.h>
void main()
...{
    char strKey[]="Software/Microsoft/Internet Explorer";
    LPTSTR szSaveFileName;
    HKEY key;
    // 申請備份權限
    HANDLE hToken;
    TOKEN_PRIVILEGES tkp;
    if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
        return;
    LookupPrivilegeValue(NULL,SE_BACKUP_NAME,&tkp.Privileges[0].Luid);//申請SE_BACKUP_NAME權限
    tkp.PrivilegeCount=1;
    tkp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;           
    AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0);
    //開始備份工作
    szSaveFileName=LPTSTR("D:/KeyDate");        //注意文件不可存在否則無法成功
    RegOpenKeyEx(
                            HKEY_CURRENT_USER,
                            (LPCTSTR)strKey,
                            0,
                            KEY_ALL_ACCESS,
                            &key);
    RegSaveKey(key,szSaveFileName, NULL);
    RegCloseKey(key);
}

上面用到了提升權限的代碼,以前雜誌上面有很多可以參考一下來看,這樣備份就完成了.而恢復可以用函數RegRestoreKey來實現
它的原形如下
RegRestoreKey(
        hkey,            //要恢復的鍵句柄
        lpFile,        //保存信息的文件名稱
        dwFlage        //標誌是否易失
        )
此函數的前兩個參數可以與RegSaveKey相同,而參數dwFlage爲TRUE的話是暫時恢復註冊表,如果爲FALSE則是永久修改註冊表值.同樣需要
注意的是使用這個函數需要有SE_RESTORE_NAME權限
發佈了55 篇原創文章 · 獲贊 6 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章