////////////////////////////////////////////////////////////////////////////// // // Test DetourCreateProcessWithDll function (withdll.cpp). // // Microsoft Research Detours Package // // Copyright (c) Microsoft Corporation. All rights reserved. // #include #include #include #pragma warning(push) #if _MSC_VER > 1400 #pragma warning(disable:6102 6103) // /analyze warnings #endif #include #pragma warning(pop) ////////////////////////////////////////////////////////////////////////////// // void PrintUsage(void) { printf("Usage:\n" " withdll.exe [options] [command line]\n" "Options:\n" " /d:file.dll : Start the process with file.dll.\n" " /v : Verbose, display memory at start.\n" " /? : This help screen.\n"); } ////////////////////////////////////////////////////////////////////////////// // // This code verifies that the named DLL has been configured correctly // to be imported into the target process. DLLs must export a function with // ordinal #1 so that the import table touch-up magic works. // struct ExportContext { BOOL fHasOrdinal1; ULONG nExports; }; static BOOL CALLBACK ExportCallback(_In_opt_ PVOID pContext, _In_ ULONG nOrdinal, _In_opt_ LPCSTR pszSymbol, _In_opt_ PVOID pbTarget) { (void)pContext; (void)pbTarget; (void)pszSymbol; ExportContext *pec = (ExportContext *)pContext; if (nOrdinal == 1) { pec->fHasOrdinal1 = TRUE; } pec->nExports++; return TRUE; } ////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////// // void TypeToString(DWORD Type, char *pszBuffer, size_t cBuffer) { if (Type == MEM_IMAGE) { StringCchPrintfA(pszBuffer, cBuffer, "img"); } else if (Type == MEM_MAPPED) { StringCchPrintfA(pszBuffer, cBuffer, "map"); } else if (Type == MEM_PRIVATE) { StringCchPrintfA(pszBuffer, cBuffer, "pri"); } else { StringCchPrintfA(pszBuffer, cBuffer, "%x", Type); } } void StateToString(DWORD State, char *pszBuffer, size_t cBuffer) { if (State == MEM_COMMIT) { StringCchPrintfA(pszBuffer, cBuffer, "com"); } else if (State == MEM_FREE) { StringCchPrintfA(pszBuffer, cBuffer, "fre"); } else if (State == MEM_RESERVE) { StringCchPrintfA(pszBuffer, cBuffer, "res"); } else { StringCchPrintfA(pszBuffer, cBuffer, "%x", State); } } void ProtectToString(DWORD Protect, char *pszBuffer, size_t cBuffer) { if (Protect == 0) { StringCchPrintfA(pszBuffer, cBuffer, ""); } else if (Protect == PAGE_EXECUTE) { StringCchPrintfA(pszBuffer, cBuffer, "--x"); } else if (Protect == PAGE_EXECUTE_READ) { StringCchPrintfA(pszBuffer, cBuffer, "r-x"); } else if (Protect == PAGE_EXECUTE_READWRITE) { StringCchPrintfA(pszBuffer, cBuffer, "rwx"); } else if (Protect == PAGE_EXECUTE_WRITECOPY) { StringCchPrintfA(pszBuffer, cBuffer, "rcx"); } else if (Protect == PAGE_NOACCESS) { StringCchPrintfA(pszBuffer, cBuffer, "---"); } else if (Protect == PAGE_READONLY) { StringCchPrintfA(pszBuffer, cBuffer, "r--"); } else if (Protect == PAGE_READWRITE) { StringCchPrintfA(pszBuffer, cBuffer, "rw-"); } else if (Protect == PAGE_WRITECOPY) { StringCchPrintfA(pszBuffer, cBuffer, "rc-"); } else if (Protect == (PAGE_GUARD | PAGE_EXECUTE)) { StringCchPrintfA(pszBuffer, cBuffer, "g--x"); } else if (Protect == (PAGE_GUARD | PAGE_EXECUTE_READ)) { StringCchPrintfA(pszBuffer, cBuffer, "gr-x"); } else if (Protect == (PAGE_GUARD | PAGE_EXECUTE_READWRITE)) { StringCchPrintfA(pszBuffer, cBuffer, "grwx"); } else if (Protect == (PAGE_GUARD | PAGE_EXECUTE_WRITECOPY)) { StringCchPrintfA(pszBuffer, cBuffer, "grcx"); } else if (Protect == (PAGE_GUARD | PAGE_NOACCESS)) { StringCchPrintfA(pszBuffer, cBuffer, "g---"); } else if (Protect == (PAGE_GUARD | PAGE_READONLY)) { StringCchPrintfA(pszBuffer, cBuffer, "gr--"); } else if (Protect == (PAGE_GUARD | PAGE_READWRITE)) { StringCchPrintfA(pszBuffer, cBuffer, "grw-"); } else if (Protect == (PAGE_GUARD | PAGE_WRITECOPY)) { StringCchPrintfA(pszBuffer, cBuffer, "grc-"); } else { StringCchPrintfA(pszBuffer, cBuffer, "%x", Protect); } } typedef union { struct { DWORD Signature; IMAGE_FILE_HEADER FileHeader; } ih; IMAGE_NT_HEADERS32 ih32; IMAGE_NT_HEADERS64 ih64; } IMAGE_NT_HEADER; struct SECTIONS { PBYTE pbBeg; PBYTE pbEnd; CHAR szName[16]; } Sections[256]; DWORD SectionCount = 0; DWORD Bitness = 0; PCHAR FindSectionName(PBYTE pbBase, PBYTE& pbEnd) { for (DWORD n = 0; n < SectionCount; n++) { if (Sections[n].pbBeg == pbBase) { pbEnd = Sections[n].pbEnd; return Sections[n].szName; } } pbEnd = NULL; return NULL; } ULONG PadToPage(ULONG Size) { return (Size & 0xfff) ? Size + 0x1000 - (Size & 0xfff) : Size; } BOOL GetSections(HANDLE hp, PBYTE pbBase) { DWORD beg = 0; DWORD cnt = 0; SIZE_T done; IMAGE_DOS_HEADER idh; if (!ReadProcessMemory(hp, pbBase, &idh, sizeof(idh), &done) || done != sizeof(idh)) { return FALSE; } if (idh.e_magic != IMAGE_DOS_SIGNATURE) { return FALSE; } IMAGE_NT_HEADER inh; if (!ReadProcessMemory(hp, pbBase + idh.e_lfanew, &inh, sizeof(inh), &done) || done != sizeof(inh)) { printf("No Read\n"); return FALSE; } if (inh.ih.Signature != IMAGE_NT_SIGNATURE) { printf("No NT\n"); return FALSE; } beg = idh.e_lfanew + FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + inh.ih.FileHeader.SizeOfOptionalHeader; cnt = inh.ih.FileHeader.NumberOfSections; Bitness = (inh.ih32.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) ? 32 : 64; #if 0 printf("%d %d count=%d\n", beg, Bitness, cnt); #endif IMAGE_SECTION_HEADER ish; for (DWORD n = 0; n < cnt; n++) { if (!ReadProcessMemory(hp, pbBase + beg + n * sizeof(ish), &ish, sizeof(ish), &done) || done != sizeof(ish)) { printf("No Read\n"); return FALSE; } Sections[n].pbBeg = pbBase + ish.VirtualAddress; Sections[n].pbEnd = pbBase + ish.VirtualAddress + PadToPage(ish.Misc.VirtualSize); memcpy(Sections[n].szName, ish.Name, sizeof(ish.Name)); Sections[n].szName[sizeof(ish.Name)] = '\0'; #if 0 printf("--- %p %s\n", Sections[n].pbBeg, Sections[n].szName); #endif } SectionCount = cnt; return TRUE; } BOOL DumpProcess(HANDLE hp) { ULONG64 base; ULONG64 next; MEMORY_BASIC_INFORMATION mbi; printf(" %12s %8s %8s: %3s %3s %4s %3s : %8s\n", "Address", "Offset", "Size", "Typ", "Sta", "Prot", "Ini", "Contents"); printf(" %12s %8s %8s: %3s %3s %4s %3s : %8s\n", "------------", "--------", "--------", "---", "---", "----", "---", "-----------------"); for (next = 0;;) { base = next; ZeroMemory(&mbi, sizeof(mbi)); if (VirtualQueryEx(hp, (PVOID)base, &mbi, sizeof(mbi)) == 0) { break; } if ((mbi.RegionSize & 0xfff) == 0xfff) { break; } next = (ULONG64)mbi.BaseAddress + mbi.RegionSize; if (mbi.State == MEM_FREE) { continue; } CHAR szType[16]; TypeToString(mbi.Type, szType, ARRAYSIZE(szType)); CHAR szState[16]; StateToString(mbi.State, szState, ARRAYSIZE(szState)); CHAR szProtect[16]; ProtectToString(mbi.Protect, szProtect, ARRAYSIZE(szProtect)); CHAR szAllocProtect[16]; ProtectToString(mbi.AllocationProtect, szAllocProtect, ARRAYSIZE(szAllocProtect)); CHAR szFile[MAX_PATH]; szFile[0] = '\0'; DWORD cb = 0; PCHAR pszFile = szFile; if (base == (ULONG64)mbi.AllocationBase) { #if 0 cb = pfGetMappedFileName(hp, (PVOID)mbi.AllocationBase, szFile, ARRAYSIZE(szFile)); #endif if (GetSections(hp, (PBYTE)mbi.AllocationBase)) { next = base + 0x1000; StringCchPrintfA(szFile, ARRAYSIZE(szFile), "%d-bit PE", Bitness); } } if (cb > 0) { for (DWORD c = 0; c < cb; c++) { szFile[c] = (szFile[c] >= 'a' && szFile[c] <= 'z') ? szFile[c] - 'a' + 'A' : szFile[c]; } szFile[cb] = '\0'; } if ((pszFile = strrchr(szFile, '\\')) == NULL) { pszFile = szFile; } else { pszFile++; } PBYTE pbEnd; PCHAR pszSect = FindSectionName((PBYTE)base, pbEnd); if (pszSect != NULL) { pszFile = pszSect; if (next > (ULONG64)pbEnd) { next = (ULONG64)pbEnd; } } CHAR szDesc[128]; ZeroMemory(&szDesc, ARRAYSIZE(szDesc)); if (base == (ULONG64)mbi.AllocationBase) { StringCchPrintfA(szDesc, ARRAYSIZE(szDesc), " %12I64x %8I64x %8I64x: %3s %3s %4s %3s : %s", (ULONG64)base, (ULONG64)base - (ULONG64)mbi.AllocationBase, (ULONG64)next - (ULONG64)base, szType, szState, szProtect, szAllocProtect, pszFile); } else { StringCchPrintfA(szDesc, ARRAYSIZE(szDesc), " %12s %8I64x %8I64x: %3s %3s %4s %3s : %s", "-", (ULONG64)base - (ULONG64)mbi.AllocationBase, (ULONG64)next - (ULONG64)base, szType, szState, szProtect, szAllocProtect, pszFile); } printf("%s\n", szDesc); } return TRUE; } //////////////////////////////////////////////////////////////////////// main. // int CDECL main(int argc, char **argv) { BOOLEAN fNeedHelp = FALSE; BOOLEAN fVerbose = FALSE; LPCSTR rpszDllsRaw[256]; LPCSTR rpszDllsOut[256]; DWORD nDlls = 0; for (DWORD n = 0; n < ARRAYSIZE(rpszDllsRaw); n++) { rpszDllsRaw[n] = NULL; rpszDllsOut[n] = NULL; } int arg = 1; for (; arg < argc && (argv[arg][0] == '-' || argv[arg][0] == '/'); arg++) { CHAR *argn = argv[arg] + 1; CHAR *argp = argn; while (*argp && *argp != ':' && *argp != '=') argp++; if (*argp == ':' || *argp == '=') *argp++ = '\0'; switch (argn[0]) { case 'd': // Set DLL Name case 'D': if (nDlls < ARRAYSIZE(rpszDllsRaw)) { rpszDllsRaw[nDlls++] = argp; } else { printf("withdll.exe: Too many DLLs.\n"); fNeedHelp = TRUE; break; } break; case 'v': // Verbose case 'V': fVerbose = TRUE; break; case '?': // Help fNeedHelp = TRUE; break; default: fNeedHelp = TRUE; printf("withdll.exe: Bad argument: %s\n", argv[arg]); break; } } if (arg >= argc) { fNeedHelp = TRUE; } if (nDlls == 0) { fNeedHelp = TRUE; } if (fNeedHelp) { PrintUsage(); return 9001; } /////////////////////////////////////////////////////////// Validate DLLs. // for (DWORD n = 0; n < nDlls; n++) { CHAR szDllPath[1024]; PCHAR pszFilePart = NULL; if (!GetFullPathNameA(rpszDllsRaw[n], ARRAYSIZE(szDllPath), szDllPath, &pszFilePart)) { printf("withdll.exe: Error: %s is not a valid path name..\n", rpszDllsRaw[n]); return 9002; } DWORD c = (DWORD)strlen(szDllPath) + 1; PCHAR psz = new CHAR [c]; StringCchCopyA(psz, c, szDllPath); rpszDllsOut[n] = psz; HMODULE hDll = LoadLibraryExA(rpszDllsOut[n], NULL, DONT_RESOLVE_DLL_REFERENCES); if (hDll == NULL) { printf("withdll.exe: Error: %s failed to load (error %ld).\n", rpszDllsOut[n], GetLastError()); return 9003; } ExportContext ec; ec.fHasOrdinal1 = FALSE; ec.nExports = 0; DetourEnumerateExports(hDll, &ec, ExportCallback); FreeLibrary(hDll); if (!ec.fHasOrdinal1) { printf("withdll.exe: Error: %s does not export ordinal #1.\n", rpszDllsOut[n]); printf(" See help entry DetourCreateProcessWithDllEx in Detours.chm.\n"); return 9004; } } ////////////////////////////////////////////////////////////////////////// STARTUPINFOA si; PROCESS_INFORMATION pi; CHAR szCommand[2048]; CHAR szExe[1024]; CHAR szFullExe[1024] = "\0"; PCHAR pszFileExe = NULL; ZeroMemory(&si, sizeof(si)); ZeroMemory(&pi, sizeof(pi)); si.cb = sizeof(si); szCommand[0] = L'\0'; StringCchCopyA(szExe, sizeof(szExe), argv[arg]); for (; arg < argc; arg++) { if (strchr(argv[arg], ' ') != NULL || strchr(argv[arg], '\t') != NULL) { StringCchCatA(szCommand, sizeof(szCommand), "\""); StringCchCatA(szCommand, sizeof(szCommand), argv[arg]); StringCchCatA(szCommand, sizeof(szCommand), "\""); } else { StringCchCatA(szCommand, sizeof(szCommand), argv[arg]); } if (arg + 1 < argc) { StringCchCatA(szCommand, sizeof(szCommand), " "); } } printf("withdll.exe: Starting: `%s'\n", szCommand); for (DWORD n = 0; n < nDlls; n++) { printf("withdll.exe: with `%s'\n", rpszDllsOut[n]); } fflush(stdout); DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED; SetLastError(0); SearchPathA(NULL, szExe, ".exe", ARRAYSIZE(szFullExe), szFullExe, &pszFileExe); if (!DetourCreateProcessWithDllsA(szFullExe[0] ? szFullExe : NULL, szCommand, NULL, NULL, TRUE, dwFlags, NULL, NULL, &si, &pi, nDlls, rpszDllsOut, NULL)) { DWORD dwError = GetLastError(); printf("withdll.exe: DetourCreateProcessWithDllEx failed: %ld\n", dwError); if (dwError == ERROR_INVALID_HANDLE) { #if DETOURS_64BIT printf("withdll.exe: Can't detour a 32-bit target process from a 64-bit parent process.\n"); #else printf("withdll.exe: Can't detour a 64-bit target process from a 32-bit parent process.\n"); #endif } ExitProcess(9009); } if (fVerbose) { DumpProcess(pi.hProcess); } ResumeThread(pi.hThread); WaitForSingleObject(pi.hProcess, INFINITE); DWORD dwResult = 0; if (!GetExitCodeProcess(pi.hProcess, &dwResult)) { printf("withdll.exe: GetExitCodeProcess failed: %ld\n", GetLastError()); return 9010; } for (DWORD n = 0; n < nDlls; n++) { if (rpszDllsOut[n] != NULL) { delete[] rpszDllsOut[n]; rpszDllsOut[n] = NULL; } } return dwResult; } // ///////////////////////////////////////////////////////////////// End of File.