Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //
- // dumpster_ordinals.cpp
- // https://pastebin.com/C3qcNMbh
- //
- // See Homies Twee t relating to cli obfus in Windows
- /*
- https://x.com/sixtyvividtails/status/1795410561775095816
- https://x.com/sixtyvividtails/status/1797667842185372084
- https://x.com/sixtyvividtails/status/1825951974153662711
- https://x.com/malmoeb/status/1825888362265182578
- */
- // Compile via msvc:
- // cl /O2 /GS- /Brepro dumpster_ordinals.cpp /link /dll /nocoffgrpinfo /noentry /out:dumpster_ordinals.dll
- //
- // Self-test via rundll32:
- // rundll32.exe %__CD__%\dumpster_ordinals.dll,test
- //
- #define _NO_CRT_STDIO_INLINE
- #include <stdio.h>
- #include <Windows.h>
- #pragma comment(lib, "ntdllp") // you might need wdk for this
- #pragma comment(lib, "user32")
- #pragma comment(lib, "kernel32")
- #pragma execution_character_set("utf-8")
- // round 1
- #pragma comment(linker, "/export:A=advapi32.dll.#1212,@1") // with dll extension (and we enforce our ordinal)
- #pragma comment(linker, "/export:B=advapi32.#+4294968508!") // overflows uint32 into 1212
- // "advapi32.#▲ ♫1212*1337", linker directive quoted due to " "; RtlCharToInteger skips leading chars with ords <= 0x20
- #pragma comment(linker, "\"/export:C=advapi32.#\x1E \x0E" "1212*1337\"")
- #pragma comment(linker, "/export:D=advapi32.#-0x44FFFFFB44") // overflows and then gets negated
- // round 2
- #pragma comment(linker, "/export:E=advapi32.dll::$DATA.#1212") // use ads streams (here unnamed default)
- #pragma comment(linker, "/export:F=C:\\windows\\system32\\advapi32.#1212") // full path
- #pragma comment(linker, "/export:G=\\windows\\system32\\advapi32.#1212") // ok only when current drive is C:
- #pragma comment(linker, "/export:H=C:advapi32.#1212") // 👎 doesn't work even if cwd is %system32%
- // round 2 with editing own export (only coz msvc linker doesn't allow to build such file statically)
- #pragma comment(linker, "/export:J=dumpster_ordinals.#") // 👎 means ordinal is 0; but 0 ordinal doesn't work
- #pragma comment(linker, "/export:K=dumpster_ordinals.#0x13370001") // not an overflow(!): ok when ordinalBase 0x13370001
- // for rundll32.exe test
- #pragma comment(linker, "/export:test")
- #pragma comment(linker, "/alternatename:test=_test@16") // needed for x32 only
- extern "C"
- IMAGE_DOS_HEADER __ImageBase; // linker-defined
- static void test_round2()
- {
- HMODULE ownImage = (HMODULE)&__ImageBase;
- char funcName[2]{"E"};
- void* funcs[6];
- for (int i = 0; i < _countof(funcs); ++i, ++funcName[0])
- funcs[i] = GetProcAddress(ownImage, funcName);
- // prepare to edit our own export table, to change ordinal base first to 0, and then to 0x13370001;
- // it doesn't have to be edited dynamically, it's just msvc linker doesn't allow us to build it statically
- auto* peHeader = (const IMAGE_NT_HEADERS*)(PBYTE(ownImage) + __ImageBase.e_lfanew);
- UINT exportRva = peHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
- auto* exports = (IMAGE_EXPORT_DIRECTORY*)(PBYTE(ownImage) + exportRva);
- ULONG dummyOldProtect;
- VirtualProtect(exports, sizeof(*exports), PAGE_READWRITE, &dummyOldProtect);
- UINT origOrdinalBase = exports->Base;
- void* funcsX[2];
- exports->Base = 0; // 'J' still won't resolve, but only because LdrpGetProcedureAddress bans ordinal 0
- funcsX[0] = GetProcAddress(ownImage, "J");
- exports->Base = 0x13370001; // 'K' should resolve now
- funcsX[1] = GetProcAddress(ownImage, "K");
- // note if module has OrdinalBase larger than 0xFFFF in its export table, you won't be able to resolve any of its
- // funcs by ordinals via GetProcAddress; however, you can use native ntdll!LdrGetProcedureAddressForCaller for that
- exports->Base = origOrdinalBase;
- wchar_t buf[0x740];
- swprintf_s(buf, _countof(buf),
- LR"(Secret round2 discovered!
- %p "E" > advapi32.dll::$DATA.#1212
- %p "F"
- > C:\windows\system32\advapi32.#1212
- %p "G"
- > \windows\system32\advapi32.#1212
- should work iff "C:" is current drive
- %p "H" > C:advapi32.#1212
- does NOT work even if %%system32%% is cwd
- %p "J" > dumpster_ordinals.#
- %p "J" > dumpster_ordinals.#
- ^ OrdinalBase is 0 - still does NOT work
- %p "K" > dumpster_ordinals.#0x13370001
- %p "K" > dumpster_ordinals.#0x13370001
- ^ OrdinalBase is 0x13370001 - should work okay)",
- funcs[0], funcs[1], funcs[2], funcs[3],
- funcs[4], funcsX[0],
- funcs[5], funcsX[1]);
- MessageBoxW({}, buf, L"dumpster ordinals", MB_OK|MB_ICONINFORMATION|MB_SETFOREGROUND);
- }
- extern "C"
- void __stdcall test(HWND, HINSTANCE, LPSTR, int)
- {
- void* funcs[4];
- char funcName[2]{"A"};
- unsigned exportsResolved = 0;
- for (int i = 0; i < _countof(funcs); exportsResolved += !!funcs[i], ++i, ++funcName[0])
- funcs[i] = GetProcAddress((HMODULE)&__ImageBase, funcName);
- // may only retrieve image base like that once we've resolved forwarded exports
- HMODULE advapi = GetModuleHandleA("advapi32");
- void* testFuncs[3]{};
- if (advapi)
- {
- testFuncs[0] = GetProcAddress(advapi, "CryptGenRandom");
- testFuncs[1] = GetProcAddress(advapi, (LPCSTR)1212);
- testFuncs[2] = GetProcAddress(advapi, (LPCSTR)1213);
- }
- wchar_t buf[0x400];
- swprintf_s(buf, _countof(buf),
- LR"(%p advapi32.dll
- %p dumpster_ordinals.dll
- ------------------------------------------------
- Sanity test; exports resolved via
- GetProcAddress(advapi32):
- %p "CryptGenRandom"
- %p #1212
- %p #1213
- ------------------------------------------------
- ------------------------------------------------
- Actual test; exports resolved via
- GetProcAddress(dumpster_ordinals):
- %p "A" > advapi32.dll.#1212
- %p "B" > advapi32.#+4294968508!
- %p "C" > advapi32.#▲ ♫1212*1337
- %p "D" > advapi32.#-0x44FFFFFB44
- ------------------------------------------------
- RESULT: %u/4 weird exports resolved.)",
- advapi, &__ImageBase,
- testFuncs[0], testFuncs[1], testFuncs[2],
- funcs[0], funcs[1], funcs[2], funcs[3], exportsResolved);
- // Note we cheat a bit with visual representation of variant C - we use higher Unicode chars. That doesn't affect
- // our export table, sequence "\x1E\x20\x0E" there still consists of just 3 bytes. !▲ ♫! << Unicode
- // This comment line contains actual lower ansi chars just to test your environment. ! ! << Ansi
- int rez = MessageBoxW({}, buf, L"dumpster ordinals",
- MB_CANCELTRYCONTINUE|MB_DEFBUTTON2|MB_ICONINFORMATION|MB_SETFOREGROUND);
- if (rez == IDTRYAGAIN)
- test_round2();
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement