熱點推薦:
您现在的位置: 電腦知識網 >> 操作系統 >> windows xp >> 正文

在Win2000/XP上安靜地替換正在使用的系統文件

2022-06-13   來源: windows xp 


在Win2000/XP上安靜地替換正在使用的系統文件總是索而不敷總有些過意不去.另外在安焦上灌了兩年水竟然安焦文檔還找不到一個我的名字. 灌不出篇精華帖子還回復不到別人灌的精華貼. 也算得上是個奇跡了.

  要安靜地替換正在使用的系統文件要解決兩個問題:
  1. 替換正在使用的文件.
  2. 在替換系統文件時不顯示插CD的對話框.

  微軟有兩個工具可以替換正在使用的文件,zap和inuse. 不過都沒有源代碼, 只好逆向分析了. inuse比較大40K, zap很小7K. 就分析zap了.

用ida打開zap. 就有一個核心函數, 原來它的工作原理是把這個文件移了下位置, 因為比較簡單就直接貼上代碼.

-------------------cut zap.c---------
#include <Windows.h>

BOOL ZapDelFile(char *szFileToDel)
{
    char cTempFileName[0x80];
    char cTempPathName[0x100];
    char cFileName[0x100];

    if(szFileToDel[1] == ':'){
        sprintf(cTempPathName, "%c:\", szFileToDel[0]);
    }
    else{
        GetModuleFileName(NULL, cFileName, 0x100);
        sprintf(cTempPathName, "%c:\", cFileName[0]);
    }

    if(GetTempFileName(cTempPathName, "_@", 0, cTempFileName) == 0){
        return FALSE;
    }

    if(MoveFileEx(szFileToDel, cTempFileName, 1) == 0){
        return FALSE;
    }

    if(MoveFileEx(cTempFileName, NULL, 4) == 0){
        return FALSE;
    }

    return TRUE;
}

void usage(char *n) {
    printf("usage: %s fileNeedToDeln", n);
    exit(0);
}

int main(int argc, char* argv[])
{

    printf("Zap programed by bgate. :) *nn");

    if (argc != 2)
        usage(argv[0]);

    if(ZapDelFile(argv[1]) == TRUE){
        printf("OK");
    }
    else{
        printf("error %d", GetLastError());
    }
    return 0;
}

-------------------end cat-----------

現在你已經可以用它去刪除正在使用的系統文件了, 不過刪除之後會彈出讓你插入Windows CD對話框.
注意: 刪系統文件前做好備份, 在重啟前恢復, 另外刪系統文件前還需要把dllcache中相應的備份刪除. 否則系統會自動恢復.

接下來就想辦法去掉這個對話框, 拿出我的法寶--google. 胡亂地搜了一氣. 搜到兩條有用信息.
1.Windows 2000下執行系統文件保護的代碼在sfc.dll中, Xp系統下在sfc_os.dll中.
2.注冊表中把一個叫SfcDisable的鍵設為FFFFFF9D能在下次啟動時讓文件保護功能失效.

下面的分析是在Win2K sp4+上進行的. 其中分析的sfc.dll版本是5.0.2195.6673

    用ida打開sfc.dll在string中找sfcdisable, 沒找到! 讓string顯示Unicode. 這下看到了. 找到對SfcDisable引用的一個地方.代碼如下
.text:769269F9                 call     _SfcQueryRegDwordWithAlternate@16 ; SfcQueryRegDwordWithAlternate(x,x,x,x)
.text:769269FE                 push     ebx
.text:769269FF                 push     offset ??_C@_1BG@HOGG@?$AAS?$AAf?$AAc?$AAD?; "SfcDisable"
.text:76926A04                 push     edi
.text:76926A05                 push     esi
.text:76926A06                 mov     _SFCDebug, eax
.text:76926A0B                 call     _SfcQueryRegDwordWithAlternate@16 ; SfcQueryRegDwordWithAlternate(x,x,x,x)
.text:76926A10                 push     ebx
.text:76926A11                 push     offset ??_C@_1BA@HLJH@?$AAS?$AAf?$AAc?$AAS?$AAc?$AAa?$AAn?$AA?$AA@ ; "SfcScan"
.text:76926A16                 push     edi
.text:76926A17                 push     esi
.text:76926A18                 mov     _SFCDisable, eax
.text:76926A1D                 call     _SfcQueryRegDwordWithAlternate@16 ; SfcQueryRegDwordWithAlternate(x,x,x,x)
.text:76926A22                 push     ebx
.text:76926A23                 push     offset ??_C@_1BC@KFAJ@?$AAS?$AAf?$AAc?$AAQ?$AAu?$AAo?$AAt?$AAa?$AA?$AA@ ; "SfcQuota"
.text:76926A28                 push     edi
.text:76926A29                 push     esi
.text:76926A2A                 mov     _SFCScan, eax

    其中_SfcQueryRegDwordWithAlternate@16是讀注冊表的函數. 很明顯, 它把注冊表中SfcDisable的值讀到了_SFCDisable中. 好, 調出softice. 在_SFCDisable上設斷點. 我們又用剛寫的zap去刪系統文件, softice彈出來了. 斷到了下面這個地方, eip為7692A326, _SFCDisable為2.
.text:7692A319                 push     ecx
.text:7692A31A                 and     [esp+4+var_4], 0
.text:7692A31F                 cmp     _SFCDisable, 3
.text:7692A326                 push     ebx
.text:7692A327                 push     ebp
.text:7692A328                 push     esi
.text:7692A329                 push     edi
.text:7692A32A                 jnz     short loc_7692A333
.text:7692A32C                 xor     eax, eax
.text:7692A32E                 jmp     loc_7692A459
    F5退出, 一會兒對話框彈了出來, 就對這兒引用了一次. 很好, 看看上面這段代碼"cmp _SFCDisable, 3". 此時_SFCDisable為2彈出了對話框, 那麼我就把它改為3又用zap刪系統文件試試. 哈, 運氣很好, 這次沒出現讓插CD的對話框了. 也就是說只要我們把_SFCDisable改為3就能偷偷地替換系統文件了. 不過不同版本這個地址是不一樣的, 用switch來做這個活總是不好. 得寫個有通用性的代碼.

    開始我想它的工作原理大概是Winlogon發現了有對系統文件進行操作. 便調用sfc.dll中的輸出函數進行檢查. 我們就只需得到這個輸出函數入口然後把這個函數"注釋"掉就可以了.跟著上面這段代碼逆流而上, 找到最後由76924544輸出, 又在76924544上加個斷點, 繼續去刪文件. softice跳出來了, 不過不在函數的入口, 反倒在剛才設置的對_SFCDisable的讀取上, 沒運行函數的入口就運行了函數體中的代碼, 看來遇到高人了. 非得逼我出必殺技, 打開2000源代碼 : ). 找了半天沒找到相應代碼又只得退回來看匯編, 最後發現了這個函數NtWaitForMultipleObjects. 呵, 難怪沒中斷在函數的入口上, 原來早運行了函數的入口然後在函數體裡一直沒退出. 注釋函數的方法不行了. 



    這時我想它的工作原理大概是winlogon調用sfc.dll中的輸出函數在系統啟動時創建了一系列事件. 既然winlogon創建了, 那麼它也應該得撤銷. 用depends打開winlogon. 果然從sfc.dll中輸入了兩個函數. 一個是剛才分析的那個, 創建了一系列事件. 看看另一個, 輸出地址是76926869, 不出所料, 關閉了一系列事件. 現在我們只要向winlogon中注入代碼調用"另一個"函數就能取消文件保護功能了. 不過winlogon不能隨便注入代碼. 26A雜志第六期上有篇文章提到了注入方法:"adjust debugger access rightz to our process". 那也是一篇SFCDisable的文章, 他用的方法是在內存中搜索特征碼, 然後修改. 通用性應該沒這麼好.

    下面的注入方法是從crazylord的代碼中拷過來的, 不過方法不是. :), 寫完後就懶得檢查了, 加之水平有限, 寫的不過優雅的地方就將就著看.

-----------------cut antisfc.c-----------

#include <stdlib.h>
#include "Windows.h"
#include "Tlhelp32.h"
#pragma comment( lib, "Advapi32.lib" )

typedef void (_stdcall * CLOSEEVENTS)(void);
typedef unsigned long DWORD;
typedef DWORD ANTISFC_ACCESS;

/*
* ANTISFC structures
*/

typedef struct _ANTISFC_PROCESS {
    DWORD     Pid;                 // process pid
    HANDLE ProcessHandle;       // process handle
    char   ImageName[MAX_PATH]; // image name (not full path)
} ANTISFC_PROCESS, *PANTISFC_PROCESS;

__inline void ErrorMessageBox(char *szAdditionInfo)
{
    printf("error on %s, error code %d. n", szAdditionInfo, GetLastError());
}

void usage(char *n) {
    printf("usage: %s [/d]n", n);
    printf("t/d: disable sfc file protecte fuction.n");
    exit(0);
}

DWORD Init() {
    DWORD   Ret = 0;
    HANDLE hToken;
    LUID sedebugnameValue;
    TOKEN_PRIVILEGES tkp;

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
        ErrorMessageBox("OpenProcessToken");
    } else {

        if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue)) {
            ErrorMessageBox("LookupPrivilegeValue");
        } else {

            tkp.PrivilegeCount = 1;
            tkp.Privileges[0].Luid = sedebugnameValue;
            tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

            if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL)) {
                ErrorMessageBox("AdjustTokenPrivileges");
            } else {
                Ret = 1;
            }
        }
        CloseHandle(hToken);
    }

    return(Ret);
}

DWORD GetPidEx(char *proc_name, char *full_path) {
    DWORD             dwPid=0;
    HANDLE             hSnapshot;
    PROCESSENTRY32     pe;
    BOOL               Ret;
   
  if (isdigit(proc_name[0]))
      dwPid = strtoul(proc_name, NULL, 0);
  else
      dwPid = -1;
     
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == (HANDLE) -1){
        ErrorMessageBox("CreateToolhelp32Snapshot");
        return(0);
    }

    pe.dwSize = sizeof(PROCESSENTRY32);
    Ret = Process32First(hSnapshot, &pe);

    while (Ret) {
          if((strncmp(strlwr(pe.szExeFile), strlwr(proc_name), strlen(proc_name)) == 0)
           
(pe.th32ProcessID == dwPid)) {
                dwPid = pe.th32ProcessID;
                strcpy(full_path, pe.szExeFile);
                break;
        }
        pe.dwSize = sizeof(PROCESSENTRY32);
        Ret = Process32Next(hSnapshot, &pe);
    }

    CloseHandle(hSnapshot);
    if (dwPid == -1)
      dwPid = 0;
    return(dwPid);
}

DWORD InitProcess(PANTISFC_PROCESS Process, char *proc_name, ANTISFC_ACCESS access) {
    DWORD Ret=0;

    Process->Pid = GetPidEx(proc_name, Process->ImageName);
    if (Process->Pid != 0 && Process->ImageName[0] != 0) {
        Process->ProcessHandle = OpenProcess(access, FALSE, Process->Pid);
        if (Process->ProcessHandle == NULL)
            ErrorMessageBox("OpenProcess");
        else
            Ret = 1;
    }

    return(Ret);
}

DWORD InjectThread(PANTISFC_PROCESS Process,
                PVOID function) {
    HANDLE     hThread;
    DWORD     dwThreadPid = 0, dwState;

    hThread = CreateRemoteThread(Process->ProcessHandle,
                                NULL,
                                0,
                                (DWORD (__stdcall *) (void *)) function,
                                NULL,
                                0,
                                &dwThreadPid);
    if (hThread == NULL) {
        ErrorMessageBox("CreateRemoteThread");
        goto cleanup;
    }

    dwState = WaitForSingleObject(hThread, 4000); // attends 4 secondes

    switch (dwState) {
    case WAIT_TIMEOUT:
    case WAIT_FAILED:
        ErrorMessageBox("WaitForSingleObject");
        goto cleanup;

    case WAIT_OBJECT_0:
        break;

    default:
        ErrorMessageBox("WaitForSingleObject");
        goto cleanup;
    }

    CloseHandle(hThread);
    return dwThreadPid;
   
cleanup:
    CloseHandle(hThread);

    return 0;
}

int main(int argc, char* argv[])
{
    ANTISFC_PROCESS     Process;
    HMODULE hSfc;
    DWORD     dwThread;
    CLOSEEVENTS pfnCloseEvents;
    DWORD dwVersion;

    printf("AntiSfc programed by bgate. :) *nn");

    if (argc != 2)
        usage(argv[0]);

    if (strcmp(argv[1], "/d") != 0) {
        usage(argv[0]);
    }

    if (Init()) {
        printf("debug privilege setn");
    } else {
        printf("error on get debug privilegen");
        return(0);
    }

    if(InitProcess(&Process, "winlogon.exe", PROCESS_ALL_ACCESS) == 0) {
        printf("error on get process info. n");
        return(0);
    }

    dwVersion = GetVersion();
    if ((DWORD)(LOBYTE(LOWORD(dwVersion))) == 5){                 // Windows 2000/XP
        if((DWORD)(HIBYTE(LOWORD(dwVersion))) == 0){             //Windows 2000
            hSfc = LoadLibrary("sfc.dll");
            printf("Win2000n");
        }
        else {//if((DWORD)(HIBYTE(LOWORD(dwVersion))) = 1)             //Windows XP
            hSfc = LoadLibrary("sfc_os.dll");
            printf("Windows XPn");
        }
    }    
    //else if ()   //2003?
    else {
        printf("unsupported versionn");
    }

    pfnCloseEvents = (CLOSEEVENTS)GetProcAddress(hSfc,
                                                MAKEINTRESOURCE(2));
    if(pfnCloseEvents == NULL){
        printf("Load the sfc fuction failedn");
        FreeLibrary(hSfc);
        return(0);
    }

    FreeLibrary(hSfc);

    dwThread = InjectThread(&Process,
                            pfnCloseEvents);
   
    if(dwThread == 0){
        printf("failedn");
    }
    else{
        printf("OKn");
    }

    CloseHandle(Process.ProcessHandle);
    return(0);

}


------------------end cut---------
    在運行zap替換系統文件前運行一下antisfc就行了, 你也可以把它們寫到一起. 理論上他能在2000, xp, 2003?的任何版本上使用. 不過我只在Win2K sp4+, WinXP sp1+上測試過.
    本文的缺點是替換的系統文件只能在重啟後生效, 寫完了.


參考文獻:
    http://www.chapeaux-noirs.org/win/


From:http://tw.wingwit.com/Article/os/windowsxp/201311/4548.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.