Source code of Windows XP (NT5)
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.

1768 lines
36 KiB

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