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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. #include <cstdio>
  2. #include <ntdll.h>
  3. #include <cstdint>
  4. #include "structs.h"
  5. #include "wow64ext.h"
  6. #include "misc.h"
  7. #include "syscall64.h"
  8. #include "get_syscall64_ids.h"
  9. /**
  10. \file
  11. */
  12. /**
  13. \brief All functions doing direct syscalls are saved with a hash of their name
  14. and the accompying syscall ID in the table (API_TO_INDEX*) ID_table.
  15. To get the ID for a given hash the table can be searched.
  16. (To speed up the search the table is sorted and a binary search is used)
  17. */
  18. struct API_TO_INDEX
  19. {
  20. DWORD hash;
  21. DWORD id;
  22. } *ID_table = NULL;
  23. DWORD ID_table_count = 0; //!< Count of entries in ID_table
  24. /**
  25. \brief Parses the ID out of a x64 function.
  26. \return Returns the ID or INVALID_SYSCALL_ID on err
  27. */
  28. static DWORD get_syscall_id(LPBYTE function)
  29. {
  30. if(!function)
  31. return INVALID_SYSCALL_ID;
  32. /*
  33. 00000000`77b61800 4c8bd1 mov r10,rcx
  34. 00000000`77b61803 b852000000 mov eax,52h
  35. 00000000`77b61808 0f05 syscall
  36. On Windows 10 it's:
  37. 0:000> u NtOpenFile
  38. ntdll!NtOpenFile:
  39. 00007ffe`62bf6720 4c8bd1 mov r10,rcx
  40. 00007ffe`62bf6723 b833000000 mov eax,33h
  41. 00007ffe`62bf6728 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
  42. 00007ffe`62bf6730 7503 jne ntdll!NtOpenFile+0x15 (00007ffe`62bf6735)
  43. 00007ffe`62bf6732 0f05 syscall
  44. 00007ffe`62bf6734 c3 ret
  45. 00007ffe`62bf6735 cd2e int 2Eh
  46. 00007ffe`62bf6737 c3 ret
  47. */
  48. const unsigned char MOV_R10_RCX_OPCODE[] = {0x4c, 0x8b, 0xd1};
  49. if(memcmp(function, MOV_R10_RCX_OPCODE, sizeof(MOV_R10_RCX_OPCODE)))
  50. return INVALID_SYSCALL_ID;
  51. const unsigned char SYSCALL_OPCODE[] = {0x0f, 0x05};
  52. const size_t SYSCALL_OFFSET_OLD = 8,
  53. SYSCALL_OFFSET_10 = 0x12;
  54. uint8_t* old = function + SYSCALL_OFFSET_OLD;
  55. uint8_t* win10 = function + SYSCALL_OFFSET_10;
  56. if (memcmp(old, SYSCALL_OPCODE, sizeof(SYSCALL_OPCODE)) &&
  57. memcmp(win10, SYSCALL_OPCODE, sizeof(SYSCALL_OPCODE)))
  58. {
  59. return INVALID_SYSCALL_ID;
  60. }
  61. const unsigned char MOV_EAX_OPCODE = 0xB8;
  62. const size_t MOV_EAX_ID_OFFSET = 3;
  63. if(MOV_EAX_OPCODE != *(function + MOV_EAX_ID_OFFSET))
  64. return INVALID_SYSCALL_ID;
  65. return *(PDWORD)(function + MOV_EAX_ID_OFFSET + 1);
  66. }
  67. /**
  68. \brief Parses the given image to get the ID or just count them.
  69. \return Number of entries found / populated
  70. \param imageBase Image base of the DLL the info will be taken from
  71. \param ids The struct the info is put into. If NULL - won't be touched
  72. */
  73. static DWORD get_syscall_ids(LPVOID imageBase, API_TO_INDEX* ids)
  74. {
  75. if(!imageBase)
  76. return 0;
  77. PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)imageBase;
  78. // not a valid DOS header
  79. if(IMAGE_DOS_SIGNATURE != dos->e_magic)
  80. return 0;
  81. PIMAGE_NT_HEADERS64 nt = (PIMAGE_NT_HEADERS64)((LPBYTE)imageBase + dos->e_lfanew);
  82. // not a valid PE or not the correct architecture
  83. if(IMAGE_NT_SIGNATURE != nt->Signature || IMAGE_FILE_MACHINE_AMD64 != nt->FileHeader.Machine)
  84. {
  85. return 0;
  86. }
  87. // No exports?
  88. if(!nt->OptionalHeader.DataDirectory->Size)
  89. return 0;
  90. DWORD count = 0; //! Number of functions who do direct syscalls
  91. IMAGE_EXPORT_DIRECTORY *exportDir = (IMAGE_EXPORT_DIRECTORY *)((LPBYTE)imageBase + nt->OptionalHeader.DataDirectory->VirtualAddress);
  92. PDWORD nameRef = (DWORD *)((LPBYTE)imageBase + exportDir->AddressOfNames);
  93. WORD* ordinal = (WORD *)((LPBYTE)imageBase + exportDir->AddressOfNameOrdinals);
  94. DWORD* addressOfFunctions = (DWORD*)((LPBYTE)imageBase + exportDir->AddressOfFunctions);
  95. printf("Total functions: %d\n", exportDir->NumberOfNames);
  96. for(DWORD i = 0; i < exportDir->NumberOfNames; i++, nameRef++)
  97. {
  98. const char* name = (const char*)((LPBYTE)imageBase + (*nameRef));
  99. LPBYTE address = (LPBYTE)imageBase + addressOfFunctions[ordinal[i]];
  100. DWORD id = get_syscall_id(address);
  101. if(INVALID_SYSCALL_ID == id)
  102. continue;
  103. // Put it into the table
  104. if(ids)
  105. {
  106. ids[count].hash = hash(name);
  107. ids[count].id = id;
  108. }
  109. // Print info
  110. else
  111. {
  112. printf("%s (%X) = %X\n", name, hash(name), id);
  113. }
  114. count++;
  115. }
  116. return count;
  117. }
  118. /**
  119. \brief (Bubble)Sorts the ID_table so it can be searched via binary search
  120. */
  121. static void sort_ID_table()
  122. {
  123. DWORD n = ID_table_count;
  124. bool swapped = false;
  125. do
  126. {
  127. for(unsigned int i = 0; i < n - 1; ++i)
  128. {
  129. if(ID_table[i].hash > ID_table[i + 1].hash)
  130. {
  131. API_TO_INDEX tmp = {ID_table[i].hash, ID_table[i].id};
  132. ID_table[i].hash = ID_table[i + 1].hash;
  133. ID_table[i].id = ID_table[i + 1].id;
  134. ID_table[i + 1].hash = tmp.hash;
  135. ID_table[i + 1].id = tmp.id;
  136. swapped = true;
  137. }
  138. }
  139. n = n - 1;
  140. } while(swapped == true && n);
  141. }
  142. /**
  143. \brief Gets the path to the x64 ntdll in native format
  144. \param path Output buffer
  145. */
  146. static void get_ntdll_path(wchar_t* path)
  147. {
  148. wchar_t systemDirectory[MAX_PATH];
  149. if(!path)
  150. return;
  151. GetSystemDirectory(systemDirectory, _countof(systemDirectory));
  152. wsprintfW(path, L"\\??\\%s\\ntdll.dll", systemDirectory);
  153. }
  154. /**
  155. */
  156. static BOOL read_ntdll(LPBYTE& content, DWORD& size)
  157. {
  158. wchar_t ntdllPath[MAX_PATH]; //! Path in the native format
  159. get_ntdll_path(ntdllPath);
  160. printf("%ls\n", ntdllPath);
  161. _UNICODE_STRING_T<DWORD64> filename = {lstrlenW(ntdllPath) * sizeof(WCHAR), MAX_PATH * sizeof(WCHAR), (DWORD64)ntdllPath};
  162. _OBJECT_ATTRIBUTES_T<DWORD64> obja = {sizeof(_OBJECT_ATTRIBUTES_T<DWORD64>), NULL, (DWORD64)&filename, OBJ_CASE_INSENSITIVE, NULL, NULL};
  163. _IO_STATUS_BLOCK_T<DWORD64> iostatusblock = {0};
  164. _HANDLE_T<DWORD64> fileHandle = {(DWORD64)INVALID_HANDLE_VALUE};
  165. NTSTATUS stat = DO_SYSCALL(get_basic_syscall_ID(NTOPENFILE), &fileHandle,
  166. FILE_READ_DATA | SYNCHRONIZE, &obja, &iostatusblock, FILE_SHARE_READ,
  167. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
  168. if(STATUS_SUCCESS != stat)
  169. {
  170. printf("NTSTATUS: %X\tHandle: %llX\n", stat, fileHandle.h);
  171. return FALSE;
  172. }
  173. printf("Handle: %llX\n", fileHandle.h);
  174. if(!(size = GetFileSize((HANDLE)fileHandle.h, NULL)))
  175. {
  176. DO_SYSCALL(get_basic_syscall_ID(NTCLOSE), fileHandle.h);
  177. return FALSE;
  178. }
  179. printf("Size of ntdll: %dkb\n", size / 1024);
  180. /* As no code is actually executed in the new ntdll - but only
  181. a few bytes are read from it - rw is enough */
  182. if(!(content = (LPBYTE)VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE,
  183. PAGE_READWRITE)))
  184. {
  185. DO_SYSCALL(get_basic_syscall_ID(NTCLOSE), fileHandle.h);
  186. return FALSE;
  187. }
  188. printf("content %p\n", content);
  189. printf("ios ptr %X", &iostatusblock);
  190. LARGE_INTEGER offset = {0};
  191. stat = DO_SYSCALL(get_basic_syscall_ID(NTREADFILE),
  192. (HANDLE)fileHandle.h,
  193. NULL, // event
  194. NULL, // ApcRoutine
  195. NULL, // ApcContext
  196. &iostatusblock,
  197. content,
  198. size,
  199. &offset,
  200. NULL); // key
  201. if(STATUS_SUCCESS != stat)
  202. {
  203. VirtualFree(content, 0, MEM_RELEASE);
  204. content = NULL;
  205. DO_SYSCALL(get_basic_syscall_ID(NTCLOSE), fileHandle.h);
  206. printf("Reading failed: %X\t%X != %X\n", stat, size, offset.LowPart);
  207. return FALSE;
  208. }
  209. printf("ios: %X\toffset: %X\n", iostatusblock.Status, offset.LowPart);
  210. DO_SYSCALL(get_basic_syscall_ID(NTCLOSE), fileHandle.h);
  211. return TRUE;
  212. }
  213. /**
  214. \brief (Partly) maps the image.
  215. This does NOT fix relocations, imports, TLS, sets section protections yadda yadda.
  216. As this image is only used to parse out the IDs that is no problem.
  217. \param content Raw data of the file to be mapped
  218. \param mappedImage The finished image
  219. */
  220. static BOOL map_PE(LPBYTE content, LPBYTE* mappedImage)
  221. {
  222. PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)content;
  223. if(IMAGE_DOS_SIGNATURE != dos->e_magic)
  224. {
  225. printf("Not a valid DOS header: %s\n", content);
  226. return FALSE;
  227. }
  228. PIMAGE_NT_HEADERS64 nt = (PIMAGE_NT_HEADERS64)(content + dos->e_lfanew);
  229. if(IMAGE_NT_SIGNATURE != nt->Signature)
  230. {
  231. printf("Not a valid PE header\n");
  232. return FALSE;
  233. }
  234. /* Must be mapped properly (Actually almost -
  235. only the code section would be needed) so the IDs can be be
  236. parsed properly */
  237. if(!(*mappedImage = (LPBYTE)VirtualAlloc(NULL, nt->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE,
  238. PAGE_READWRITE)))
  239. {
  240. printf("Can't alloc\n");
  241. return FALSE;
  242. }
  243. printf("Let's map that shit @ %p. %X sections. %X imageSize\n", *mappedImage,
  244. nt->FileHeader.NumberOfSections, nt->OptionalHeader.SizeOfImage);
  245. // Copy up to the first section (Size of optional header + delta optional header to start of image)
  246. memcpy(*mappedImage, content, nt->FileHeader.SizeOfOptionalHeader + ((LPBYTE)&nt->OptionalHeader - (LPBYTE)content));
  247. // Copy sections
  248. #define IMAGE_FIRST_SECTION64( ntheader ) ((PIMAGE_SECTION_HEADER) \
  249. ((ULONG_PTR)(ntheader)+\
  250. FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + \
  251. ((ntheader))->FileHeader.SizeOfOptionalHeader \
  252. ))
  253. PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION64(nt);
  254. for(unsigned int i = 0; i < nt->FileHeader.NumberOfSections; i++)
  255. {
  256. printf("Copy %Xbytes @ RVA %X From %X\n", pish->SizeOfRawData, pish->VirtualAddress, pish->PointerToRawData);
  257. memcpy((*mappedImage + pish->VirtualAddress), (content + pish->PointerToRawData),
  258. pish->SizeOfRawData);
  259. pish++;
  260. }
  261. return TRUE;
  262. }
  263. /**
  264. \brief Initalize the ID_table.
  265. */
  266. BOOL initalize_ID_table()
  267. {
  268. LPBYTE ntdll = NULL, imageBase = NULL;
  269. DWORD ntdllSize = 0;
  270. if(!read_ntdll(ntdll, ntdllSize))
  271. return FALSE;
  272. if(!map_PE(ntdll, &imageBase))
  273. return FALSE;
  274. ID_table_count = get_syscall_ids(imageBase, NULL);
  275. printf("%i functions who do direct syscalls\n", ID_table_count);
  276. if(!ID_table_count)
  277. return FALSE;
  278. if(!(ID_table = (API_TO_INDEX*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ID_table_count * sizeof(API_TO_INDEX))))
  279. return FALSE;
  280. if(!get_syscall_ids(imageBase, ID_table))
  281. return FALSE;
  282. VirtualFree(ntdll, 0, MEM_RELEASE);
  283. VirtualFree(imageBase, 0, MEM_RELEASE);
  284. sort_ID_table();
  285. return TRUE;
  286. }
  287. /**
  288. \brief Has a few hardcoded IDs and returns the correct one for the current OS
  289. Which IDs exactly are hardcoded can be seen in the enum.
  290. Table made mostly with data from:
  291. x86: http://j00ru.vexillium.org/ntapi/
  292. WOW64: http://j00ru.vexillium.org/ntapi_64/
  293. \fixme >=Win8 Support?
  294. */
  295. DWORD get_basic_syscall_ID(SYSCALL_IDS func)
  296. {
  297. _KUSER_SHARED_DATA* _kuser_s_d = GET_KUSER_SHARED_DATA();
  298. ULONG majorVersion = _kuser_s_d->NtMajorVersion;
  299. ULONG minorVersion = _kuser_s_d->NtMinorVersion;
  300. NT_PRODUCT_TYPE productType = _kuser_s_d->NtProductType;
  301. _PEB* p = (_PEB*)__readfsdword(0x30);
  302. ULONG buildID = p->NtBuildNumber;
  303. switch(majorVersion)
  304. {
  305. case 5:
  306. // XP
  307. if(1 == minorVersion ||
  308. (2 == minorVersion && VER_NT_WORKSTATION == productType))
  309. {
  310. if(NTOPENFILE == func)
  311. return 0x30;
  312. else if(NTCREATEFILE == func)
  313. return 0x52;
  314. else if(NTREADFILE == func)
  315. return 0x03;
  316. else if(NTCLOSE == func)
  317. return 0x0C;
  318. }
  319. // Server 2003
  320. else
  321. {
  322. printf("SRV03 unsupported\n"); // fixme
  323. if(NTOPENFILE == func)
  324. return 0x7a;
  325. else if(NTCREATEFILE == func)
  326. return 0x26;
  327. else if(NTREADFILE == func)
  328. return 0xbf;
  329. else if(NTCLOSE == func)
  330. return 0x1b;
  331. }
  332. break;
  333. case 6:
  334. switch(minorVersion)
  335. {
  336. // Vista SP0-2 & Server 2008
  337. case 0:
  338. printf("Vista unsupported\n"); // fixme
  339. if(NTOPENFILE == func)
  340. return 0xba;
  341. else if(NTCREATEFILE == func)
  342. return 0x3c;
  343. else if(NTREADFILE == func)
  344. return 0x102;
  345. else if(NTCLOSE == func)
  346. return 0x30;
  347. break;
  348. // Win7
  349. case 1:
  350. if(NTOPENFILE == func)
  351. return 0x30;
  352. else if(NTCREATEFILE == func)
  353. return 0x52;
  354. else if(NTREADFILE == func)
  355. return 0x03;
  356. else if(NTCLOSE == func)
  357. return 0x0C;
  358. // Win 8
  359. case 2:
  360. printf("Win8 unsupported\n"); // fixme
  361. if(NTOPENFILE == func)
  362. return 0xe7;
  363. else if(NTCREATEFILE == func)
  364. return 0x15f;
  365. else if(NTREADFILE == func)
  366. return 0x86;
  367. else if(NTCLOSE == func)
  368. return 0x0171;
  369. break;
  370. }
  371. break;
  372. // Win 10
  373. case 10:
  374. if (NTOPENFILE == func)
  375. return 0x0033;
  376. else if (NTCREATEFILE == func)
  377. return 0x55;
  378. else if (NTREADFILE == func)
  379. return 0x06;
  380. else if (NTCLOSE == func)
  381. return 0x0f;
  382. }
  383. return INVALID_SYSCALL_ID;
  384. }
  385. DWORD get_syscall_ID(DWORD func)
  386. {
  387. DWORD left = 0, right = ID_table_count;
  388. while(left != right)
  389. {
  390. DWORD middle = (left + right) / 2;
  391. if(func == ID_table[middle].hash)
  392. return ID_table[middle].id;
  393. if(func > ID_table[middle].hash)
  394. left = middle + 1;
  395. else
  396. right = middle - 1;
  397. }
  398. if(func == ID_table[left].hash)
  399. {
  400. return ID_table[left].id;
  401. }
  402. return INVALID_SYSCALL_ID;
  403. }
  404. VOID destroy_ID_table()
  405. {
  406. HeapFree(GetProcessHeap(), 0, ID_table);
  407. }
  408. #if 0
  409. BOOL test_id_table()
  410. {
  411. LPVOID imageBase = GetModuleHandle(TEXT("ntdll.dll"));
  412. PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)imageBase;
  413. // not a valid DOS header
  414. if(IMAGE_DOS_SIGNATURE != dos->e_magic)
  415. return FALSE;
  416. PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((LPBYTE)imageBase + dos->e_lfanew);
  417. // not a valid PE or not the correct architecture
  418. if(IMAGE_NT_SIGNATURE != nt->Signature || IMAGE_FILE_MACHINE_I386 != nt->FileHeader.Machine)
  419. {
  420. return FALSE;
  421. }
  422. // No exports?
  423. if(!nt->OptionalHeader.DataDirectory->Size)
  424. return FALSE;
  425. IMAGE_EXPORT_DIRECTORY *exportDir = (IMAGE_EXPORT_DIRECTORY *)((LPBYTE)imageBase + nt->OptionalHeader.DataDirectory->VirtualAddress);
  426. PDWORD nameRef = (DWORD *)((LPBYTE)imageBase + exportDir->AddressOfNames);
  427. WORD* ordinal = (WORD *)((LPBYTE)imageBase + exportDir->AddressOfNameOrdinals);
  428. DWORD* addressOfFunctions = (DWORD*)((LPBYTE)imageBase + exportDir->AddressOfFunctions);
  429. for(DWORD i = 0; i < exportDir->NumberOfNames; i++, nameRef++)
  430. {
  431. const char* name = (const char*)((LPBYTE)imageBase + (*nameRef));
  432. LPBYTE address = (LPBYTE)imageBase + addressOfFunctions[ordinal[i]];
  433. DWORD id = get_syscall_id(address);
  434. if(INVALID_SYSCALL_ID == id)
  435. continue;
  436. // At address is a function doing direct syscalls - check if can be found
  437. if(INVALID_SYSCALL_ID == get_syscall_ID(hash(name)))
  438. {
  439. printf("%s not found", name);
  440. return FALSE;
  441. }
  442. }
  443. return TRUE;
  444. }
  445. #endif