Advertisement
cakemaker

dumpster_ordinals.dll

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