Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
af062f47d7 | ||
![]() |
7f0784424f | ||
![]() |
b02ad7a9d6 | ||
![]() |
9d90abc0de | ||
![]() |
2f4355e616 | ||
![]() |
f74f30e3a9 | ||
![]() |
712ef4e38b | ||
![]() |
a28e098cee | ||
![]() |
66c10c4067 | ||
![]() |
1a69d35642 | ||
![]() |
0094d19358 | ||
![]() |
3a3e195c6b | ||
![]() |
2ff1e01cc8 | ||
![]() |
dfc7f82036 | ||
![]() |
490bc062b4 | ||
![]() |
3ce7f39269 | ||
![]() |
500eddf349 | ||
![]() |
49d32db491 | ||
![]() |
fd984db033 | ||
![]() |
d1df067812 | ||
![]() |
6e67f6de80 | ||
![]() |
45a2b915f9 |
34
README.md
34
README.md
@@ -1,4 +1,4 @@
|
||||
# wufuc [](../../tree/old-kb4012218-19) [](https://github.com/zeffy/kb4012218-19/releases/latest)
|
||||
# wufuc [](../../tree/old-kb4012218-19) [ ](https://github.com/zeffy/wufuc/releases/latest)
|
||||
|
||||
Disables the "Unsupported Hardware" message in Windows Update, and allows you to continue installing updates on Windows 7 and 8.1 systems with Intel Kaby Lake, AMD Ryzen, or other unsupported processors.
|
||||
|
||||
@@ -24,25 +24,39 @@ My patch takes advantage of this result caching behavior by setting the "hasn't
|
||||
|
||||
## How it works
|
||||
|
||||
- On system boot the `wufuc` scheduled task runs under the `NT AUTHORITY\SYSTEM` user.
|
||||
- `wufuc` determines what service host process the Windows Update service (`wuauserv`) runs in, and injects itself into it.
|
||||
- At system boot the wufuc scheduled task runs as the `NT AUTHORITY\SYSTEM` user.
|
||||
- `wufuc` determines what service host group process the Windows Update service runs in (typically `netsvcs`), and injects itself into it.
|
||||
- Once injected, it applies a hook to `LoadLibraryEx` that automatically patches `wuaueng.dll` when it is loaded.
|
||||
- Any previously loaded `wuaueng.dll` is also patched.
|
||||
|
||||
### Several improvements over my script-based approach:
|
||||
- **No system files are modified!***
|
||||
- Heuristic byte signature patching persists over new updates.
|
||||
### Several improvements over my xdelta3/batch script method:
|
||||
|
||||
- **No system files are modified!**
|
||||
- Heuristic-based patching, which means it will usually keep working even after updates.
|
||||
- C is best language.
|
||||
- No external dependencies except for Microsoft Visual C++ 2015 Redistributable.
|
||||
- No external dependencies except for Microsoft Visual C++ 2017 Redistributable.
|
||||
|
||||
### How to install/uninstall?
|
||||
|
||||
Just run move the `wufuc` folder to wherever you want and run `install_wufuc.bat` as administrator.
|
||||
Just download the [latest release](https://github.com/zeffy/wufuc/releases/latest), and extract the `wufuc` folder to a permanent location (like `C:\Program Files\wufuc`) and then run `install_wufuc.bat` as administrator.
|
||||
|
||||
To uninstall run `uninstall_wufuc.bat` as administrator.
|
||||
|
||||
To temporarily disable the patch, just go to the Task Scheduler and disable the `wufuc.{ ... }` task, then restart your computer.
|
||||
To temporarily disable the patch, just go to the Task Scheduler and disable the `wufuc.{72EEE38B-9997-42BD-85D3-2DD96DA17307}` task, then restart your computer.
|
||||
|
||||
### How do I remove your old patch and use this instead?
|
||||
|
||||
I've included a utility script called `repair_wuaueng.dll.bat` that will initiate an sfc scan and attempt to automatically revert any changes made to `wuaueng.dll`.
|
||||
I've included a utility script called `repair_wuaueng.dll.bat` that will initiate an sfc scan and revert any changes made to `wuaueng.dll`.
|
||||
|
||||
### How to see wufuc's debugging message output?
|
||||
|
||||
You will need to download [DebugView](https://technet.microsoft.com/en-us/sysinternals/debugview.aspx) to do this.
|
||||
|
||||
The best way to get a log of the entire life-cycle of wufuc is to do the following:
|
||||
|
||||
1. Disable wufuc in Task Scheduler.
|
||||
2. Restart your computer.
|
||||
3. Start `DebugView.exe` as administrator and check `Capture -> Capture Global Win32`.
|
||||
4. Enable wufuc in Task Scheduler.
|
||||
5. Run wufuc in Task Scheduler.
|
||||
6. Output will be shown in DebugView.
|
||||
|
@@ -1,5 +1,5 @@
|
||||
@echo off
|
||||
title wufuc installer - v0.6
|
||||
title wufuc installer - v0.6.0.1
|
||||
:: Copyright (C) 2017 zeffy
|
||||
|
||||
:: This program is free software: you can redistribute it and/or modify
|
||||
@@ -15,7 +15,7 @@ title wufuc installer - v0.6
|
||||
:: You should have received a copy of the GNU General Public License
|
||||
:: along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
echo Copyright (C) 2017 zeffy
|
||||
echo Copyright ^(C^) 2017 zeffy
|
||||
echo This program comes with ABSOLUTELY NO WARRANTY.
|
||||
echo This is free software, and you are welcome to redistribute it
|
||||
echo under certain conditions; see COPYING.txt for details.
|
||||
@@ -76,7 +76,7 @@ goto :die
|
||||
for %%a in (%SUPPORTED_HOTFIXES%) do (
|
||||
wmic /output:stdout qfe get hotfixid | find "%%a" >nul && (
|
||||
set "INSTALLED_HOTFIX=%%a"
|
||||
echo Detected installed supported update: %%a
|
||||
echo Detected supported installed update: %%a
|
||||
goto :confirmation
|
||||
)
|
||||
)
|
||||
@@ -84,8 +84,15 @@ for %%a in (%SUPPORTED_HOTFIXES%) do (
|
||||
echo.
|
||||
echo WARNING - Detected that no supported updates are installed!
|
||||
echo.
|
||||
echo This can be a false warning, if you are certain that need wufuc then you
|
||||
echo can continue (there will be no side effects even if you don't need it)
|
||||
echo This can be a false warning, sometimes it is caused by the WMI
|
||||
echo Win32_QuickFixEngineering class being broken. If you are certain
|
||||
echo that you need wufuc, then you can continue ^(there should be no
|
||||
echo side effects even if you don't need it^).
|
||||
echo.
|
||||
echo This warning could also mean that a new update came out and the
|
||||
echo installer script's list of updates hasn't been updated yet. If
|
||||
echo this is the case and you know which update it is, feel free to
|
||||
echo create an issue. https://github.com/zeffy/wufuc/issues
|
||||
|
||||
set /p CONTINUE=Enter 'Y' if you still want to continue:
|
||||
if /I not "%CONTINUE%"=="Y" goto :cancel
|
||||
@@ -123,7 +130,9 @@ rundll32 "%wufuc_dll%",Rundll32Unload
|
||||
schtasks /Run /TN "%wufuc_task%"
|
||||
|
||||
echo.
|
||||
echo Installed and started wufuc!
|
||||
echo Installed and started wufuc, you can now continue installing updates! :^)
|
||||
echo.
|
||||
echo To uninstall, run uninstall_wufuc.bat as administrator.
|
||||
goto :die
|
||||
|
||||
:die
|
||||
@@ -132,7 +141,6 @@ echo Press any key to exit...
|
||||
pause >nul
|
||||
exit
|
||||
|
||||
|
||||
:cancel
|
||||
echo.
|
||||
echo Canceled by user, press any key to exit...
|
||||
|
@@ -1,5 +1,5 @@
|
||||
@echo off
|
||||
title install wufuc (repair wuaueng.dll) - v0.6
|
||||
title install wufuc ^(repair wuaueng.dll^) - v0.6.0.1
|
||||
:: Copyright (C) 2017 zeffy
|
||||
|
||||
:: This program is free software: you can redistribute it and/or modify
|
||||
@@ -15,7 +15,7 @@ title install wufuc (repair wuaueng.dll) - v0.6
|
||||
:: You should have received a copy of the GNU General Public License
|
||||
:: along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
echo Copyright (C) 2017 zeffy
|
||||
echo Copyright ^(C^) 2017 zeffy
|
||||
echo This program comes with ABSOLUTELY NO WARRANTY.
|
||||
echo This is free software, and you are welcome to redistribute it
|
||||
echo under certain conditions; see COPYING.txt for details.
|
||||
|
@@ -1,5 +1,5 @@
|
||||
@echo off
|
||||
title wufuc uninstaller - v0.6
|
||||
title wufuc uninstaller - v0.6.0.1
|
||||
:: Copyright (C) 2017 zeffy
|
||||
|
||||
:: This program is free software: you can redistribute it and/or modify
|
||||
@@ -15,7 +15,7 @@ title wufuc uninstaller - v0.6
|
||||
:: You should have received a copy of the GNU General Public License
|
||||
:: along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
echo Copyright (C) 2017 zeffy
|
||||
echo Copyright ^(C^) 2017 zeffy
|
||||
echo This program comes with ABSOLUTELY NO WARRANTY.
|
||||
echo This is free software, and you are welcome to redistribute it
|
||||
echo under certain conditions; see COPYING.txt for details.
|
||||
@@ -56,7 +56,7 @@ rundll32 "%wufuc_dll%",Rundll32Unload
|
||||
schtasks /Delete /TN "%wufuc_task%" /F
|
||||
|
||||
echo.
|
||||
echo Unloaded and uninstalled wufuc!
|
||||
echo Unloaded and uninstalled wufuc. :^(
|
||||
|
||||
:die
|
||||
echo.
|
||||
|
Binary file not shown.
@@ -10,7 +10,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
.gitignore = .gitignore
|
||||
LICENSE = LICENSE
|
||||
README.md = README.md
|
||||
wufuc.xml = wufuc.xml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
|
39
wufuc/core.c
39
wufuc/core.c
@@ -13,7 +13,7 @@ DWORD WINAPI NewThreadProc(LPVOID lpParam) {
|
||||
SC_HANDLE hSCManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
|
||||
|
||||
TCHAR lpBinaryPathName[0x8000];
|
||||
QueryServiceBinaryPathName(hSCManager, _T("wuauserv"), lpBinaryPathName, _countof(lpBinaryPathName));
|
||||
get_svcpath(hSCManager, _T("wuauserv"), lpBinaryPathName, _countof(lpBinaryPathName));
|
||||
|
||||
BOOL result = _tcsicmp(GetCommandLine(), lpBinaryPathName);
|
||||
CloseServiceHandle(hSCManager);
|
||||
@@ -27,22 +27,23 @@ DWORD WINAPI NewThreadProc(LPVOID lpParam) {
|
||||
ConvertStringSecurityDescriptorToSecurityDescriptor(_T("D:PAI(A;;FA;;;BA)"), SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL);
|
||||
sa.bInheritHandle = FALSE;
|
||||
|
||||
HANDLE hEvent = CreateEvent(&sa, FALSE, FALSE, _T("Global\\wufuc_UnloadEvent"));
|
||||
HANDLE hEvent = CreateEvent(&sa, TRUE, FALSE, _T("Global\\wufuc_UnloadEvent"));
|
||||
|
||||
if (!hEvent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD dwProcessId = GetCurrentProcessId();
|
||||
DWORD dwThreadId = GetCurrentThreadId();
|
||||
HANDLE lphThreads[0x1000];
|
||||
SIZE_T cb;
|
||||
SuspendProcess(lphThreads, _countof(lphThreads), &cb);
|
||||
|
||||
SuspendProcessThreads(dwProcessId, dwThreadId, lphThreads, _countof(lphThreads), &cb);
|
||||
|
||||
HMODULE hm = GetModuleHandle(NULL);
|
||||
DETOUR_IAT(hm, LoadLibraryExA);
|
||||
DETOUR_IAT(hm, LoadLibraryExW);
|
||||
|
||||
DbgPrint("Applied LoadLibraryEx hooks.");
|
||||
|
||||
HMODULE hwu = GetModuleHandle(_T("wuaueng.dll"));
|
||||
if (hwu) {
|
||||
PatchWUModule(hwu);
|
||||
@@ -50,16 +51,16 @@ DWORD WINAPI NewThreadProc(LPVOID lpParam) {
|
||||
ResumeAndCloseThreads(lphThreads, cb);
|
||||
|
||||
WaitForSingleObject(hEvent, INFINITE);
|
||||
CloseHandle(hEvent);
|
||||
|
||||
DbgPrint("Received wufuc_UnloadEvent, removing hooks.");
|
||||
_tdbgprintf(_T("Received wufuc_UnloadEvent, removing hooks."));
|
||||
|
||||
SuspendProcess(lphThreads, _countof(lphThreads), &cb);
|
||||
SuspendProcessThreads(dwProcessId, dwThreadId, lphThreads, _countof(lphThreads), &cb);
|
||||
RESTORE_IAT(hm, LoadLibraryExA);
|
||||
RESTORE_IAT(hm, LoadLibraryExW);
|
||||
ResumeAndCloseThreads(lphThreads, cb);
|
||||
|
||||
DbgPrint("Unloading library. Cya!");
|
||||
_tdbgprintf(_T("Unloading library. Cya!"));
|
||||
CloseHandle(hEvent);
|
||||
FreeLibraryAndExitThread(HINST_THISCOMPONENT, 0);
|
||||
return 0;
|
||||
}
|
||||
@@ -97,8 +98,7 @@ BOOL PatchWUModule(HMODULE hModule) {
|
||||
"A1????????"; // mov eax,dword ptr ds:[????????]
|
||||
n1 = 5;
|
||||
n2 = 13;
|
||||
}
|
||||
else if (IsWindows7()) {
|
||||
} else if (IsWindows7()) {
|
||||
lpszPattern =
|
||||
"833D????????00" // cmp dword ptr ds:[????????],0
|
||||
"743E" // je $+3E
|
||||
@@ -118,20 +118,19 @@ BOOL PatchWUModule(HMODULE hModule) {
|
||||
if (!FindPattern(modinfo.lpBaseOfDll, modinfo.SizeOfImage, lpszPattern, 0, &offset)) {
|
||||
return FALSE;
|
||||
}
|
||||
DbgPrint("IsDeviceServiceable(void) matched at %p", (UINT_PTR)modinfo.lpBaseOfDll + offset);
|
||||
SIZE_T rva = (SIZE_T)modinfo.lpBaseOfDll + offset;
|
||||
_tdbgprintf(_T("IsDeviceServiceable(void) matched at %p"), rva);
|
||||
|
||||
DWORD *lpdwResultIsNotCachedOffset = (DWORD *)((UINT_PTR)modinfo.lpBaseOfDll + offset + n1);
|
||||
BOOL *lpbResultIsNotCached = (BOOL *)((UINT_PTR)modinfo.lpBaseOfDll + offset + n1 + sizeof(DWORD) + *lpdwResultIsNotCachedOffset);
|
||||
if (*lpbResultIsNotCached) {
|
||||
*lpbResultIsNotCached = FALSE;
|
||||
DbgPrint("Patched %p=%d", lpbResultIsNotCached, *lpbResultIsNotCached);
|
||||
BOOL *lpbNotRunOnce = (BOOL *)(rva + n1 + sizeof(DWORD) + *(DWORD *)(rva + n1));
|
||||
if (*lpbNotRunOnce) {
|
||||
*lpbNotRunOnce = FALSE;
|
||||
_tdbgprintf(_T("Patched %p=%d"), lpbNotRunOnce, *lpbNotRunOnce);
|
||||
}
|
||||
|
||||
DWORD *lpdwCachedResultOffset = (DWORD *)((UINT_PTR)modinfo.lpBaseOfDll + offset + n2);
|
||||
BOOL *lpbCachedResult = (BOOL *)((UINT_PTR)modinfo.lpBaseOfDll + offset + n2 + sizeof(DWORD) + *lpdwCachedResultOffset);
|
||||
BOOL *lpbCachedResult = (BOOL *)(rva + n2 + sizeof(DWORD) + *(DWORD *)(rva + n2));
|
||||
if (!*lpbCachedResult) {
|
||||
*lpbCachedResult = TRUE;
|
||||
DbgPrint("Patched %p=%d", lpbCachedResult, *lpbCachedResult);
|
||||
_tdbgprintf(_T("Patched %p=%d"), lpbCachedResult, *lpbCachedResult);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
@@ -14,9 +14,9 @@ void CALLBACK Rundll32Entry(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int n
|
||||
SC_HANDLE hSCManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
|
||||
TCHAR lpGroupName[256];
|
||||
DWORD dwProcessId;
|
||||
BOOL result = QueryServiceProcessId(hSCManager, _T("wuauserv"), &dwProcessId);
|
||||
if (!result && GetServiceGroupName(hSCManager, _T("wuauserv"), lpGroupName, _countof(lpGroupName))) {
|
||||
result = FindServiceGroupProcessId(hSCManager, lpGroupName, &dwProcessId);
|
||||
BOOL result = get_svcpid(hSCManager, _T("wuauserv"), &dwProcessId);
|
||||
if (!result && get_svcgname(hSCManager, _T("wuauserv"), lpGroupName, _countof(lpGroupName))) {
|
||||
result = get_svcgpid(hSCManager, lpGroupName, &dwProcessId);
|
||||
}
|
||||
CloseServiceHandle(hSCManager);
|
||||
if (!result) {
|
||||
@@ -34,7 +34,7 @@ void CALLBACK Rundll32Entry(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int n
|
||||
void CALLBACK Rundll32Unload(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) {
|
||||
HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, _T("Global\\wufuc_UnloadEvent"));
|
||||
if (hEvent) {
|
||||
DbgPrint("Setting wufuc_UnloadEvent...");
|
||||
_tdbgprintf(_T("Setting wufuc_UnloadEvent..."));
|
||||
SetEvent(hEvent);
|
||||
CloseHandle(hEvent);
|
||||
}
|
||||
|
139
wufuc/process.c
139
wufuc/process.c
@@ -1,139 +0,0 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <tchar.h>
|
||||
#include "util.h"
|
||||
#include "process.h"
|
||||
|
||||
VOID DetourIAT(HMODULE hModule, LPSTR lpFuncName, LPVOID *lpOldAddress, LPVOID lpNewAddress) {
|
||||
LPVOID *lpAddress = FindIAT(hModule, lpFuncName);
|
||||
if (!lpAddress || *lpAddress == lpNewAddress) {
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD flOldProtect;
|
||||
DWORD flNewProtect = PAGE_READWRITE;
|
||||
VirtualProtect(lpAddress, sizeof(LPVOID), flNewProtect, &flOldProtect);
|
||||
if (lpOldAddress) {
|
||||
*lpOldAddress = *lpAddress;
|
||||
}
|
||||
DbgPrint("%S %p => %p", lpFuncName, *lpAddress, lpNewAddress);
|
||||
*lpAddress = lpNewAddress;
|
||||
VirtualProtect(lpAddress, sizeof(LPVOID), flOldProtect, &flNewProtect);
|
||||
}
|
||||
|
||||
LPVOID *FindIAT(HMODULE hModule, LPSTR lpFuncName) {
|
||||
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)hModule;
|
||||
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((LPBYTE)dos + dos->e_lfanew);
|
||||
PIMAGE_IMPORT_DESCRIPTOR desc = (PIMAGE_IMPORT_DESCRIPTOR)((LPBYTE)dos + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
|
||||
|
||||
for (PIMAGE_IMPORT_DESCRIPTOR iid = desc; iid->Name != 0; iid++) {
|
||||
for (int i = 0; *(i + (LPVOID*)(iid->FirstThunk + (SIZE_T)hModule)) != NULL; i++) {
|
||||
LPSTR name = (LPSTR)(*(i + (SIZE_T*)(iid->OriginalFirstThunk + (SIZE_T)hModule)) + (SIZE_T)hModule + 2);
|
||||
const uintptr_t n = (uintptr_t)name;
|
||||
if (!(n & (sizeof(n) == 4 ? 0x80000000 : 0x8000000000000000)) && !_stricmp(lpFuncName, name)) {
|
||||
return i + (LPVOID*)(iid->FirstThunk + (SIZE_T)hModule);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BOOL FindPattern(LPCBYTE lpBytes, SIZE_T nNumberOfBytes, LPSTR lpszPattern, SIZE_T nStart, SIZE_T *lpOffset) {
|
||||
SIZE_T nPatternLength = strlen(lpszPattern);
|
||||
SIZE_T nMaskLength = nPatternLength / 2;
|
||||
if (nMaskLength > nNumberOfBytes || nPatternLength % 2) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LPBYTE lpPattern = malloc(nMaskLength * sizeof(BYTE));
|
||||
BOOL *lpbMask = malloc(nMaskLength * sizeof(BOOL));
|
||||
|
||||
LPSTR p = lpszPattern;
|
||||
BOOL valid = TRUE;
|
||||
for (SIZE_T i = 0; i < nMaskLength; i++) {
|
||||
if (lpbMask[i] = strncmp(p, "??", 2)) {
|
||||
if (sscanf_s(p, "%2hhx", &lpPattern[i]) != 1) {
|
||||
valid = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
p += 2;
|
||||
}
|
||||
BOOL result = FALSE;
|
||||
if (valid) {
|
||||
for (SIZE_T i = nStart; i < nNumberOfBytes - nStart - (nMaskLength - 1); i++) {
|
||||
BOOL found = TRUE;
|
||||
for (SIZE_T j = 0; j < nMaskLength; j++) {
|
||||
if (lpbMask[j] && lpBytes[i + j] != lpPattern[j]) {
|
||||
found = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
*lpOffset = i;
|
||||
result = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(lpPattern);
|
||||
free(lpbMask);
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL InjectLibrary(HANDLE hProcess, LPCTSTR lpLibFileName, DWORD cb) {
|
||||
LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, cb, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
if (!WriteProcessMemory(hProcess, lpBaseAddress, lpLibFileName, cb, NULL)) {
|
||||
return FALSE;
|
||||
}
|
||||
DWORD dwProcessId = GetProcessId(hProcess);
|
||||
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
|
||||
MODULEENTRY32 me;
|
||||
me.dwSize = sizeof(me);
|
||||
|
||||
Module32First(hSnap, &me);
|
||||
do {
|
||||
if (!_tcsicmp(me.szModule, _T("kernel32.dll"))) {
|
||||
break;
|
||||
}
|
||||
} while (Module32Next(hSnap, &me));
|
||||
CloseHandle(hSnap);
|
||||
DbgPrint("Injecting %s into process %d", lpLibFileName, dwProcessId);
|
||||
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(me.hModule, _CRT_STRINGIZE(LoadLibrary)), lpBaseAddress, 0, NULL);
|
||||
CloseHandle(hThread);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
VOID SuspendProcess(HANDLE *lphThreads, SIZE_T dwSize, SIZE_T *lpcb) {
|
||||
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
||||
THREADENTRY32 te;
|
||||
te.dwSize = sizeof(te);
|
||||
Thread32First(hSnap, &te);
|
||||
|
||||
DWORD dwProcessId = GetCurrentProcessId();
|
||||
DWORD dwThreadId = GetCurrentThreadId();
|
||||
|
||||
SIZE_T count = 0;
|
||||
|
||||
do {
|
||||
if (te.th32OwnerProcessID != dwProcessId || te.th32ThreadID == dwThreadId) {
|
||||
continue;
|
||||
}
|
||||
lphThreads[count] = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID);
|
||||
SuspendThread(lphThreads[count]);
|
||||
count++;
|
||||
} while (count < dwSize && Thread32Next(hSnap, &te));
|
||||
CloseHandle(hSnap);
|
||||
|
||||
*lpcb = count;
|
||||
DbgPrint("Suspended other threads.");
|
||||
}
|
||||
|
||||
VOID ResumeAndCloseThreads(HANDLE *lphThreads, SIZE_T cb) {
|
||||
for (SIZE_T i = 0; i < cb; i++) {
|
||||
ResumeThread(lphThreads[i]);
|
||||
CloseHandle(lphThreads[i]);
|
||||
}
|
||||
DbgPrint("Resumed threads.");
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
VOID DetourIAT(HMODULE hModule, LPSTR lpFuncName, LPVOID *lpOldAddress, LPVOID lpNewAddress);
|
||||
|
||||
#define DETOUR_IAT(x, y) \
|
||||
LPVOID __LPORIGINAL##y; \
|
||||
DetourIAT(x, #y, &__LPORIGINAL##y, &_##y)
|
||||
|
||||
#define RESTORE_IAT(x, y) \
|
||||
DetourIAT(x, #y, NULL, __LPORIGINAL##y)
|
||||
|
||||
LPVOID *FindIAT(HMODULE hModule, LPSTR lpFuncName);
|
||||
|
||||
BOOL FindPattern(LPCBYTE lpBytes, SIZE_T nNumberOfBytes, LPSTR lpszPattern, SIZE_T nStart, SIZE_T *lpOffset);
|
||||
|
||||
BOOL InjectLibrary(HANDLE hProcess, LPCTSTR lpLibFileName, DWORD cb);
|
||||
|
||||
VOID SuspendProcess(HANDLE *lphThreads, SIZE_T dwSize, SIZE_T *lpcb);
|
||||
|
||||
VOID ResumeAndCloseThreads(HANDLE *lphThreads, SIZE_T dwSize);
|
@@ -1,15 +0,0 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by version.rc
|
||||
//
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1000
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
@@ -2,8 +2,9 @@
|
||||
#include <tchar.h>
|
||||
#include "util.h"
|
||||
#include "service.h"
|
||||
#include "shellapihelper.h"
|
||||
|
||||
BOOL QueryServiceBinaryPathName(SC_HANDLE hSCManager, LPCTSTR lpServiceName, LPTSTR lpBinaryPathName, SIZE_T dwSize) {
|
||||
BOOL get_svcpath(SC_HANDLE hSCManager, LPCTSTR lpServiceName, LPTSTR lpBinaryPathName, SIZE_T dwSize) {
|
||||
HANDLE hService = OpenService(hSCManager, lpServiceName, SERVICE_QUERY_CONFIG);
|
||||
if (!hService) {
|
||||
return FALSE;
|
||||
@@ -13,16 +14,15 @@ BOOL QueryServiceBinaryPathName(SC_HANDLE hSCManager, LPCTSTR lpServiceName, LPT
|
||||
QueryServiceConfig(hService, NULL, 0, &cbBytesNeeded);
|
||||
LPQUERY_SERVICE_CONFIG sc = malloc(cbBytesNeeded);
|
||||
BOOL result = QueryServiceConfig(hService, sc, cbBytesNeeded, &cbBytesNeeded);
|
||||
int a = GetLastError();
|
||||
CloseServiceHandle(hService);
|
||||
if (result) {
|
||||
_tcscpy_s(lpBinaryPathName, dwSize, sc->lpBinaryPathName);
|
||||
}
|
||||
LocalFree(sc);
|
||||
free(sc);
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL QueryServiceProcessId(SC_HANDLE hSCManager, LPCTSTR lpServiceName, DWORD *lpdwProcessId) {
|
||||
BOOL get_svcpid(SC_HANDLE hSCManager, LPCTSTR lpServiceName, DWORD *lpdwProcessId) {
|
||||
SC_HANDLE hService = OpenService(hSCManager, lpServiceName, SERVICE_QUERY_STATUS);
|
||||
if (!hService) {
|
||||
return FALSE;
|
||||
@@ -34,15 +34,14 @@ BOOL QueryServiceProcessId(SC_HANDLE hSCManager, LPCTSTR lpServiceName, DWORD *l
|
||||
if (QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&lpBuffer, sizeof(lpBuffer), &cbBytesNeeded) && lpBuffer.dwProcessId) {
|
||||
*lpdwProcessId = lpBuffer.dwProcessId;
|
||||
result = TRUE;
|
||||
DbgPrint("Found %s pid %d", lpServiceName, *lpdwProcessId);
|
||||
}
|
||||
CloseServiceHandle(hService);
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL GetServiceGroupName(SC_HANDLE hSCManager, LPCTSTR lpServiceName, LPTSTR lpGroupName, SIZE_T dwSize) {
|
||||
BOOL get_svcgname(SC_HANDLE hSCManager, LPCTSTR lpServiceName, LPTSTR lpGroupName, SIZE_T dwSize) {
|
||||
TCHAR lpBinaryPathName[0x8000];
|
||||
if (!QueryServiceBinaryPathName(hSCManager, lpServiceName, lpBinaryPathName, _countof(lpBinaryPathName))) {
|
||||
if (!get_svcpath(hSCManager, lpServiceName, lpBinaryPathName, _countof(lpBinaryPathName))) {
|
||||
return FALSE;
|
||||
}
|
||||
int numArgs;
|
||||
@@ -57,11 +56,10 @@ BOOL GetServiceGroupName(SC_HANDLE hSCManager, LPCTSTR lpServiceName, LPTSTR lpG
|
||||
BOOL result = FALSE;
|
||||
if (!_tcsicmp(fname, _T("svchost"))) {
|
||||
LPWSTR *p = argv;
|
||||
for (int i = 0; i + 1 < numArgs; i++) {
|
||||
for (int i = 1; i < numArgs; i++) {
|
||||
if (!_tcsicmp(*(p++), _T("-k"))) {
|
||||
_tcscpy_s(lpGroupName, dwSize, *p);
|
||||
result = TRUE;
|
||||
DbgPrint("Found %s svc group: %s", lpServiceName, lpGroupName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -69,29 +67,25 @@ BOOL GetServiceGroupName(SC_HANDLE hSCManager, LPCTSTR lpServiceName, LPTSTR lpG
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL FindServiceGroupProcessId(SC_HANDLE hSCManager, LPTSTR lpServiceGroupName, DWORD *lpdwProcessId) {
|
||||
BOOL get_svcgpid(SC_HANDLE hSCManager, LPTSTR lpServiceGroupName, DWORD *lpdwProcessId) {
|
||||
DWORD uBytes = 0x100000;
|
||||
LPBYTE pvData = malloc(uBytes);
|
||||
|
||||
RegGetValue(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost"), lpServiceGroupName, RRF_RT_REG_MULTI_SZ, NULL, pvData, &uBytes);
|
||||
|
||||
LPTSTR ptr = (LPTSTR)pvData;
|
||||
|
||||
BOOL result = FALSE;
|
||||
while (*ptr) {
|
||||
for (LPTSTR p = (LPTSTR)pvData; *p; p += _tcslen(p) + 1) {
|
||||
DWORD dwProcessId;
|
||||
if (QueryServiceProcessId(hSCManager, ptr, &dwProcessId)) {
|
||||
TCHAR group[256];
|
||||
GetServiceGroupName(hSCManager, ptr, group, _countof(group));
|
||||
if (get_svcpid(hSCManager, p, &dwProcessId)) {
|
||||
get_svcgname(hSCManager, p, group, _countof(group));
|
||||
result = !_tcsicmp(group, lpServiceGroupName);
|
||||
}
|
||||
if (result) {
|
||||
DbgPrint("Found %s pid %d", lpServiceGroupName, dwProcessId);
|
||||
*lpdwProcessId = dwProcessId;
|
||||
break;
|
||||
}
|
||||
ptr += _tcslen(ptr) + 1;
|
||||
}
|
||||
LocalFree(pvData);
|
||||
free(pvData);
|
||||
return result;
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
BOOL QueryServiceBinaryPathName(SC_HANDLE hSCManager, LPCTSTR lpServiceName, LPTSTR lpBinaryPathName, SIZE_T dwSize);
|
||||
BOOL get_svcpath(SC_HANDLE hSCManager, LPCTSTR lpServiceName, LPTSTR lpBinaryPathName, SIZE_T dwSize);
|
||||
|
||||
BOOL QueryServiceProcessId(SC_HANDLE hSCManager, LPCTSTR lpServiceName, DWORD *lpdwProcessId);
|
||||
BOOL get_svcpid(SC_HANDLE hSCManager, LPCTSTR lpServiceName, DWORD *lpdwProcessId);
|
||||
|
||||
BOOL GetServiceGroupName(SC_HANDLE hSCManager, LPCTSTR lpServiceName, LPTSTR lpGroupName, SIZE_T dwSize);
|
||||
BOOL get_svcgname(SC_HANDLE hSCManager, LPCTSTR lpServiceName, LPTSTR lpGroupName, SIZE_T dwSize);
|
||||
|
||||
BOOL FindServiceGroupProcessId(SC_HANDLE hSCManager, LPTSTR lpServiceGroupName, DWORD *lpdwProcessId);
|
||||
BOOL get_svcgpid(SC_HANDLE hSCManager, LPTSTR lpServiceGroupName, DWORD *lpdwProcessId);
|
||||
|
7
wufuc/shellapihelper.h
Normal file
7
wufuc/shellapihelper.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef UNICODE
|
||||
#define CommandLineToArgv CommandLineToArgvW
|
||||
#else
|
||||
#define CommandLineToArgv CommandLineToArgvA
|
||||
#endif // !UNICODE
|
149
wufuc/util.c
149
wufuc/util.c
@@ -1,5 +1,7 @@
|
||||
#include <Windows.h>
|
||||
#include <stdio.h>
|
||||
#include <VersionHelpers.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <tchar.h>
|
||||
#include "util.h"
|
||||
|
||||
@@ -15,13 +17,148 @@ BOOL IsWindows8Point1(void) {
|
||||
return IsWindows8Point1OrGreater() && !IsWindows10OrGreater();
|
||||
}
|
||||
|
||||
//#ifdef _DEBUG
|
||||
VOID _DbgPrint(LPCTSTR format, ...) {
|
||||
TCHAR buffer[0x1000];
|
||||
VOID DetourIAT(HMODULE hModule, LPSTR lpFuncName, LPVOID *lpOldAddress, LPVOID lpNewAddress) {
|
||||
LPVOID *lpAddress = FindIAT(hModule, lpFuncName);
|
||||
if (!lpAddress || *lpAddress == lpNewAddress) {
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD flOldProtect;
|
||||
DWORD flNewProtect = PAGE_READWRITE;
|
||||
VirtualProtect(lpAddress, sizeof(LPVOID), flNewProtect, &flOldProtect);
|
||||
if (lpOldAddress) {
|
||||
*lpOldAddress = *lpAddress;
|
||||
}
|
||||
_dbgprintf("%s %p => %p", lpFuncName, *lpAddress, lpNewAddress);
|
||||
*lpAddress = lpNewAddress;
|
||||
VirtualProtect(lpAddress, sizeof(LPVOID), flOldProtect, &flNewProtect);
|
||||
}
|
||||
|
||||
LPVOID *FindIAT(HMODULE hModule, LPSTR lpFunctionName) {
|
||||
SIZE_T hm = (SIZE_T)hModule;
|
||||
|
||||
for (PIMAGE_IMPORT_DESCRIPTOR iid = (PIMAGE_IMPORT_DESCRIPTOR)(hm + ((PIMAGE_NT_HEADERS)(hm + ((PIMAGE_DOS_HEADER)hm)->e_lfanew))
|
||||
->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); iid->Name; iid++) {
|
||||
|
||||
LPVOID *p;
|
||||
for (SIZE_T i = 0; *(p = i + (LPVOID *)(hm + iid->FirstThunk)); i++) {
|
||||
LPSTR fn = (LPSTR)(hm + *(i + (SIZE_T *)(hm + iid->OriginalFirstThunk)) + 2);
|
||||
if (!((uintptr_t)fn & IMAGE_ORDINAL_FLAG) && !_stricmp(lpFunctionName, fn)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BOOL FindPattern(LPCBYTE pvData, SIZE_T nNumberOfBytes, LPSTR lpszPattern, SIZE_T nStart, SIZE_T *lpOffset) {
|
||||
SIZE_T length = strlen(lpszPattern);
|
||||
SIZE_T nBytes;
|
||||
if (length % 2 || (nBytes = length / 2) > nNumberOfBytes) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LPBYTE lpBytes = malloc(nBytes * sizeof(BYTE));
|
||||
BOOL *lpbwc = malloc(nBytes * sizeof(BOOL));
|
||||
|
||||
LPSTR p = lpszPattern;
|
||||
BOOL valid = TRUE;
|
||||
for (SIZE_T i = 0; i < nBytes; i++) {
|
||||
if ((lpbwc[i] = strncmp(p, "??", 2)) && sscanf_s(p, "%2hhx", &lpBytes[i]) != 1) {
|
||||
valid = FALSE;
|
||||
break;
|
||||
}
|
||||
p += 2;
|
||||
}
|
||||
BOOL result = FALSE;
|
||||
if (valid) {
|
||||
for (SIZE_T i = nStart; i < nNumberOfBytes - nStart - (nBytes - 1); i++) {
|
||||
BOOL found = TRUE;
|
||||
for (SIZE_T j = 0; j < nBytes; j++) {
|
||||
if (lpbwc[j] && pvData[i + j] != lpBytes[j]) {
|
||||
found = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
*lpOffset = i;
|
||||
result = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(lpBytes);
|
||||
free(lpbwc);
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL InjectLibrary(HANDLE hProcess, LPCTSTR lpLibFileName, DWORD cb) {
|
||||
LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, cb, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
if (!WriteProcessMemory(hProcess, lpBaseAddress, lpLibFileName, cb, NULL)) {
|
||||
return FALSE;
|
||||
}
|
||||
DWORD dwProcessId = GetProcessId(hProcess);
|
||||
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
|
||||
MODULEENTRY32 me;
|
||||
me.dwSize = sizeof(me);
|
||||
|
||||
Module32First(hSnap, &me);
|
||||
do {
|
||||
if (!_tcsicmp(me.szModule, _T("kernel32.dll"))) {
|
||||
break;
|
||||
}
|
||||
} while (Module32Next(hSnap, &me));
|
||||
CloseHandle(hSnap);
|
||||
_tdbgprintf(_T("Injecting %s into process %d"), lpLibFileName, dwProcessId);
|
||||
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(me.hModule, _CRT_STRINGIZE(LoadLibrary)), lpBaseAddress, 0, NULL);
|
||||
CloseHandle(hThread);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
VOID SuspendProcessThreads(DWORD dwProcessId, DWORD dwThreadId, HANDLE *lphThreads, SIZE_T dwSize, SIZE_T *lpcb) {
|
||||
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
||||
THREADENTRY32 te;
|
||||
te.dwSize = sizeof(te);
|
||||
Thread32First(hSnap, &te);
|
||||
|
||||
SIZE_T count = 0;
|
||||
|
||||
do {
|
||||
if (te.th32OwnerProcessID != dwProcessId || te.th32ThreadID == dwThreadId) {
|
||||
continue;
|
||||
}
|
||||
lphThreads[count] = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID);
|
||||
SuspendThread(lphThreads[count]);
|
||||
count++;
|
||||
} while (count < dwSize && Thread32Next(hSnap, &te));
|
||||
CloseHandle(hSnap);
|
||||
|
||||
*lpcb = count;
|
||||
_tdbgprintf(_T("Suspended other threads."));
|
||||
}
|
||||
|
||||
VOID ResumeAndCloseThreads(HANDLE *lphThreads, SIZE_T cb) {
|
||||
for (SIZE_T i = 0; i < cb; i++) {
|
||||
ResumeThread(lphThreads[i]);
|
||||
CloseHandle(lphThreads[i]);
|
||||
}
|
||||
_tdbgprintf(_T("Resumed threads."));
|
||||
}
|
||||
|
||||
VOID _wdbgprintf(LPCWSTR format, ...) {
|
||||
WCHAR buffer[0x1000];
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
_vstprintf_s(buffer, _countof(buffer), format, argptr);
|
||||
vswprintf_s(buffer, _countof(buffer), format, argptr);
|
||||
va_end(argptr);
|
||||
OutputDebugString(buffer);
|
||||
OutputDebugStringW(buffer);
|
||||
}
|
||||
|
||||
VOID _dbgprintf(LPCSTR format, ...) {
|
||||
CHAR buffer[0x1000];
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
vsprintf_s(buffer, _countof(buffer), format, argptr);
|
||||
va_end(argptr);
|
||||
OutputDebugStringA(buffer);
|
||||
}
|
||||
//#endif
|
||||
|
35
wufuc/util.h
35
wufuc/util.h
@@ -9,16 +9,33 @@ BOOL IsWindows7(void);
|
||||
|
||||
BOOL IsWindows8Point1(void);
|
||||
|
||||
//#ifdef _DEBUG
|
||||
VOID _DbgPrint(LPCTSTR format, ...);
|
||||
#define DbgPrint(format, ...) \
|
||||
_DbgPrint(_T(__FUNCTION__) _T(": ") _T(format), ##__VA_ARGS__)
|
||||
//#else
|
||||
//#define DbgPrint(format, ...)
|
||||
//#endif
|
||||
VOID DetourIAT(HMODULE hModule, LPSTR lpFuncName, LPVOID *lpOldAddress, LPVOID lpNewAddress);
|
||||
|
||||
#define DETOUR_IAT(x, y) \
|
||||
LPVOID __LPORIGINAL##y; \
|
||||
DetourIAT(x, #y, &__LPORIGINAL##y, &_##y)
|
||||
|
||||
#define RESTORE_IAT(x, y) \
|
||||
DetourIAT(x, #y, NULL, __LPORIGINAL##y)
|
||||
|
||||
LPVOID *FindIAT(HMODULE hModule, LPSTR lpFuncName);
|
||||
|
||||
BOOL FindPattern(LPCBYTE lpBytes, SIZE_T nNumberOfBytes, LPSTR lpszPattern, SIZE_T nStart, SIZE_T *lpOffset);
|
||||
|
||||
BOOL InjectLibrary(HANDLE hProcess, LPCTSTR lpLibFileName, DWORD cb);
|
||||
|
||||
VOID SuspendProcessThreads(DWORD dwProcessId, DWORD dwThreadId, HANDLE *lphThreads, SIZE_T dwSize, SIZE_T *lpcb);
|
||||
|
||||
VOID ResumeAndCloseThreads(HANDLE *lphThreads, SIZE_T dwSize);
|
||||
|
||||
VOID _wdbgprintf(LPCWSTR format, ...);
|
||||
VOID _dbgprintf(LPCSTR format, ...);
|
||||
//#ifdef _DEBUG
|
||||
#ifdef UNICODE
|
||||
#define CommandLineToArgv CommandLineToArgvW
|
||||
#define _tdbgprintf _wdbgprintf
|
||||
#else
|
||||
#define CommandLineToArgv CommandLineToArgvA
|
||||
#define _tdbgprintf _dbgprintf
|
||||
#endif // !UNICODE
|
||||
//#else
|
||||
//#define _tdbgprintf(format, ...)
|
||||
//#endif // !_DEBUG
|
||||
|
BIN
wufuc/version.rc
BIN
wufuc/version.rc
Binary file not shown.
@@ -144,6 +144,9 @@
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y "$(TargetPath)" "$(SolutionDir)install\$(TargetFileName)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
@@ -163,12 +166,14 @@
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y "$(TargetPath)" "$(SolutionDir)install\$(TargetFileName)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="core.c" />
|
||||
<ClCompile Include="dllmain.c" />
|
||||
<ClCompile Include="entrypoint.c" />
|
||||
<ClCompile Include="process.c" />
|
||||
<ClCompile Include="service.c" />
|
||||
<ClCompile Include="util.c" />
|
||||
</ItemGroup>
|
||||
@@ -177,8 +182,8 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="core.h" />
|
||||
<ClInclude Include="process.h" />
|
||||
<ClInclude Include="service.h" />
|
||||
<ClInclude Include="shellapihelper.h" />
|
||||
<ClInclude Include="util.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
Reference in New Issue
Block a user