Advertisement
savsanta

dumpster_ordinals.dll

Aug 21st, 2024
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. //
  2. // dumpster_ordinals.cpp
  3. // https://pastebin.com/C3qcNMbh
  4. //
  5. // See Homies Twee t relating to cli obfus in Windows
  6. /*
  7. https://x.com/sixtyvividtails/status/1795410561775095816
  8. https://x.com/sixtyvividtails/status/1797667842185372084
  9. https://x.com/sixtyvividtails/status/1825951974153662711
  10. https://x.com/malmoeb/status/1825888362265182578
  11.  
  12. */
  13. // Compile via msvc:
  14. // cl /O2 /GS- /Brepro dumpster_ordinals.cpp /link /dll /nocoffgrpinfo /noentry /out:dumpster_ordinals.dll
  15. //
  16. // Self-test via rundll32:
  17. // rundll32.exe %__CD__%\dumpster_ordinals.dll,test
  18. //
  19.  
  20. #define _NO_CRT_STDIO_INLINE
  21.  
  22. #include <stdio.h>
  23. #include <Windows.h>
  24.  
  25.  
  26. #pragma comment(lib, "ntdllp")      // you might need wdk for this
  27. #pragma comment(lib, "user32")
  28. #pragma comment(lib, "kernel32")
  29.  
  30. #pragma execution_character_set("utf-8")
  31.  
  32.  
  33. // round 1
  34. #pragma comment(linker, "/export:A=advapi32.dll.#1212,@1")      // with dll extension (and we enforce our ordinal)
  35. #pragma comment(linker, "/export:B=advapi32.#+4294968508!")     // overflows uint32 into 1212
  36. // "advapi32.#▲ ♫1212*1337", linker directive quoted due to " "; RtlCharToInteger skips leading chars with ords <= 0x20
  37. #pragma comment(linker, "\"/export:C=advapi32.#\x1E \x0E" "1212*1337\"")
  38. #pragma comment(linker, "/export:D=advapi32.#-0x44FFFFFB44")    // overflows and then gets negated
  39.  
  40. // round 2
  41. #pragma comment(linker, "/export:E=advapi32.dll::$DATA.#1212")              // use ads streams (here unnamed default)
  42. #pragma comment(linker, "/export:F=C:\\windows\\system32\\advapi32.#1212")  // full path
  43. #pragma comment(linker, "/export:G=\\windows\\system32\\advapi32.#1212")    // ok only when current drive is C:
  44. #pragma comment(linker, "/export:H=C:advapi32.#1212")                       // 👎 doesn't work even if cwd is %system32%
  45.  
  46. // round 2 with editing own export (only coz msvc linker doesn't allow to build such file statically)
  47. #pragma comment(linker, "/export:J=dumpster_ordinals.#")           // 👎 means ordinal is 0; but 0 ordinal doesn't work
  48. #pragma comment(linker, "/export:K=dumpster_ordinals.#0x13370001") // not an overflow(!): ok when ordinalBase 0x13370001
  49.  
  50. // for rundll32.exe test
  51. #pragma comment(linker, "/export:test")
  52. #pragma comment(linker, "/alternatename:test=_test@16") // needed for x32 only
  53.  
  54.  
  55. extern "C"
  56. IMAGE_DOS_HEADER __ImageBase;    // linker-defined
  57.  
  58.  
  59. static void test_round2()
  60. {
  61.     HMODULE ownImage = (HMODULE)&__ImageBase;
  62.  
  63.     char funcName[2]{"E"};
  64.     void* funcs[6];
  65.     for (int i = 0; i < _countof(funcs); ++i, ++funcName[0])
  66.         funcs[i] = GetProcAddress(ownImage, funcName);
  67.  
  68.     // prepare to edit our own export table, to change ordinal base first to 0, and then to 0x13370001;
  69.     // it doesn't have to be edited dynamically, it's just msvc linker doesn't allow us to build it statically
  70.     auto* peHeader = (const IMAGE_NT_HEADERS*)(PBYTE(ownImage) + __ImageBase.e_lfanew);
  71.     UINT exportRva = peHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
  72.     auto* exports = (IMAGE_EXPORT_DIRECTORY*)(PBYTE(ownImage) + exportRva);
  73.     ULONG dummyOldProtect;
  74.     VirtualProtect(exports, sizeof(*exports), PAGE_READWRITE, &dummyOldProtect);
  75.     UINT origOrdinalBase = exports->Base;
  76.  
  77.     void* funcsX[2];
  78.     exports->Base = 0;          // 'J' still won't resolve, but only because LdrpGetProcedureAddress bans ordinal 0
  79.     funcsX[0] = GetProcAddress(ownImage, "J");
  80.     exports->Base = 0x13370001; // 'K' should resolve now
  81.     funcsX[1] = GetProcAddress(ownImage, "K");
  82.     // note if module has OrdinalBase larger than 0xFFFF in its export table, you won't be able to resolve any of its
  83.     // funcs by ordinals via GetProcAddress; however, you can use native ntdll!LdrGetProcedureAddressForCaller for that
  84.     exports->Base = origOrdinalBase;
  85.  
  86.     wchar_t buf[0x740];
  87.     swprintf_s(buf, _countof(buf),
  88. LR"(Secret round2 discovered!
  89.  
  90.  
  91. %p  "E" > advapi32.dll::$DATA.#1212
  92. %p  "F"
  93.     > C:\windows\system32\advapi32.#1212
  94.  
  95. %p  "G"
  96.     > \windows\system32\advapi32.#1212
  97. should work iff "C:" is current drive
  98.  
  99. %p  "H" > C:advapi32.#1212
  100. does NOT work even if %%system32%% is cwd
  101.  
  102. %p  "J" > dumpster_ordinals.#
  103. %p  "J" > dumpster_ordinals.#
  104. ^ OrdinalBase is 0 - still does NOT work
  105.  
  106. %p  "K" > dumpster_ordinals.#0x13370001
  107. %p  "K" > dumpster_ordinals.#0x13370001
  108. ^ OrdinalBase is 0x13370001 - should work okay)",
  109.         funcs[0], funcs[1], funcs[2], funcs[3],
  110.         funcs[4], funcsX[0],
  111.         funcs[5], funcsX[1]);
  112.  
  113.     MessageBoxW({}, buf, L"dumpster ordinals", MB_OK|MB_ICONINFORMATION|MB_SETFOREGROUND);
  114. }
  115.  
  116.  
  117. extern "C"
  118. void __stdcall test(HWND, HINSTANCE, LPSTR, int)
  119. {
  120.     void* funcs[4];
  121.     char funcName[2]{"A"};
  122.     unsigned exportsResolved = 0;
  123.     for (int i = 0; i < _countof(funcs); exportsResolved += !!funcs[i], ++i, ++funcName[0])
  124.         funcs[i] = GetProcAddress((HMODULE)&__ImageBase, funcName);
  125.    
  126.     // may only retrieve image base like that once we've resolved forwarded exports
  127.     HMODULE advapi = GetModuleHandleA("advapi32");
  128.     void* testFuncs[3]{};
  129.     if (advapi)
  130.     {
  131.         testFuncs[0] = GetProcAddress(advapi, "CryptGenRandom");
  132.         testFuncs[1] = GetProcAddress(advapi, (LPCSTR)1212);
  133.         testFuncs[2] = GetProcAddress(advapi, (LPCSTR)1213);
  134.     }
  135.  
  136.     wchar_t buf[0x400];
  137.     swprintf_s(buf, _countof(buf),
  138. LR"(%p  advapi32.dll
  139. %p  dumpster_ordinals.dll
  140.  
  141. ------------------------------------------------
  142. Sanity test; exports resolved via
  143. GetProcAddress(advapi32):
  144.  
  145. %p  "CryptGenRandom"
  146. %p  #1212
  147. %p  #1213
  148. ------------------------------------------------
  149.  
  150.  
  151. ------------------------------------------------
  152. Actual test; exports resolved via
  153. GetProcAddress(dumpster_ordinals):
  154.  
  155. %p  "A" > advapi32.dll.#1212
  156. %p  "B" > advapi32.#+4294968508!
  157. %p  "C" > advapi32.#▲ ♫1212*1337
  158. %p  "D" > advapi32.#-0x44FFFFFB44
  159. ------------------------------------------------
  160.  
  161.  
  162. RESULT: %u/4 weird exports resolved.)",
  163.         advapi, &__ImageBase,
  164.         testFuncs[0], testFuncs[1], testFuncs[2],
  165.         funcs[0], funcs[1], funcs[2], funcs[3], exportsResolved);
  166.  
  167.     // Note we cheat a bit with visual representation of variant C - we use higher Unicode chars. That doesn't affect
  168.     // our export table, sequence "\x1E\x20\x0E" there still consists of just 3 bytes.         !▲ ♫!  << Unicode
  169.     // This comment line contains actual lower ansi chars just to test your environment.       ! !  << Ansi
  170.     int rez = MessageBoxW({}, buf, L"dumpster ordinals",
  171.         MB_CANCELTRYCONTINUE|MB_DEFBUTTON2|MB_ICONINFORMATION|MB_SETFOREGROUND);
  172.     if (rez == IDTRYAGAIN)
  173.         test_round2();
  174. }
  175.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement