View difference between Paste ID: uBvdWEwp and C3qcNMbh
SHOW: | | - or go back to the newest paste.
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