Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1760 lines
38 KiB

  1. /*++
  2. Copyright (c) 2000-2002 Microsoft Corporation
  3. Module Name:
  4. Common.cpp
  5. Abstract:
  6. Common functions for all modules
  7. Notes:
  8. None
  9. History:
  10. 12/15/1999 linstev Created
  11. 01/10/2000 linstev Format to new style
  12. 03/14/2000 robkenny Added StringWiden and StringNWiden,
  13. StringSubstituteRoutine[A|W] was not using the proper compare routine
  14. when calling recursively.
  15. 07/06/2000 t-adams Added IsImage16Bit
  16. 10/18/2000 a-larrsh Move PatternMatch to common removing redundent code in shims.
  17. 10/25/2000 linstev Cleaned up
  18. 08/14/2001 robkenny Moved code inside the ShimLib namespace.
  19. 09/11/2001 mnikkel Modified DebugPrintfList, DebugPrintf, ShimLogList and ShimLog to retain LastError
  20. 09/25/2001 rparsons Modified logging code to use NT calls. Added critical section.
  21. 10/18/2001 rparsons Removed critical section, added mutex for logging.
  22. 02/15/2002 robkenny Security changes.
  23. --*/
  24. #include "ShimHook.h"
  25. #include "ShimLib.h"
  26. #include "ShimHookMacro.h"
  27. #include "StrSafe.h"
  28. #include <stdio.h>
  29. namespace ShimLib
  30. {
  31. #define MAX_LOG_LENGTH 1024
  32. static char g_szLog[MAX_LOG_LENGTH];
  33. WCHAR g_wszFileLog[MAX_PATH]; // name of the log file
  34. BOOL g_bFileLogEnabled = FALSE; // enable/disable file logging
  35. static HANDLE g_hMemoryHeap = INVALID_HANDLE_VALUE;
  36. BOOL g_bDebugLevelInitialized = FALSE;
  37. DEBUGLEVEL g_DebugLevel = eDbgLevelBase;
  38. inline HANDLE GetHeap()
  39. {
  40. if (g_hMemoryHeap == INVALID_HANDLE_VALUE)
  41. {
  42. g_hMemoryHeap = HeapCreate(0, 0, 0);
  43. }
  44. return g_hMemoryHeap;
  45. }
  46. void * __cdecl ShimMalloc(size_t size)
  47. {
  48. HANDLE heap = GetHeap();
  49. void* memory = HeapAlloc(heap, HEAP_ZERO_MEMORY, size);
  50. return memory;
  51. }
  52. void __cdecl ShimFree(void * memory)
  53. {
  54. HANDLE heap = GetHeap();
  55. HeapFree(heap, 0, memory);
  56. }
  57. void * __cdecl ShimCalloc( size_t num, size_t size )
  58. {
  59. size_t nBytes = size * num;
  60. void * callocMemory = ShimMalloc(nBytes);
  61. ZeroMemory(callocMemory, nBytes);
  62. return callocMemory;
  63. }
  64. void * __cdecl ShimRealloc(void * memory, size_t size)
  65. {
  66. if (memory == NULL)
  67. return ShimMalloc(size);
  68. HANDLE heap = GetHeap();
  69. void * reallocMemory = HeapReAlloc(heap, 0, memory, size);
  70. return reallocMemory;
  71. }
  72. DEBUGLEVEL GetDebugLevel()
  73. {
  74. CHAR cEnv[MAX_PATH];
  75. if (g_bDebugLevelInitialized) {
  76. return g_DebugLevel;
  77. }
  78. g_DebugLevel = eDbgLevelBase;
  79. DWORD cchEnv = GetEnvironmentVariableA(szDebugEnvironmentVariable,
  80. cEnv,
  81. MAX_PATH);
  82. if (cchEnv > 0 && cchEnv < MAX_PATH) {
  83. CHAR c = cEnv[0];
  84. if ((c >= '0') || (c <= '9')) {
  85. g_DebugLevel = (DEBUGLEVEL)((int)(c - '0'));
  86. }
  87. }
  88. g_bDebugLevelInitialized = TRUE;
  89. return g_DebugLevel;
  90. }
  91. /*++
  92. Function Description:
  93. Assert that prints file and line number.
  94. Arguments:
  95. IN LPCSTR file name
  96. IN DWORD line number
  97. IN BOOL assertion
  98. IN LPCSTR String to print if assertion is false
  99. Return Value:
  100. None
  101. History:
  102. 11/01/1999 markder Created
  103. --*/
  104. #if DBG
  105. VOID
  106. DebugAssert(
  107. LPCSTR szFile,
  108. DWORD dwLine,
  109. BOOL bAssert,
  110. LPCSTR szHelpString
  111. )
  112. {
  113. if (!bAssert )
  114. {
  115. DPF("ShimLib", eDbgLevelError, "\n");
  116. DPF("ShimLib", eDbgLevelError, "ASSERT: %s\n", szHelpString);
  117. DPF("ShimLib", eDbgLevelError, "FILE: %s\n", szFile);
  118. DPF("ShimLib", eDbgLevelError, "LINE: %d\n", dwLine);
  119. DPF("ShimLib", eDbgLevelError, "\n");
  120. DebugBreak();
  121. }
  122. }
  123. #endif // DBG
  124. /*++
  125. Function Description:
  126. Print a formatted string using DebugOutputString.
  127. Arguments:
  128. IN dwDetail - Detail level above which no print will occur
  129. IN pszFmt - Format string
  130. Return Value:
  131. None
  132. History:
  133. 11/01/1999 markder Created
  134. --*/
  135. VOID
  136. DebugPrintfList(
  137. LPCSTR szShimName,
  138. DEBUGLEVEL dwDetail,
  139. LPCSTR pszFmt,
  140. va_list vaArgList
  141. )
  142. {
  143. #if DBG
  144. // This must be the first line of this routine to preserve LastError.
  145. DWORD dwLastError = GetLastError();
  146. extern DEBUGLEVEL GetDebugLevel();
  147. char szT[1024];
  148. szT[1022] = '\0';
  149. StringCchVPrintfA(szT, 1022, pszFmt, vaArgList);
  150. // make sure we have a '\n' at the end of the string
  151. int len = lstrlen(szT);
  152. if (szT[len-1] != '\n')
  153. {
  154. szT[len] = L'\n';
  155. szT[len+1] = L'\0';
  156. }
  157. if (dwDetail <= GetDebugLevel())
  158. {
  159. switch (dwDetail)
  160. {
  161. case eDbgLevelError:
  162. OutputDebugStringA ("[FAIL] ");
  163. break;
  164. case eDbgLevelWarning:
  165. OutputDebugStringA ("[WARN] ");
  166. break;
  167. case eDbgLevelInfo:
  168. OutputDebugStringA ("[INFO] ");
  169. break;
  170. }
  171. OutputDebugStringA(szShimName);
  172. OutputDebugStringA(" - ");
  173. OutputDebugStringA(szT);
  174. }
  175. // This must be the last line of this routine to preserve LastError.
  176. SetLastError(dwLastError);
  177. #endif
  178. }
  179. VOID
  180. DebugPrintf(
  181. LPCSTR szShimName,
  182. DEBUGLEVEL dwDetail,
  183. LPCSTR pszFmt,
  184. ...
  185. )
  186. {
  187. #if DBG
  188. // This must be the first line of this routine to preserve LastError.
  189. DWORD dwLastError = GetLastError();
  190. va_list vaArgList;
  191. va_start(vaArgList, pszFmt);
  192. DebugPrintfList(szShimName, dwDetail, pszFmt, vaArgList);
  193. va_end(vaArgList);
  194. // This must be the last line of this routine to preserve LastError.
  195. SetLastError(dwLastError);
  196. #endif
  197. }
  198. /*++
  199. Function Description:
  200. Prints a log in the log file if logging is enabled
  201. Arguments:
  202. IN pszFmt - Format string
  203. Return Value:
  204. none
  205. History:
  206. 03/03/2000 clupu Created
  207. --*/
  208. /*++
  209. Function Description:
  210. Prints a log in the log file if logging is enabled
  211. Arguments:
  212. IN wszShimName - Name of shim that string originates from
  213. IN dwDetail - Detail level above which no print will occur
  214. IN pszFmt - Format string
  215. Return Value:
  216. none
  217. History:
  218. 03/03/2000 clupu Created
  219. 09/25/2001 rparsons Converted to NT calls
  220. --*/
  221. void
  222. ShimLogList(
  223. LPCSTR szShimName,
  224. DEBUGLEVEL dwDbgLevel,
  225. LPCSTR pszFmt,
  226. va_list arglist
  227. )
  228. {
  229. //
  230. // This must be the first line of this routine to preserve LastError.
  231. //
  232. DWORD dwLastError = GetLastError();
  233. int nLen = 0;
  234. NTSTATUS status;
  235. SYSTEMTIME lt;
  236. UNICODE_STRING strLogFile = {0};
  237. OBJECT_ATTRIBUTES ObjectAttributes;
  238. IO_STATUS_BLOCK IoStatusBlock;
  239. LARGE_INTEGER liOffset;
  240. char szNewLine[] = "\r\n";
  241. DWORD dwWaitResult;
  242. HANDLE hFile = INVALID_HANDLE_VALUE;
  243. HANDLE hLogMutex;
  244. //
  245. // Convert the path to the log file from DOS to NT.
  246. //
  247. RtlInitUnicodeString(&strLogFile, g_wszFileLog);
  248. status = RtlDosPathNameToNtPathName_U(strLogFile.Buffer, &strLogFile, NULL, NULL);
  249. if (!NT_SUCCESS(status)) {
  250. DPF("ShimLib", eDbgLevelError,
  251. "[ShimLogList] 0x%X Failed to convert log file '%ls' to NT path",
  252. status, g_wszFileLog);
  253. return;
  254. }
  255. //
  256. // Attempt to get a handle to our log file.
  257. //
  258. InitializeObjectAttributes(&ObjectAttributes,
  259. &strLogFile,
  260. OBJ_CASE_INSENSITIVE,
  261. NULL,
  262. NULL);
  263. status = NtCreateFile(&hFile,
  264. FILE_APPEND_DATA | SYNCHRONIZE,
  265. &ObjectAttributes,
  266. &IoStatusBlock,
  267. NULL,
  268. FILE_ATTRIBUTE_NORMAL,
  269. 0,
  270. FILE_OPEN,
  271. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  272. NULL,
  273. 0);
  274. RtlFreeUnicodeString(&strLogFile);
  275. if (!NT_SUCCESS(status)) {
  276. DPF("ShimLib", eDbgLevelError, "[ShimLogList] 0x%X Failed to open log file %ls",
  277. status, g_wszFileLog);
  278. return;
  279. }
  280. SetFilePointer(hFile, 0, NULL, FILE_END);
  281. //
  282. // Print a header consisting of data, time, app name, and shim name.
  283. //
  284. GetLocalTime(&lt);
  285. StringCbPrintf(g_szLog, MAX_LOG_LENGTH, "%02d/%02d/%04d %02d:%02d:%02d %s %d - ",
  286. lt.wMonth, lt.wDay, lt.wYear,
  287. lt.wHour, lt.wMinute, lt.wSecond,
  288. szShimName,
  289. dwDbgLevel);
  290. nLen = lstrlen(g_szLog);
  291. //
  292. // Write the header out to the file.
  293. //
  294. IoStatusBlock.Status = 0;
  295. IoStatusBlock.Information = 0;
  296. liOffset.LowPart = 0;
  297. liOffset.HighPart = 0;
  298. //
  299. // Get a handle to the mutex and attempt to get ownership.
  300. //
  301. hLogMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "SHIMLIB_LOG_MUTEX");
  302. if (!hLogMutex) {
  303. DPF("ShimLib", eDbgLevelError, "[ShimLogList] %lu Failed to open logging mutex", GetLastError());
  304. goto exit;
  305. }
  306. dwWaitResult = WaitForSingleObject(hLogMutex, 500);
  307. if (WAIT_OBJECT_0 == dwWaitResult) {
  308. //
  309. // Write the header to the log file.
  310. //
  311. status = NtWriteFile(hFile,
  312. NULL,
  313. NULL,
  314. NULL,
  315. &IoStatusBlock,
  316. (PVOID)g_szLog,
  317. (ULONG)nLen,
  318. &liOffset,
  319. NULL);
  320. if (!NT_SUCCESS(status)) {
  321. DPF("ShimLib", eDbgLevelError, "[ShimLogList] 0x%X Failed to write header to log file",
  322. status);
  323. goto exit;
  324. }
  325. //
  326. // Format our string using the specifiers passed.
  327. //
  328. StringCchVPrintfA(g_szLog, MAX_LOG_LENGTH - 1, pszFmt, arglist);
  329. //
  330. // Write the actual data out to the file.
  331. //
  332. IoStatusBlock.Status = 0;
  333. IoStatusBlock.Information = 0;
  334. liOffset.LowPart = 0;
  335. liOffset.HighPart = 0;
  336. nLen = lstrlen(g_szLog);
  337. status = NtWriteFile(hFile,
  338. NULL,
  339. NULL,
  340. NULL,
  341. &IoStatusBlock,
  342. (PVOID)g_szLog,
  343. (ULONG)nLen,
  344. &liOffset,
  345. NULL);
  346. if (!NT_SUCCESS(status)) {
  347. DPF("ShimLib", eDbgLevelError, "[ShimLogList] 0x%X Failed to make entry in log file",
  348. status);
  349. goto exit;
  350. }
  351. //
  352. // Now write a new line to the log file.
  353. //
  354. IoStatusBlock.Status = 0;
  355. IoStatusBlock.Information = 0;
  356. liOffset.LowPart = 0;
  357. liOffset.HighPart = 0;
  358. nLen = lstrlen(szNewLine);
  359. status = NtWriteFile(hFile,
  360. NULL,
  361. NULL,
  362. NULL,
  363. &IoStatusBlock,
  364. (PVOID)szNewLine,
  365. (ULONG)nLen,
  366. &liOffset,
  367. NULL);
  368. if (!NT_SUCCESS(status)) {
  369. DPF("ShimLib", eDbgLevelError, "[ShimLogList] 0x%X Failed to write new line to log file",
  370. status);
  371. goto exit;
  372. }
  373. }
  374. //
  375. // Dump it out to the debugger on checked builds.
  376. //
  377. #if DBG
  378. DebugPrintf(szShimName, dwDbgLevel, g_szLog);
  379. DebugPrintf(szShimName, dwDbgLevel, "\n");
  380. #endif // DBG
  381. exit:
  382. if (INVALID_HANDLE_VALUE != hFile) {
  383. NtClose(hFile);
  384. hFile = INVALID_HANDLE_VALUE;
  385. }
  386. if (hLogMutex) {
  387. ReleaseMutex(hLogMutex);
  388. }
  389. //
  390. // This must be the last line of this routine to preserve LastError.
  391. //
  392. SetLastError(dwLastError);
  393. }
  394. /*++
  395. Function Description:
  396. Initializes the support for file logging.
  397. Arguments:
  398. None.
  399. Return Value:
  400. TRUE if successful, FALSE if failed
  401. History:
  402. 03/03/2000 clupu Created
  403. --*/
  404. BOOL
  405. InitFileLogSupport()
  406. {
  407. BOOL fReturn = FALSE;
  408. WCHAR wszAppPatch[MAX_PATH];
  409. WCHAR* pwsz = NULL;
  410. HANDLE hFile = INVALID_HANDLE_VALUE;
  411. HANDLE hLogMutex = NULL;
  412. DWORD dwLen = 0;
  413. DWORD dwWait;
  414. NTSTATUS status;
  415. UNICODE_STRING strLogFile = {0};
  416. OBJECT_ATTRIBUTES ObjectAttributes;
  417. IO_STATUS_BLOCK IoStatusBlock;
  418. //
  419. // Attempt to create a mutex. If the mutex already exists,
  420. // we don't need to go any further as the log file has
  421. // already been created.
  422. //
  423. hLogMutex = CreateMutex(NULL, FALSE, "SHIMLIB_LOG_MUTEX");
  424. if (hLogMutex == NULL) {
  425. DPF("ShimLib",
  426. eDbgLevelError,
  427. "[InitFileLogSupport] 0x%08X Failed to create logging mutex",
  428. GetLastError());
  429. return FALSE;
  430. }
  431. DWORD dwLastError = GetLastError();
  432. if (ERROR_ALREADY_EXISTS == dwLastError) {
  433. fReturn = TRUE;
  434. goto exit;
  435. }
  436. //
  437. // Ensure that we own the mutex before continuing.
  438. //
  439. dwWait = WaitForSingleObject(hLogMutex, 2000);
  440. if (WAIT_OBJECT_0 != dwWait) {
  441. //
  442. // Failed to obtain ownership.
  443. //
  444. DPF("ShimLib",
  445. eDbgLevelError,
  446. "[InitFileLogSupport] Wait on mutex failed");
  447. return FALSE;
  448. }
  449. //
  450. // We'll create the log file in %windir%\AppPatch.
  451. //
  452. if (!GetSystemWindowsDirectoryW(g_wszFileLog, MAX_PATH)) {
  453. DPF("ShimLib",
  454. eDbgLevelError,
  455. "[InitFileLogSupport] 0x%08X Failed to get windir path",
  456. GetLastError());
  457. goto exit;
  458. }
  459. StringCchCatW(g_wszFileLog, MAX_PATH, L"\\AppPatch\\");
  460. dwLen = lstrlenW(g_wszFileLog);
  461. pwsz = g_wszFileLog + dwLen;
  462. //
  463. // Query the environment variable and get the name of our log file.
  464. //
  465. if (!GetEnvironmentVariableW(wszFileLogEnvironmentVariable,
  466. pwsz,
  467. (MAX_PATH - dwLen))) {
  468. goto exit;
  469. }
  470. //
  471. // Convert the path to the log file from DOS to NT.
  472. //
  473. RtlInitUnicodeString(&strLogFile, g_wszFileLog);
  474. status = RtlDosPathNameToNtPathName_U(strLogFile.Buffer,
  475. &strLogFile,
  476. NULL,
  477. NULL);
  478. if (!NT_SUCCESS(status)) {
  479. DPF("ShimLib",
  480. eDbgLevelError,
  481. "[InitFileLogSupport] 0x%X Failed to convert log file '%ls' to NT path",
  482. status,
  483. g_wszFileLog);
  484. goto exit;
  485. }
  486. //
  487. // Attempt to create the log file. If it exists,
  488. // the contents will be cleared.
  489. //
  490. InitializeObjectAttributes(&ObjectAttributes,
  491. &strLogFile,
  492. OBJ_CASE_INSENSITIVE,
  493. NULL,
  494. NULL);
  495. status = NtCreateFile(&hFile,
  496. GENERIC_WRITE | SYNCHRONIZE,
  497. &ObjectAttributes,
  498. &IoStatusBlock,
  499. NULL,
  500. FILE_ATTRIBUTE_NORMAL,
  501. 0,
  502. FILE_OPEN_IF,
  503. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  504. NULL,
  505. 0);
  506. RtlFreeUnicodeString(&strLogFile);
  507. if (!NT_SUCCESS(status)) {
  508. DPF("ShimLib",
  509. eDbgLevelError,
  510. "[InitFileLogSupport] 0x%X Failed to open log file %ls",
  511. status,
  512. g_wszFileLog);
  513. goto exit;
  514. }
  515. NtClose(hFile);
  516. g_bFileLogEnabled = TRUE;
  517. fReturn = TRUE;
  518. exit:
  519. ReleaseMutex(hLogMutex);
  520. return fReturn;
  521. }
  522. /*++
  523. Function Description:
  524. Determine the drive type a file resides on.
  525. Arguments:
  526. IN lpFileName - Filename or relative filename
  527. Return Value:
  528. See GetDriveType in MSDN
  529. History:
  530. 10/25/2000 linstev Created
  531. --*/
  532. UINT
  533. GetDriveTypeFromFileNameA(LPCSTR lpFileName, char *lpDriveLetter)
  534. {
  535. WCHAR * lpwszFileName = ToUnicode(lpFileName);
  536. if (lpwszFileName)
  537. {
  538. WCHAR szDrive;
  539. UINT uType = GetDriveTypeFromFileNameW(lpwszFileName, &szDrive);
  540. if (lpDriveLetter)
  541. {
  542. char * lpszFileName = ToAnsi(lpwszFileName);
  543. if (lpszFileName)
  544. {
  545. *lpDriveLetter = lpszFileName[0];
  546. free(lpszFileName);
  547. }
  548. }
  549. free(lpwszFileName);
  550. return uType;
  551. }
  552. else
  553. {
  554. return DRIVE_UNKNOWN;
  555. }
  556. }
  557. /*++
  558. Function Description:
  559. Determine the drive type a file resides on.
  560. Arguments:
  561. IN lpFileName - Filename or relative filename
  562. Return Value:
  563. See GetDriveType in MSDN
  564. History:
  565. 10/25/2000 linstev Created
  566. --*/
  567. UINT
  568. GetDriveTypeFromFileNameW(LPCWSTR lpFileName, WCHAR *lpDriveLetter)
  569. {
  570. if (lpFileName && (lpFileName[0] == L'\\') && (lpFileName[1] == L'\\'))
  571. {
  572. // UNC naming - always network
  573. if (lpDriveLetter)
  574. {
  575. *lpDriveLetter = L'\0';
  576. }
  577. return DRIVE_REMOTE;
  578. }
  579. WCHAR cDrive;
  580. if (lpFileName && lpFileName[0] && (lpFileName[1] == L':'))
  581. {
  582. // Format is Drive:Path\File, so just take the drive
  583. cDrive = lpFileName[0];
  584. }
  585. else
  586. {
  587. // Must be a relative path
  588. cDrive = 0;
  589. WCHAR *wzCurDir = NULL;
  590. DWORD dwCurDirSize = GetCurrentDirectoryW(0, wzCurDir);
  591. if (!dwCurDirSize)
  592. {
  593. goto EXIT;
  594. }
  595. wzCurDir = (LPWSTR) LocalAlloc(LPTR, dwCurDirSize * sizeof(WCHAR));
  596. if (!wzCurDir)
  597. {
  598. goto EXIT;
  599. }
  600. dwCurDirSize = GetCurrentDirectoryW(dwCurDirSize, wzCurDir);
  601. if (!dwCurDirSize || wzCurDir[0] == L'\\')
  602. {
  603. goto EXIT;
  604. }
  605. cDrive = wzCurDir[0];
  606. EXIT:
  607. if (wzCurDir)
  608. {
  609. LocalFree(wzCurDir);
  610. }
  611. }
  612. if (lpDriveLetter)
  613. {
  614. *lpDriveLetter = L'\0';
  615. }
  616. if (cDrive)
  617. {
  618. WCHAR wzDrive[4];
  619. wzDrive[0] = cDrive;
  620. wzDrive[1] = L':';
  621. wzDrive[2] = L'\\';
  622. wzDrive[3] = L'\0';
  623. if (lpDriveLetter)
  624. {
  625. *lpDriveLetter = cDrive;
  626. }
  627. return GetDriveTypeW(wzDrive);
  628. }
  629. else
  630. {
  631. return DRIVE_UNKNOWN;
  632. }
  633. }
  634. /*++
  635. Function Description:
  636. Widen and duplicate a string into malloc memory.
  637. Arguments:
  638. IN strToCopy - String to copy
  639. Return Value:
  640. String in malloc memory
  641. History:
  642. 03/07/2000 robkenny Created
  643. 05/16/2000 robkenny Moved MassagePath (shim specific) routines out of here.
  644. --*/
  645. WCHAR *
  646. ToUnicode(const char *strToCopy)
  647. {
  648. if (strToCopy == NULL)
  649. {
  650. return NULL;
  651. }
  652. // Get the number of characters in the resulting string, includes NULL at end
  653. int nChars = MultiByteToWideChar(CP_ACP, 0, strToCopy, -1, NULL, 0);
  654. WCHAR *lpwsz = (WCHAR *) malloc(nChars * sizeof(WCHAR));
  655. if (lpwsz)
  656. {
  657. nChars = MultiByteToWideChar(CP_ACP, 0, strToCopy, -1, lpwsz, nChars);
  658. // If MultibyteToWideChar failed, return NULL
  659. if (nChars == 0)
  660. {
  661. free(lpwsz);
  662. lpwsz = NULL;
  663. }
  664. }
  665. return lpwsz;
  666. }
  667. /*++
  668. Function Description:
  669. Convert a WCHAR string to a char string
  670. Arguments:
  671. IN lpOld - String to convert to char
  672. Return Value:
  673. char string in malloc memory
  674. History:
  675. 06/19/2000 robkenny Created
  676. --*/
  677. char *
  678. ToAnsi(const WCHAR *lpOld)
  679. {
  680. if (lpOld == NULL)
  681. {
  682. return NULL;
  683. }
  684. // Get the number of bytes necessary for the WCHAR string
  685. int nBytes = WideCharToMultiByte(CP_ACP, 0, lpOld, -1, NULL, 0, NULL, NULL);
  686. char *lpsz = (char *) malloc(nBytes);
  687. if (lpsz)
  688. {
  689. nBytes = WideCharToMultiByte(CP_ACP, 0, lpOld, -1, lpsz, nBytes, NULL, NULL);
  690. // If WideCharToMultibyte failed, return NULL
  691. if (nBytes == 0)
  692. {
  693. free(lpsz);
  694. lpsz = NULL;
  695. }
  696. }
  697. return lpsz;
  698. }
  699. /*++
  700. Function Description:
  701. Duplicate the first nChars of strToCopy string into malloc memory.
  702. Arguments:
  703. IN strToCopy - String to copy
  704. IN nChar - Number of chars to duplicate, does not count NULL at end.
  705. Return Value:
  706. String in malloc memory
  707. History:
  708. 06/02/2000 robkenny Created
  709. --*/
  710. char *
  711. StringNDuplicateA(const char *strToCopy, int nChars)
  712. {
  713. if (strToCopy == NULL)
  714. {
  715. return NULL;
  716. }
  717. size_t nBytes = (nChars + 1) * sizeof(strToCopy[0]);
  718. char *strDuplicate = (char *) malloc(nBytes);
  719. if (strDuplicate)
  720. {
  721. memcpy(strDuplicate, strToCopy, nBytes);
  722. strDuplicate[nChars] = 0;
  723. }
  724. return strDuplicate;
  725. }
  726. /*++
  727. Function Description:
  728. Duplicate a string into malloc memory.
  729. Arguments:
  730. IN strToCopy - String to copy
  731. Return Value:
  732. String in malloc memory
  733. History:
  734. 01/10/2000 linstev Updated
  735. 02/14/2000 robkenny Converted from VirtualAlloc to malloc
  736. 06/02/2000 robkenny Use StringNDuplicateA
  737. --*/
  738. char *
  739. StringDuplicateA(const char *strToCopy)
  740. {
  741. if (strToCopy == NULL)
  742. {
  743. return NULL;
  744. }
  745. char *strDuplicate = StringNDuplicateA(strToCopy, strlen(strToCopy));
  746. return strDuplicate;
  747. }
  748. /*++
  749. Function Description:
  750. Duplicate the first nChars of strToCopy string into malloc memory.
  751. Arguments:
  752. IN strToCopy - String to copy
  753. IN nChar - Number of chars to duplicate, does not count NULL at end.
  754. Return Value:
  755. String in malloc memory
  756. History:
  757. 06/02/2000 robkenny Created
  758. --*/
  759. WCHAR *
  760. StringNDuplicateW(const WCHAR *strToCopy, int nChars)
  761. {
  762. if (strToCopy == NULL)
  763. {
  764. return NULL;
  765. }
  766. size_t nBytes = (nChars + 1) * sizeof(strToCopy[0]);
  767. WCHAR *strDuplicate = (WCHAR *) malloc(nBytes);
  768. if (strDuplicate)
  769. {
  770. memcpy(strDuplicate, strToCopy, nBytes);
  771. strDuplicate[nChars] = 0;
  772. }
  773. return strDuplicate;
  774. }
  775. /*++
  776. Function Description:
  777. Duplicate a string into malloc memory.
  778. Arguments:
  779. IN strToCopy - String to copy
  780. Return Value:
  781. String in malloc memory
  782. History:
  783. 01/10/2000 linstev Updated
  784. 02/14/2000 robkenny Converted from VirtualAlloc to malloc
  785. 06/02/2000 robkenny Use StringNDuplicateW
  786. --*/
  787. WCHAR *
  788. StringDuplicateW(const WCHAR *strToCopy)
  789. {
  790. if (strToCopy == NULL)
  791. {
  792. return NULL;
  793. }
  794. WCHAR *wstrDuplicate = StringNDuplicateW(strToCopy, wcslen(strToCopy));
  795. return wstrDuplicate;
  796. }
  797. /*++
  798. Function Description:
  799. Skip leading whitespace
  800. Arguments:
  801. IN str - String to scan
  802. Return Value:
  803. None
  804. History:
  805. 01/10/2000 linstev Updated
  806. --*/
  807. VOID
  808. SkipBlanksW(const WCHAR *& str)
  809. {
  810. if (str)
  811. {
  812. // Skip leading whitespace
  813. static const WCHAR *WhiteSpaceString = L" \t";
  814. str += wcsspn(str, WhiteSpaceString);
  815. }
  816. }
  817. /*++
  818. Function Description:
  819. Find the first occurance of strCharSet in string
  820. Case insensitive
  821. Arguments:
  822. IN string - String to search
  823. IN strCharSet - String to search for
  824. Return Value:
  825. First occurance or NULL
  826. History:
  827. 12/01/1999 robkenny Created
  828. 12/15/1999 linstev Reformatted
  829. --*/
  830. char*
  831. __cdecl
  832. stristr(
  833. IN const char* string,
  834. IN const char* strCharSet
  835. )
  836. {
  837. char *pszRet = NULL;
  838. long nstringLen = strlen(string) + 1;
  839. long nstrCharSetLen = strlen(strCharSet) + 1;
  840. char *szTemp_string = (char *) malloc(nstringLen);
  841. char *szTemp_strCharSet = (char *) malloc(nstrCharSetLen);
  842. if ((!szTemp_string) || (!szTemp_strCharSet))
  843. {
  844. goto Fail;
  845. }
  846. StringCchCopyA(szTemp_string, nstringLen, string);
  847. StringCchCopyA(szTemp_strCharSet, nstrCharSetLen, strCharSet);
  848. _strlwr(szTemp_string);
  849. _strlwr(szTemp_strCharSet);
  850. pszRet = strstr(szTemp_string, szTemp_strCharSet);
  851. if (pszRet)
  852. {
  853. pszRet = ((char *) string) + (pszRet - szTemp_string);
  854. }
  855. Fail:
  856. if (szTemp_string)
  857. {
  858. free(szTemp_string);
  859. }
  860. if (szTemp_strCharSet)
  861. {
  862. free(szTemp_strCharSet);
  863. }
  864. return pszRet;
  865. }
  866. /*++
  867. Function Description:
  868. Find the first occurance of strCharSet in string
  869. Case insensitive
  870. Arguments:
  871. IN string - String to search
  872. IN strCharSet - String to search for
  873. Return Value:
  874. First occurance or NULL
  875. History:
  876. 12/01/1999 robkenny Created
  877. 12/15/1999 linstev Reformatted
  878. 05/04/2001 maonis Changed to use more efficient implementation.
  879. --*/
  880. #define _UPPER 0x1 /* upper case letter */
  881. #define iswupper(_c) (iswctype(_c,_UPPER))
  882. WCHAR*
  883. __cdecl
  884. wcsistr(
  885. IN const WCHAR* wcs1,
  886. IN const WCHAR* wcs2
  887. )
  888. {
  889. wchar_t *cp = (wchar_t *) wcs1;
  890. wchar_t *s1, *s2;
  891. wchar_t cs1, cs2;
  892. while (*cp)
  893. {
  894. s1 = cp;
  895. s2 = (wchar_t *) wcs2;
  896. cs1 = *s1;
  897. cs2 = *s2;
  898. if (iswupper(cs1))
  899. cs1 = towlower(cs1);
  900. if (iswupper(cs2))
  901. cs2 = towlower(cs2);
  902. while ( *s1 && *s2 && !(cs1-cs2) ) {
  903. s1++, s2++;
  904. cs1 = *s1;
  905. cs2 = *s2;
  906. if (iswupper(cs1))
  907. cs1 = towlower(cs1);
  908. if (iswupper(cs2))
  909. cs2 = towlower(cs2);
  910. }
  911. if (!*s2)
  912. return(cp);
  913. cp++;
  914. }
  915. return(NULL);
  916. }
  917. /*++
  918. Function Description:
  919. Find the next token in a string. See strtok in MSDN.
  920. Implemented here so we don't need CRT.
  921. Arguments:
  922. OUT strToken - string containing token(s)
  923. IN strDelimit - token list
  924. Return Value:
  925. Return a pointer to the next token found.
  926. History:
  927. 04/19/2000 linstev Created
  928. --*/
  929. char *
  930. __cdecl
  931. _strtok(
  932. char *strToken,
  933. const char *strDelimit
  934. )
  935. {
  936. unsigned char *str = (unsigned char *)strToken;
  937. const unsigned char *ctrl = (const unsigned char *)strDelimit;
  938. unsigned char map[32];
  939. int count;
  940. char *token;
  941. static char *nextoken;
  942. // Clear strDelimit map
  943. for (count = 0; count < 32; count++)
  944. {
  945. map[count] = 0;
  946. }
  947. // Set bits in delimiter table
  948. do
  949. {
  950. map[*ctrl >> 3] |= (1 << (*ctrl & 7));
  951. } while (*ctrl++);
  952. // If strToken==NULL, continue with previous strToken
  953. if (!str)
  954. {
  955. str = (unsigned char *)nextoken;
  956. }
  957. // Find beginning of token (skip over leading delimiters). Note that
  958. // there is no token iff this loop sets strToken to point to the terminal
  959. // null (*strToken == '\0')
  960. while ((map[*str >> 3] & (1 << (*str & 7))) && *str)
  961. {
  962. str++;
  963. }
  964. token = (char *)str;
  965. // Find the end of the token. If it is not the end of the strToken,
  966. // put a null there.
  967. for (; *str; str++)
  968. {
  969. if (map[*str >> 3] & (1 << (*str & 7)))
  970. {
  971. *str++ = '\0';
  972. break;
  973. }
  974. }
  975. // Update nextoken (or the corresponding field in the per-thread data
  976. // structure
  977. nextoken = (char *)str;
  978. // Determine if a token has been found
  979. if (token == (char *)str)
  980. {
  981. return NULL;
  982. }
  983. else
  984. {
  985. return token;
  986. }
  987. }
  988. /*++
  989. Function Description:
  990. Tests whether an executable is 16-Bit.
  991. Arguments:
  992. IN szImageName - The name of the executable image.
  993. Return Value:
  994. TRUE if executable image is found to be 16-bit, FALSE otherwise.
  995. History:
  996. 07/06/2000 t-adams Created
  997. --*/
  998. BOOL
  999. IsImage16BitA(LPCSTR lpApplicationName)
  1000. {
  1001. DWORD dwBinaryType;
  1002. if (GetBinaryTypeA(lpApplicationName, &dwBinaryType))
  1003. {
  1004. return (dwBinaryType == SCS_WOW_BINARY);
  1005. }
  1006. else
  1007. {
  1008. return FALSE;
  1009. }
  1010. }
  1011. /*++
  1012. Function Description:
  1013. Tests whether an executable is 16-Bit.
  1014. Arguments:
  1015. IN wstrImageName - The name of the executable image.
  1016. Return Value:
  1017. TRUE if executable image is found to be 16-bit, FALSE otherwise.
  1018. History:
  1019. 07/06/2000 t-adams Created
  1020. --*/
  1021. BOOL
  1022. IsImage16BitW(LPCWSTR lpApplicationName)
  1023. {
  1024. DWORD dwBinaryType;
  1025. if (GetBinaryTypeW(lpApplicationName, &dwBinaryType))
  1026. {
  1027. return (dwBinaryType == SCS_WOW_BINARY);
  1028. }
  1029. else
  1030. {
  1031. return FALSE;
  1032. }
  1033. }
  1034. /*++
  1035. Function Description:
  1036. Match these two strings, with wildcards.
  1037. ? matches a single character
  1038. * matches 0 or more characters
  1039. The compare is case in-sensitive
  1040. Arguments:
  1041. IN pszPattern - Pattern for matching.
  1042. IN pszTestString - String to match against.
  1043. Return Value:
  1044. TRUE if pszTestString matches pszPattern.
  1045. History:
  1046. 01/09/2001 markder Replaced non-straightforward version.
  1047. --*/
  1048. BOOL
  1049. PatternMatchW(
  1050. IN LPCWSTR pszPattern,
  1051. IN LPCWSTR pszTestString)
  1052. {
  1053. //
  1054. // March through pszTestString. Each time through the loop,
  1055. // pszTestString is advanced one character.
  1056. //
  1057. for (;;) {
  1058. //
  1059. // If pszPattern and pszTestString are both sitting on a NULL,
  1060. // then they reached the end at the same time and the strings
  1061. // must be equal.
  1062. //
  1063. if (*pszPattern == L'\0' && *pszTestString == L'\0') {
  1064. return TRUE;
  1065. }
  1066. if (*pszPattern != L'*') {
  1067. //
  1068. // Non-asterisk mode. Look for a match on this character.
  1069. //
  1070. switch (*(pszPattern)) {
  1071. case L'?':
  1072. //
  1073. // Match on any character, don't bother comparing.
  1074. //
  1075. pszPattern++;
  1076. break;
  1077. case L'\\':
  1078. //
  1079. // Backslash indicates to take the next character
  1080. // verbatim. Advance the pointer before making a
  1081. // comparison.
  1082. //
  1083. pszPattern++;
  1084. default:
  1085. //
  1086. // Compare the characters. If equal, continue traversing.
  1087. // Otherwise, the strings cannot be equal so return FALSE.
  1088. //
  1089. if (towupper(*pszPattern) == towupper(*pszTestString)) {
  1090. pszPattern++;
  1091. } else {
  1092. return FALSE;
  1093. }
  1094. }
  1095. } else {
  1096. //
  1097. // Asterisk mode. Look for a match on the character directly
  1098. // after the asterisk.
  1099. //
  1100. switch (*(pszPattern + 1)) {
  1101. case L'*':
  1102. //
  1103. // Asterisks exist side by side. Advance the pattern pointer
  1104. // and go through loop again.
  1105. //
  1106. pszPattern++;
  1107. continue;
  1108. case L'\0':
  1109. //
  1110. // Asterisk exists at the end of the pattern string. Any
  1111. // remaining part of pszTestString matches so we can
  1112. // immediately return TRUE.
  1113. //
  1114. return TRUE;
  1115. case L'?':
  1116. //
  1117. // Match on any character. If the remaining parts of
  1118. // pszPattern and pszTestString match, then the entire
  1119. // string matches. Otherwise, keep advancing the
  1120. // pszTestString pointer.
  1121. //
  1122. if (PatternMatchW(pszPattern + 1, pszTestString)) {
  1123. return TRUE;
  1124. }
  1125. break;
  1126. case L'\\':
  1127. //
  1128. // Backslash indicates to take the next character
  1129. // verbatim. Advance the pointer before making a
  1130. // comparison.
  1131. //
  1132. pszPattern++;
  1133. break;
  1134. }
  1135. if (towupper(*(pszPattern + 1)) == towupper(*pszTestString)) {
  1136. //
  1137. // Characters match. If the remaining parts of
  1138. // pszPattern and pszTestString match, then the entire
  1139. // string matches. Otherwise, keep advancing the
  1140. // pszTestString pointer.
  1141. //
  1142. if (PatternMatchW(pszPattern + 1, pszTestString)) {
  1143. return TRUE;
  1144. }
  1145. }
  1146. }
  1147. //
  1148. // No more pszTestString left. Must not be a match.
  1149. //
  1150. if (!*pszTestString) {
  1151. return FALSE;
  1152. }
  1153. pszTestString++;
  1154. }
  1155. return FALSE;
  1156. }
  1157. /*++
  1158. Function Description:
  1159. Determine if the current process is a SafeDisc process. We do this by
  1160. simply by testing if both an .EXE and .ICD extension exist for the
  1161. process name.
  1162. Arguments:
  1163. None.
  1164. Return Value:
  1165. TRUE if Safedisc 1.x is detected.
  1166. History:
  1167. 01/23/2001 linstev Created
  1168. --*/
  1169. BOOL
  1170. bIsSafeDisc1()
  1171. {
  1172. BOOL bRet = FALSE;
  1173. CSTRING_TRY
  1174. {
  1175. CString csFileName;
  1176. csFileName.GetModuleFileNameW(NULL);
  1177. if (csFileName.EndsWithNoCase(L".exe") == 0)
  1178. {
  1179. // Current file is .EXE, check for corresponding .ICD
  1180. csFileName.Truncate(csFileName.GetLength() - 4);
  1181. csFileName += L".icd";
  1182. bRet = GetFileAttributesW(csFileName) != 0xFFFFFFFF;
  1183. }
  1184. if (bRet) {
  1185. DPF("ShimLib", eDbgLevelInfo, "SafeDisc detected: %S", csFileName.Get());
  1186. }
  1187. }
  1188. CSTRING_CATCH
  1189. {
  1190. // Do nothing
  1191. }
  1192. return bRet;
  1193. }
  1194. /*++
  1195. Function Description:
  1196. Determine if the current process is a SafeDisc process. We do this running the
  1197. image header and looking for a particular signature.
  1198. Arguments:
  1199. None.
  1200. Return Value:
  1201. TRUE if Safedisc 2 is detected.
  1202. History:
  1203. 07/28/2001 linstev Created
  1204. --*/
  1205. BOOL
  1206. bIsSafeDisc2()
  1207. {
  1208. PPEB Peb = NtCurrentPeb();
  1209. PLIST_ENTRY LdrHead;
  1210. PLIST_ENTRY LdrNext;
  1211. DWORD dwCnt = 0;
  1212. //
  1213. // Use the try-except in case the module list changes while we're looking at it
  1214. //
  1215. __try {
  1216. //
  1217. // Loop through the loaded modules. We use a count to make sure we
  1218. // aren't looping infinitely
  1219. //
  1220. LdrHead = &Peb->Ldr->InMemoryOrderModuleList;
  1221. LdrNext = LdrHead->Flink;
  1222. while ((LdrNext != LdrHead) && (dwCnt < 256)) {
  1223. PLDR_DATA_TABLE_ENTRY LdrEntry;
  1224. LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
  1225. if ((SSIZE_T)LdrEntry->DllBase > 0) {
  1226. //
  1227. // A user mode dll, now check for temp name
  1228. //
  1229. WCHAR *wzName = LdrEntry->BaseDllName.Buffer;
  1230. DWORD dwLen;
  1231. if (wzName && (dwLen = wcslen(wzName)) && (dwLen > 4) && (_wcsicmp(wzName + dwLen - 4, L".tmp") == 0)) {
  1232. //
  1233. // Name ends in .tmp, so detect SafeDisc
  1234. //
  1235. DWORD_PTR hMod = (DWORD_PTR) LdrEntry->DllBase;
  1236. PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER) hMod;
  1237. PIMAGE_NT_HEADERS pINTH = (PIMAGE_NT_HEADERS)(hMod + pIDH->e_lfanew);
  1238. PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY) (hMod + pINTH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
  1239. LPSTR pName = (LPSTR)(hMod + pExport->Name);
  1240. if (_stricmp(pName, "SecServ.dll") == 0) {
  1241. //
  1242. // Export name says this is SafeDisc
  1243. //
  1244. DPF("ShimLib", eDbgLevelInfo, "SafeDisc 2 detected");
  1245. return TRUE;
  1246. }
  1247. }
  1248. }
  1249. dwCnt++;
  1250. LdrNext = LdrEntry->InMemoryOrderLinks.Flink;
  1251. }
  1252. } __except(EXCEPTION_EXECUTE_HANDLER) {
  1253. DPF("ShimLib", eDbgLevelError, "Exception encounterd while detecting SafeDisc 2");
  1254. }
  1255. return FALSE;
  1256. }
  1257. /*++
  1258. Function Description:
  1259. Determine if the current process is NTVDM.
  1260. Arguments:
  1261. None.
  1262. Return Value:
  1263. TRUE if NTVDM is detected.
  1264. History:
  1265. 01/14/2002 clupu Created
  1266. --*/
  1267. BOOL
  1268. IsNTVDM(
  1269. void
  1270. )
  1271. {
  1272. PLDR_DATA_TABLE_ENTRY Entry;
  1273. PLIST_ENTRY Head;
  1274. PPEB Peb = NtCurrentPeb();
  1275. Head = &Peb->Ldr->InLoadOrderModuleList;
  1276. Head = Head->Flink;
  1277. Entry = CONTAINING_RECORD(Head, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
  1278. if (_wcsicmp(Entry->FullDllName.Buffer, L"ntvdm.exe") == 0) {
  1279. return TRUE;
  1280. }
  1281. return FALSE;
  1282. }
  1283. }; // end of namespace ShimLib