ihatefeds.com · 2020-04-21 · 005.8--dc23 2015036294 no starch press and the no starch press logo...

Post on 14-Jul-2020

0 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

GAMEHACKINGDevelopingAutonomousBotsforOnlineGames

NickCano

SanFrancisco

GAMEHACKING.Copyright©2016byNickCano.

Allrightsreserved.Nopartofthisworkmaybereproducedortransmittedinanyformorbyanymeans,electronicormechanical,includingphotocopying,recording,orbyanyinformationstorageorretrievalsystem,withoutthepriorwrittenpermissionofthecopyrightownerandthepublisher.

PrintedinUSA

Firstprinting

2019181716123456789

ISBN-10:1-59327-669-9ISBN-13:978-1-59327-669-0

Publisher:WilliamPollockProductionEditor:LaurelChunCoverIllustration:RyanMilnerInteriorDesign:OctopodStudiosDevelopmentalEditor:JenniferGriffith-DelgadoTechnicalReviewer:StephenLawlerCopyeditor:RachelMonaghanCompositor:LaurelChunProofreader:PaulaL.FlemingIndexer:BIMCreatives,LLC

Forinformationondistribution,translations,orbulksales,pleasecontactNoStarchPress,Inc.directly:NoStarchPress,Inc.2458thStreet,SanFrancisco,CA94103phone:415.863.9900;info@nostarch.comwww.nostarch.com

LibraryofCongressCataloging-in-PublicationData

Cano,Nick,author.Gamehacking:developingautonomousbotsforonlinegames/byNickCano.pagescmIncludesindex.Summary:"Ahands-onguidetohackingcomputergames.Showsprogrammershowtodissectcomputergamesandcreatebotstoaltertheirgamingenvironment.Coversthebasicsofgamehacking,includingreverseengineering,assemblycodeanalysis,programmaticmemorymanipulation,persistenthacks,responsivehacks,andcodeinjection."--Providedbypublisher.ISBN978-1-59327-669-0--ISBN1-59327-669-91.Intelligentagents(Computersoftware)2.Internetprogramming.3.Internetgames--Programming.4.Hacking.I.Title.QA76.76.I58C362016

005.8--dc23

2015036294

NoStarchPressandtheNoStarchPresslogoareregisteredtrademarksofNoStarchPress,Inc.Otherproductandcompanynamesmentionedhereinmaybethetrademarksoftheirrespectiveowners.Ratherthanuseatrademarksymbolwitheveryoccurrenceofatrademarkedname,weareusingthenamesonlyinaneditorialfashionandtothebenefitofthetrademarkowner,withnointentionofinfringementofthetrademark.

Theinformationinthisbookisdistributedonan“AsIs”basis,withoutwarranty.Whileeveryprecautionhasbeentakeninthepreparationofthiswork,neithertheauthornorNoStarchPress,Inc.shallhaveanyliabilitytoanypersonorentitywithrespecttoanylossordamagecausedorallegedtobecauseddirectlyorindirectlybytheinformationcontainedinit.

AbouttheAuthorNickCanowrotehisfirstscriptsforopensourcegameserverswhenhewas12andstartedabusinesssellinghisbotswhenhewas16.Hehasbeenapartofthegame-hackingcommunityeversinceandadvisesgamedevelopersanddesignersonbestpracticestoprotecttheirgamesagainstbots.Nickalsohasyearsofexperienceindetectinganddefendingagainstmalware,andhehasspokenatmanyconferencesabouthisresearchandtools.

AbouttheTechnicalReviewerStephenLawleristhefounderandpresidentofasmallcomputersoftwareandsecurityconsultingfirm.Hehasbeenactivelyworkingininformationsecurityforover10years,primarilyinreverseengineering,malwareanalysis,andvulnerabilityresearch.HewasamemberoftheMandiantmalwareanalysisteamandassistedwithhigh-profilecomputerintrusionsaffectingseveralFortune100companies.StephenalsodevelopedandteachesthePracticalARMExploitationclass,whichhasbeenofferedatBlackHatandseveralothersecurityconferencesforthepastfiveyears.

BRIEFCONTENTS

ForewordbyDr.JaredDeMott

Acknowledgments

Introduction

PART1:TOOLSOFTHETRADEChapter1:ScanningMemoryUsingCheatEngine

Chapter2:DebuggingGameswithOllyDbg

Chapter3:ReconnaissancewithProcessMonitorandProcessExplorer

PART2:GAMEDISSECTIONChapter4:FromCodetoMemory:AGeneralPrimer

Chapter5:AdvancedMemoryForensics

Chapter6:ReadingfromandWritingtoGameMemory

PART3:PROCESSPUPPETEERINGChapter7:CodeInjection

Chapter8:ManipulatingControlFlowinaGame

PART4:CREATINGBOTSChapter9:UsingExtrasensoryPerceptiontoWardOffFogofWar

Chapter10:ResponsiveHacks

Chapter11:PuttingItAllTogether:WritingAutonomousBots

Chapter12:StayingHidden

Index

CONTENTSINDETAIL

FOREWORDbyDr.JaredDeMott

ACKNOWLEDGMENTS

INTRODUCTIONPrerequisitesfortheReaderABriefGameHackingHistoryWhyHackGames?HowThisBookIsOrganizedAbouttheOnlineResourcesHowtoUseThisBook

PART1TOOLSOFTHETRADE

1SCANNINGMEMORYUSINGCHEATENGINEWhyMemoryScannersAreImportantBasicMemoryScanningCheatEngine’sMemoryScanner

ScanTypesRunningYourFirstScanNextScansWhenYouCan’tGetaSingleResultCheatTables

MemoryModificationinGamesManualModificationwithCheatEngine

TrainerGeneratorPointerScanning

PointerChainsPointerScanningBasicsPointerScanningwithCheatEnginePointerRescanning

LuaScriptingEnvironmentSearchingforAssemblyPatternsSearchingforStrings

ClosingThoughts

2DEBUGGINGGAMESWITHOLLYDBGABriefLookatOllyDbg’sUserInterfaceOllyDbg’sCPUWindow

ViewingandNavigatingaGame’sAssemblyCodeViewingandEditingRegisterContentsViewingandSearchingaGame’sMemoryViewingaGame’sCallStack

CreatingCodePatchesTracingThroughAssemblyCodeOllyDbg’sExpressionEngine

UsingExpressionsinBreakpointsUsingOperatorsintheExpressionEngineWorkingwithBasicExpressionElementsAccessingMemoryContentswithExpressions

OllyDbgExpressionsinActionPausingExecutionWhenaSpecificPlayer’sNameIsPrintedPausingExecutionWhenYourCharacter’sHealthDrops

OllyDbgPlug-insforGameHackersCopyingAssemblyCodewithAsm2ClipboardAddingCheatEnginetoOllyDbgwithCheatUtilityControllingOllyDbgThroughtheCommandLineVisualizingControlFlowwithOllyFlow

ClosingThoughts

3RECONNAISSANCEWITHPROCESSMONITORANDPROCESSEXPLORERProcessMonitor

LoggingIn-GameEventsInspectingEventsintheProcessMonitorLogDebuggingaGametoCollectMoreData

ProcessExplorerProcessExplorer’sUserInterfaceandControlsExaminingProcessPropertiesHandleManipulationOptions

ClosingThoughts

PART2GAMEDISSECTION

4FROMCODETOMEMORY:AGENERALPRIMERHowVariablesandOtherDataManifestinMemory

NumericDataStringDataDataStructuresUnionsClassesandVFTables

x86AssemblyCrashCourseCommandSyntaxProcessorRegistersTheCallStackImportantx86InstructionsforGameHacking

ClosingThoughts

5ADVANCEDMEMORYFORENSICSAdvancedMemoryScanning

DeducingPurposeFindingthePlayer’sHealthwithOllyDbgDeterminingNewAddressesAfterGameUpdates

IdentifyingComplexStructuresinGameDataThestd::stringClassThestd::vectorClassThestd::listClassThestd::mapClass

ClosingThoughts

6READINGFROMANDWRITINGTOGAMEMEMORYObtainingtheGame’sProcessIdentifier

ObtainingProcessHandlesWorkingwithOpenProcess()

AccessingMemoryWorkingwithReadProcessMemory()andWriteProcessMemory()AccessingaValueinMemorywithReadProcessMemory()andWriteProcessMemory()WritingTemplatedMemoryAccessFunctions

MemoryProtectionDifferentiatingx86WindowsMemoryProtectionAttributesChangingMemoryProtection

AddressSpaceLayoutRandomizationDisablingASLRtoSimplifyBotDevelopmentBypassingASLRinProduction

ClosingThoughts

PART3PROCESSPUPPETEERING

7CODEINJECTIONInjectingCodeCaveswithThreadInjection

CreatinganAssemblyCodeCaveTranslatingtheAssemblytoShellcodeWritingtheCodeCavetoMemoryUsingThreadInjectiontoExecutetheCodeCave

HijackingaGame’sMainThreadtoExecuteCodeCavesBuildingtheAssemblyCodeCaveGeneratingSkeletonShellcodeandAllocatingMemoryFindingandFreezingtheMainThread

InjectingDLLsforFullControlTrickingaProcessintoLoadingYourDLLAccessingMemoryinanInjectedDLLBypassingASLRinanInjectedDLL

ClosingThoughts

8MANIPULATINGCONTROLFLOWINAGAMENOPingtoRemoveUnwantedCode

WhentoNOPHowtoNOP

HookingtoRedirectGameExecutionCallHookingVFTableHookingIATHookingJumpHooking

ApplyingCallHookstoAdobeAIR

AccessingtheRTMPGoldmineHookingtheRTMPSencode()FunctionHookingtheRTMPSdecode()FunctionPlacingtheHooks

ApplyingJumpHooksandVFHookstoDirect3DTheDrawingLoopFindingtheDirect3DDeviceWritingaHookforEndScene()WritingaHookforReset()What’sNext?

ClosingThoughts

PART4CREATINGBOTS

9USINGEXTRASENSORYPERCEPTIONTOWARDOFFFOGOFWARBackgroundKnowledgeRevealingHiddenDetailswithLighthacks

AddingaCentralAmbientLightSourceIncreasingtheAbsoluteAmbientLightCreatingOtherTypesofLighthacks

RevealingSneakyEnemieswithWallhacksRenderingwithZ-BufferingCreatingaDirect3DWallhackFingerprintingtheModelYouWanttoReveal

GettingaWiderFieldofVisionwithZoomhacksUsingNOPingZoomhacksScratchingtheSurfaceofHookingZoomhacks

DisplayingHiddenDatawithHUDsCreatinganExperienceHUD

UsingHookstoLocateDataAnOverviewofOtherESPHacksClosingThoughts

10RESPONSIVEHACKSObservingGameEvents

MonitoringMemoryDetectingVisualCuesInterceptingNetworkTraffic

PerformingIn-GameActionsEmulatingtheKeyboardSendingPackets

TyingthePiecesTogetherMakingthePerfectHealerResistingEnemyCrowd-ControlAttacksAvoidingWastedMana

ClosingThoughts

11PUTTINGITALLTOGETHER:WRITINGAUTONOMOUSBOTSControlTheoryandGameHackingStateMachinesCombiningControlTheoryandStateMachines

ABasicHealerStateMachineAComplexHypotheticalStateMachineErrorCorrection

PathfindingwithSearchAlgorithmsTwoCommonSearchTechniquesHowObstaclesDisruptSearchesAnA*SearchAlgorithmWhenA*SearchesAreParticularlyUseful

CommonandCoolAutomatedHacksLootingwithCavebotsAutomatingCombatwithWarbots

ClosingThoughts

12STAYINGHIDDENProminentAnti-CheatSoftwareThePunkBusterToolkit

Signature-BasedDetectionScreenshotsHashValidation

TheESEAAnti-CheatToolkitTheVACToolkit

DNSCacheScansBinaryValidationFalsePositives

TheGameGuardToolkitUser-ModeRootkitKernel-ModeRootkit

TheWardenToolkitCarefullyManagingaBot’sFootprint

MinimizingaBot’sFootprintMaskingYourFootprintTeachingaBottoDetectDebuggersAnti-DebuggingTechniques

DefeatingSignature-BasedDetectionDefeatingScreenshotsDefeatingBinaryValidationDefeatinganAnti-CheatRootkitDefeatingHeuristicsClosingThoughts

INDEX

FOREWORD

Nickisgreat.Wefirsthititoffinalltherightandwrongways,asyoucanimagine.I’vebeeninthesecurityfieldawhile;he’salittleyounger.I’vehadtheschooling,whereashe’snotmuchforcollege.I’mafaithguy,andhe’snot.Theinterestingthingisthatnoneofthatmatters;we’vehadablastanyway.Age,race,gender,degrees—whenitcomestogaming,hacking,andcoding,noonecares!

Nickgetsitdone.He’sfun.He’sbrilliant.He’shardworking.Andprobablymostpertinent:he’soneoftherarefewwhounderstandtheintersectionofgaming,hacking,andcoding.He’sworkedinthisnicheandcreatedprofitablebots.

Inthisfirst-of-its-kindbook,Nickwalksyouthroughwhatitmeanstopullapartgames.Heteachesyouthesoftwareinvestigationtoolsandtricksofthetrade.You’lllearnaboutgameinternals,howtopullthemapart,andhowtomodifyplay.Forexample,Nickteacheshowtoavoidanti-cheatsothatyoucanautomateplay.Wouldn’titbecooltohaveyourownbotthatcollectsexperience,gold,items,andmore—allwhileyou’reaway?

Everwonderhowthecheaterscheat?Everwantedtopatchorprotectyourgame?Grabacoffee,crackopenyourlaptop,andenjoy.

Blessingstoyouandyours,

Dr.JaredDeMottSecurityExpert&SoftwareBuilder

ACKNOWLEDGMENTS

Writingthisbookwasanamazingjourney,andIcouldn’thavedoneitalone.NoStarchPresshasbeenextremelysupportiveandworkedcloselywithmetotakethisbookfromconcepttoreality.Inparticular,I’dliketothankmydevelopmentaleditor,JenniferGriffith-Delgado,andmyproductioneditor,LaurelChun.BillPollock,TylerOrtman,AlisonLaw,andtherestoftheteamatNoStarcharewonderfulpeople,andI’mpleasedtohaveworkedwiththem.

ThankstocopyeditorRachelMonaghan,proofreaderPaulaL.Fleming,andtechnicalreviewerStephenLawler.ThanksalsotomyfriendsCavitt“synt4x”GloverandVadimKotov,whotookthetimetoskimsomechaptersbeforesubmission,andtoJaredDeMottforwritingthebook’sforeword.

I’dliketothankallofthepeopleonTPForumswhotookmeinwhenIwasjustanaivekidandhelpedmelearnhowtohackgames.Inparticular,IowemythankstoJoseph“jo3bingham”Bingham,IanObermiller,andjeremic,whoallhadasignificantinfluenceonmyprogressionasahacker,andtoTPForumsfounderJosh“Zyphrus”Hartzell,whohelpedmefindmyconfidenceandskillswhenmyfuturelookeditsbleakest.

Thanksalsotomyentireforumstaffandeverycustomerwhohaseverusedmybots.Andfinally,thankstomyfamily,friends,andcolleagues,whohavebeenfunandsupportiveandhelpedshapemeintothemanIamtoday.

INTRODUCTION

Acommonmisconceptionintheworldofonlinegamingistheideathattheonlygameyoucanplayistheoneinthetitle.Infact,gamehackersenjoyplayingthegamethathidesbehindthecurtain:acat-and-mousegameofwitsbetweenthemandthegamedevelopers.Whilegamehackersworktoreverseengineergamebinaries,automateaspectsofgameplay,andmodifygamingenvironments,gamedeveloperscombatthehacker-designedtools(normallyreferredtoasbots)usinganti-reversingtechniques,botdetectionalgorithms,andheuristicdatamining.

Asthebattlebetweengamehackersanddevelopershasprogressed,thetechnicalmethodsimplementedbybothparties—manyofwhichresembletechniquesutilizedbymalwaredevelopersandantivirusvendors—haveevolved,becomingmorecomplex.Thisbookhighlightsthefightputupbygamehackers,andtheadvancedmethodstheyhaveengineeredtomanipulategameswhilesimultaneouslyeludinggamedevelopersinthedarkcornersoftheirownsoftware.

Althoughthebookfocusesonteachingyoutodeveloptoolsthatwouldlikelybeconsideredanuisanceorevenmaliciousbygamingcompanies,you’llfindthatmanyofthetechniquesareusefulfordevelopmentoftoolsthatareperfectlybenignandneutral.Furthermore,theknowledgeofhowthesetechniquesareimplementediskeyforthegamedevelopersworkingtopreventtheiruse.

PrerequisitesfortheReaderThisbookdoesnotaimtoteachyousoftwaredevelopment,andthereforeassumesthatyouhave,atminimum,asolidsoftwaredevelopmentbackground.ThisbackgroundshouldincludefamiliaritywithnativeWindows-baseddevelopment,aswellaslightexperiencewithgamedevelopmentandmemorymanagement.Whiletheseskillswillbeenoughforyoutofollowthisbook,experiencewithx86assemblyandWindowsinternalswillensurethatdetailsofmoreadvancedimplementationsarenotlostonyou.

Furthermore,sincealltheadvancedhacksdiscussedinthisbookrelyoncodeinjection,anabilitytowritecodeinanativelanguagelikeCorC++isamust.AlloftheexamplecodeinthisbookiswritteninC++andcanbecompiledwithMicrosoftVisualC++ExpressEdition.(YoucandownloadMSVC++ExpressEditionfromhttp://www.visualstudio.com/en-US/products/visual-studio-express-vs.)

NOTE

Otherlanguagesthatcompiletonativecode,suchasDelphi,arealsocapableofinjection,butIwillnotdiscusstheminthisbook.

ABriefGameHackingHistorySincethedawnofonlinePCgamingintheearly1980s,anongoingwarofwitsbetweengamehackersandgamedevelopershasbeentakingplace.Thisseeminglyendlessstrugglehaspromptedgamedeveloperstodevotecountlesshourstowardpreventinghackersfromtakingtheirgamesapartandgreasingbetweenthegears.Thesehackers,whofightbackwiththeirsophisticatedstealthimplementations,havemanymotivations:customizedgraphics,betterperformance,easeofuse,autonomousplay,in-gameassetacquisition,and,ofcourse,real-lifeprofit.

Thelate1990sandearly2000swerethegoldenageofgamehacking,whenonlinePCgamesbecameadvancedenoughtodrawlargecrowdsbutwerestillsimpleenoughtoeasilyreverseengineerandmanipulate.Online

gamesthatcameoutduringthistime,suchasTibia(January1997),Runescape(January2001),andUltimaOnline(September1997),wereheavilytargetedbybotdevelopers.Thedevelopersofthesegamesandotherslikethemstillstruggletodaytocontrolthemassivecommunitiesofbotdevelopersandbotusers.Thegamedevelopers’lackofactionandthehackers’tenacityhavenotonlycompletelyshatteredtheeconomieswithinthegames,buthavealsoproducedathrivingfor-profitindustryfocusedaroundbotdevelopmentandbotdefense.

Intheyearssincethegoldenage,morematuregamecompaniesstartedtakingbotdefenseveryseriously.Thesecompaniesnowhavededicatedteamsfocusedondevelopingbotpreventionsystems,andmanyalsoviewbotsasalegalmatterandwillnothesitatetobanishplayerswhousebotsandsuethebotdeveloperswhoprovidedthem.Asaresult,manygamehackershavebeenforcedtodevelopadvancedstealthtechniquestokeeptheiruserssafe.

Thiswarwageson,andthenumbersonbothsidesofthefightwillcontinuetogrowasonlinegamingbecomesmoreprevalentoverthecomingyears.Majorgamedevelopersarepursuinghackerswithendlessdetermination,evenslammingsomegamehackinggiantswithmultimillion-dollarlawsuits.Thismeansthatgamehackerswhoareseriousabouttheirbusinessmusteithertargetsmallergamingcompanies,oranonymouslymarkettheirproductsfromtheshadowsinordertoescapeprosecution.Fortheforeseeablefuture,gamehackingandbotdevelopmentwillcontinuetogrowintoalargerandmorelucrativeindustryforthosegamehackersboldenoughtotaketherisks.

WhyHackGames?Asidefromitsobviousallureandchallengingnature,gamehackinghassomepracticalandprofitablepurposes.Everyday,thousandsofnoviceprogrammersexperimentwithsmall-scalegamehackingasawaytoautomatemonotonoustasksorperformmenialactions.ThesescriptkiddieswilluseautomationtoolslikeAutoItfortheirsmall,relativelyharmlesshacks.Ontheotherhand,professionalgamehackers,backedbytheirlargetoolkitsandyearsofprogrammingexperience,willdevotehundredsofhourstothedevelopmentofadvancedgamehacks.Thesetypesofgamehacks,whicharethefocusofthisbook,areoftencreatedwiththeintentofmaking

whicharethefocusofthisbook,areoftencreatedwiththeintentofmakinglargeamountsofmoney.

Gamingisahugeindustrythatgenerated$22.4billioninsalesin2014,accordingtotheEntertainmentSoftwareAssociation.Ofthetensofmillionsofplayerswhoplaygamesdaily,20percentplaymassivelymultiplayeronlinerole-playinggames(MMORPGs).TheseMMORPGsoftenhavethousandsofplayerswhotradevirtualgoodswithinthrivingin-gameeconomies.Playersoftenhaveaneedforin-gameassetsandarewillingtobuytheseassetswithreal-worldmoney.Consequently,MMORPGplayersendupdevelopinglargecommunitiesthatprovidegold-for-cashservices.Theseservicesoftengoasfarasenforcingexchangeratesfromin-gamegoldtoreal-worldcurrencies.

Totakeadvantageofthis,gamehackerswillcreatebotsthatarecapableofautomaticallyfarminggoldandlevelingcharacters.Then,dependingontheirgoal,hackerswilleithersetupmassivegoldfarmsandselltheirin-gameprofits,orperfectandselltheirsoftwaretoplayerswhowishtoseamlesslyobtainlevelsandgoldwithminimalinterference.DuetothemassivecommunitiessurroundingpopularMMORPGs,thesegamehackerscanmakebetweensixandsevenfiguresannually.

WhileMMORPGsprovidethelargestattacksurfaceforhackers,theyhavearelativelysmallaudienceoverall.About38percentofgamersfavorreal-timestrategy(RTS)andmassiveonlinebattlearena(MOBA)games,andanother6percentplayprimarilyfirst-personshooter(FPS)games.Thesecompetitiveplayerversusplayer(PvP)gamescollectivelyrepresent44percentofthegamingmarketandprovidegreatrewardstodeterminedgamehackers.

PvPgamesareoftenepisodicinnature;eachmatchisanisolatedgame,andthere’stypicallynotmuchprofitableprogressionforbottingawayfromkeyboard(AFK).Thismeansthat,insteadofrunninggoldfarmsorcreatingautonomousbotstolevelupcharacters,hackerswillcreatereactivebotsthatassistplayersincombat.

Thesehighlycompetitivegamesareaboutskillandtactics,andmostplayersparticipatetoprovetheirabilitytothemselvesandothers.Asaconsequence,thenumberofpeopleseekingbotsforPvP-typegamesissubstantiallylowerthanyou’dfindinthegrind-heavyworldofMMORPGs.Nevertheless,hackerscanstillmakeaprettypennysellingtheirPvPbots,whichareoftenmucheasiertodevelopthanfull-fledgedautonomousbots.

HowThisBookIsOrganizedThisbookissplitintofourparts,eachofwhichfocusesonadifferentcoreaspectofgamehacking.InPart1:ToolsoftheTrade,you’llgetaboxfulloftoolstohelpyouhackgames.

•Chapter1:ScanningMemoryUsingCheatEnginewillteachyouhowtoscanagame’smemoryforimportantvaluesusingCheatEngine.

•InChapter2:DebuggingGameswithOllyDbg,you’llgetacrashcourseindebuggingandreverseengineeringwithOllyDbg.Theskillsyoulearnherewillbeextremelyusefulwhenyoustartmakingadvancedbotsandinjectingcode.

•Towrapup,Chapter3:ReconnaissancewithProcessMonitorandProcessExplorer,willteachyouhowtousetworeconnaissancetoolstoinspecthowgamesinteractwithfiles,otherprocesses,thenetwork,andtheoperatingsystem.

TheonlineresourcesforeachchapterinPart1includecustombinariesIcreatedtogiveyouasafeplacetotestandhoneyournewlydiscoveredskills.

Onceyou’recomfortablewitheverywrenchandhammer,Part2:GameDissection,willteachyouhowtogetunderthehoodandfigureouthowgameswork.

•InChapter4:FromCodetoMemory:AGeneralPrimer,you’lllearnwhatagame’ssourcecodeanddatalooklikeoncecompiledintoagamebinary.

•Chapter5:AdvancedMemoryForensicsbuildsontheknowledgeyou’llgainfromChapter4.You’lllearnhowtoscanmemoryandusedebuggingtoseamlesslylocatetrickymemoryvaluesanddissectcomplexclassesandstructures.

•Finally,Chapter6:ReadingfromandWritingtoGameMemoryshowsyouhowtoreadandmodifydatawithinarunninggame.

Thesechaptersprovidelotsofin-depthproof-of-conceptexamplecodethatyoucanusetoverifyeverythingyouread.

InPart3:ProcessPuppeteering,you’llbecomeapuppeteerasyoulearnhowtoturnanygameintoamarionette.

•BuildingontheskillsfromParts1and2,Chapter7:CodeInjectiondescribeshowtoinjectandexecuteyourowncodeintheaddressspaceofagame.

•Onceyou’vemasteredinjection,Chapter8:ManipulatingControlFlowinaGamewillteachyouhowtouseinjectiontointercept,modify,ordisableanyfunctioncallmadebyagame,andwillwrapupwithsomeusefulreal-worldexamplesforthecommonlibrariesAdobeAIRandDirect3D.

Tocomplementyourpuppeteeringclasses,thesechaptersareaccompaniedbythousandsoflinesofproduction-readycodethatyoucanuseasaboilerplatelibraryforafuturebot.

InPart4:CreatingBots,you’llseehowtocombineyourtoolbox,dissectionabilities,puppeteeringskills,andsoftwareengineeringbackgroundtocreatepowerfulbots.

•Chapter9:UsingExtrasensoryPerceptiontoWardOffFogofWarexploreswaystomakeagamedisplayusefulinformationthatisn’texposedbydefault,suchasthelocationsofhiddenenemiesandtheamountofexperienceyouearnperhour.

•Chapter10:ResponsiveHacksshowscodepatternsyoucanusetodetectin-gameevents,likedecreasesinhealth,andtomakebotsthatreacttothoseeventsfasterthanhumanplayers.

•Chapter11:PuttingItAllTogether:WritingAutonomousBotsrevealshowbotsthatplaygameswithouthumaninteractionwork.Automatedbotscombinecontroltheory,statemachines,searchalgorithms,andmathematicalmodels,andthischapterisacrashcourseinthosetopics.

•InChapter12:StayingHidden,you’lllearnaboutsomeofthehigh-leveltechniquesyoucanusetoescapeandevadeanysystemthatwouldinterferewithyourbots.

Asyou’veprobablycometoexpect,thesechaptershavelotsofexamplecode.Someofthehacksshowninthispartarebuiltonexamplecodefrom

previouschapters.Othersexploresuccinct,straightforwarddesignpatternsyoucanusetocreateyourownbots.Onceyou’vefinishedallfourpartsofthisbook,you’llbesentoffintothevirtualworldwithyournewsuperpower.

AbouttheOnlineResourcesYou’llfindmanyadditionalresourcesforthisbookathttps://www.nostarch.com/gamehacking/.Theseresourcesincludecompiledbinariestotestyourskills,aconsiderableamountofexamplecode,andquiteafewsnippetsofproduction-readygamehackingcode.Theseresourcesgohand-in-handwiththebook,anditreallyisn’tcompletewithoutthem,somakesuretodownloadthembeforeyoucontinue.

HowtoUseThisBookThisbookshouldbeusedfirstandforemostasaguidetogetyoustartedingamehacking.Theprogressionissuchthatthecontentofeachchapterintroducesnewskillsandabilitiesthatbuildonallpreviouschapters.Asyoucompletechapters,Iencourageyoutoplaywiththeexamplecodeandtestyourskillsonarealgamebeforecontinuingyourreading.Thisisimportant,assomecoveredtopicswillhaveusecasesthatdon’tbecomeevidentuntilyou’re10feetdeepinthemud.

Onceyou’vefinishedthebook,Ihopeitcanstillbeusefultoyouasafieldmanual.Ifyoucomeacrosssomedatastructureyou’reunsureof,maybethedetailsinChapter5canhelp.Ifyoureverseengineeragame’smapformatandarereadytocreateapathfinder,youcanalwaysfliptoChapter11,studythecontent,andusesomeoftheexamplecodeasastartingpoint.Althoughit’simpossibletoanticipatealltheproblemsyoumightfacewhenyou’rehackingaway,I’vetriedtoensureyou’llfindsomeanswerswithinthesepages.

ANOTEFROMTHEPUBLISHERThisbookdoesnotcondonepiracy,violatingtheDMCA,infringingcopyright,orbreakingin-gameTermsofService.Gamehackershave

copyright,orbreakingin-gameTermsofService.Gamehackershavebeenbannedfromgamesforlife,suedformillionsofdollars,andevenjailedfortheirwork.

PART1TOOLSOFTHETRADE

1SCANNINGMEMORYUSINGCHEATENGINE

Thebestgamehackersintheworldspendyearspersonalizingexpansivearsenalswithcustom-builttools.Suchpotenttoolkitsenablethesehackerstoseamlesslyanalyzegames,effortlesslyprototypehacks,andeffectivelydevelopbots.Atthecore,however,eachuniquekitisbuiltfromthesamefour-piecepowerhouse:amemoryscanner,anassembler-leveldebugger,aprocessmonitor,andahexeditor.

Memoryscanningisthegatewaytogamehacking,andthischapterwillteachyouaboutCheatEngine,apowerfulmemoryscannerthatsearchesagame’soperatingmemory(whichlivesinRAM)forvaluesliketheplayer’slevel,health,orin-gamemoney.First,I’llfocusonbasicmemoryscanning,memorymodification,andpointerscanning.Followingthat,we’lldiveintoCheatEngine’spowerfulembeddedLuascriptingengine.

NOTE

YoucangrabCheatEnginefromhttp://www.cheatengine.org/.Payattentionwhenrunningtheinstallerbecauseitwilltrytoinstallsometoolbarsandotherbloatware.Youcandisablethoseoptionsifyouwish.

WhyMemoryScannersAreImportantKnowingagame’sstateisparamounttointeractingwiththegame

Knowingagame’sstateisparamounttointeractingwiththegameintelligently,butunlikehumans,softwarecan’tdeterminethestateofagamesimplybylookingatwhat’sonthescreen.Fortunately,underneathallofthestimuliproducedbyagame,acomputer’smemorycontainsapurelynumericrepresentationofthatgame’sstate—andprogramscanunderstandnumberseasily.Hackersusememoryscannerstofindthosevaluesinmemory,andthenintheirprograms,theyreadthememoryintheselocationstounderstandthegame’sstate.

Forexample,aprogramthathealsplayerswhentheyfallbelow500healthneedstoknowhowtodotwothings:trackaplayer’scurrenthealthandcastahealingspell.Theformerrequiresaccesstothegame’sstate,whilethelattermightonlyrequireabuttontobepressed.Giventhelocationwhereaplayer’shealthisstoredandthewaytoreadagame’smemory,theprogramwouldlooksomethinglikethispseudocode:

//dothisinsomeloophealth=readMemory(game,HEALTH_LOCATION)if(health<500)pressButton(HEAL_BUTTON)

AmemoryscannerallowsyoutofindHEALTH_LOCATIONsothatyoursoftwarecanqueryitforyoulater.

BasicMemoryScanningThememoryscanneristhemostbasic,yetmostimportant,toolfortheaspiringgamehacker.Asinanyprogram,alldatainthememoryofagameresidesatanabsolutelocationcalledamemoryaddress.Ifyouthinkofthememoryasaverylargebytearray,amemoryaddressisanindexpointingtoavalueinthatarray.Whenamemoryscanneristoldtofindsomevaluex(calledascanvalue,becauseit’sthevalueyou’rescanningfor)inagame’smemory,thescannerloopsthroughthebytearraylookingforanyvalueequaltox.Everytimeitfindsamatchingvalue,itaddstheindexofthematchtoaresultlist.

Duetothesheersizeofagame’smemory,however,thevalueofxcanappearinhundredsoflocations.Imaginethatxistheplayer’shealth,whichiscurrently500.Ourxuniquelyholds500,but500isnotuniquelyheldbyx,

soascanforxreturnsallvariableswithavalueof500.Anyaddressesnotrelatedtoxareultimatelyclutter;theyshareavalueof500withxonlybychance.Tofilterouttheseunwantedvalues,thememoryscannerallowsyoutorescantheresultlist,removingaddressesthatnolongerholdthesamevalueasx,whetherxisstill500orhaschanged.

Fortheserescanstobeeffective,theoverallstateofthegamemusthavesignificantentropy—ameasureofdisorder.Youincreaseentropybychangingthein-gameenvironment,oftenbymovingaround,killingcreatures,orswitchingcharacters.Asentropyincreases,unrelatedaddressesarelesslikelytocontinuetoarbitrarilyholdthesamevalue,andgivenenoughentropy,afewrescansshouldfilteroutallfalsepositivesandleaveyouwiththetrueaddressofx.

CheatEngine’sMemoryScannerThissectiongivesyouatourofCheatEngine’smemory-scanningoptions,whichwillhelpyoutrackdowntheaddressesofgamestatevaluesinmemory.I’llgiveyouachancetotrythescanneroutin“BasicMemoryEditing”onpage11;fornow,openCheatEngineandhavealookaround.Thememoryscanneristightlyencapsulatedinitsmainwindow,asshowninFigure1-1.

Figure1-1:CheatEnginemainscreen

Tobeginscanningagame’smemory,clicktheAttachicon➊toattachtoaprocessandthenenterthescanvalue(referredtoasxinourconceptualscanner)youwanttolocate➌.Byattachingtoaprocess,we’retellingCheatEnginetopreparetooperateonit;inthiscase,thatoperationisascan.IthelpstoalsotellCheatEnginewhatkindofscantorun,asI’lldiscussnext.

ScanTypesCheatEngineallowsyoutoselecttwodifferentscandirectives,calledScanTypeandValueType➍.ScanTypetellsthescannerhowtocompareyourscanvaluewiththememorybeingscannedusingoneofthefollowingscantypes:

ExactValueReturnsaddressespointingtovaluesequaltothescanvalue.Choosethisoptionifthevalueyouarelookingforwon’tchangeduringthescan;health,mana,andleveltypicallyfallintothiscategory.

BiggerThanReturnsaddressespointingtovaluesgreaterthanthescanvalue.Thisoptionisusefulwhenthevalueyou’researchingforissteadilyincreasing,whichoftenhappenswithtimers.

SmallerThanReturnsaddressespointingtovaluessmallerthanthescanvalue.LikeBiggerThan,thisoptionisusefulforfindingtimers(inthiscase,onesthatcountdownratherthanup).

ValueBetweenReturnsaddressespointingtovalueswithinascanvaluerange.ThisoptioncombinesBiggerThanandSmallerThan,displayingasecondaryscanvalueboxthatallowsyoutoinputamuchsmallerrangeofvalues.

UnknownInitialValueReturnsalladdressesinaprogram’smemory,allowingrescanstoexaminetheentireaddressrangerelativetotheirinitialvalues.Thisoptionisusefulforfindingitemorcreaturetypes,sinceyouwon’talwaysknowtheinternalvaluesthegamedevelopersusedtorepresenttheseobjects.

TheValueTypedirectivetellstheCheatEnginescannerwhattypeofvariableit’ssearchingfor.

RunningYourFirstScanOncethetwoscandirectivesareset,clickFirstScan➋torunaninitialscanforvalues,andthescannerwillpopulatetheresultslist➎.Anygreenaddressesinthislistarestatic,meaningthattheyshouldremainpersistentacrossprogramrestarts.Addresseslistedinblackresideindynamicallyallocatedmemory,memorythatisallocatedatruntime.

Whentheresultslistisfirstpopulated,itshowstheaddressandreal-timevalueofeachresult.Eachrescanwillalsoshowthevalueofeachresultduringthepreviousscan.(Anyreal-timevaluesdisplayedareupdatedatanintervalthatyoucansetinEdit▸Settings▸GeneralSettings▸Updateinterval.)

NextScansOncetheresultslistispopulated,thescannerenablestheNextScan➋button,whichofferssixnewscantypes.Theseadditionalscantypesallowyoutocomparetheaddressesintheresultslisttotheirvaluesinthepreviousscan,whichwillhelpyounarrowdownwhichaddressholdsthegamestatevalueyou’rescanningfor.Theyareasfollows:

IncreasedValueReturnsaddressespointingtovaluesthathaveincreased.ThiscomplementstheBiggerThanscantypebykeepingthesameminimumvalueandremovinganyaddresswhosevaluehasdecreased.

IncreasedValueByReturnsaddressespointingtovaluesthathaveincreasedbyadefinedamount.Thisscantypeusuallyreturnsfarfewerfalsepositives,butyoucanuseitonlywhenyouknowexactlyhowmuchavaluehasincreased.

DecreasedValueThisoptionistheoppositeofIncreasedValue.

DecreasedValueByThisoptionistheoppositeofIncreasedValueBy.

ChangedValueReturnsaddressespointingtovaluesthathavechanged.Thistypeisusefulwhenyouknowavaluewillmutate,butyou’reunsurehow.

UnchangedValueReturnsaddressespointingtovaluesthathaven’tchanged.Thiscanhelpyoueliminatefalsepositives,sinceyoucaneasilycreatealargeamountofentropywhileensuringthedesiredvaluestaysthesame.

You’llusuallyneedtousemultiplescantypesinordertonarrowdownalargeresultlistandfindthecorrectaddress.Eliminatingfalsepositivesisoftenamatterofproperlycreatingentropy(asdescribedin“BasicMemoryScanning”onpage4),tacticallychangingyourscandirectives,bravelypressingNextScan,andthenrepeatingtheprocessuntilyouhaveasingleremainingaddress.

WhenYouCan’tGetaSingleResultSometimesitisimpossibletopinpointasingleresultinCheatEngine,in

whichcaseyoumustdeterminethecorrectaddressthroughexperimentation.Forexample,ifyou’relookingforyourcharacter’shealthandcan’tnarrowitdowntofewerthanfiveaddresses,youcouldtrymodifyingthevalueofeachaddress(asdiscussedin“ManualModificationwithCheatEngine”onpage8)untilyouseethehealthdisplaychangeortheothervaluesautomaticallychangetotheoneyouset.

CheatTablesOnceyou’vefoundthecorrectaddress,youcandouble-clickittoaddittothecheattablepane➏;addressesinthecheattablepanecanbemodified,watched,andsavedtocheattablefilesforfutureuse.

Foreachaddressinthecheattablepane,youcanaddadescriptionbydouble-clickingtheDescriptioncolumn,andyoucanaddacolorbyright-clickingandselectingChangeColor.Youcanalsodisplaythevaluesofeachaddressinhexadecimalordecimalformatbyright-clickingandselectingShowashexadecimalorShowasdecimal,respectively.Lastly,youcanchangethedatatypeofeachvaluebydouble-clickingtheTypecolumn,oryoucanchangethevalueitselfbydouble-clickingtheValuecolumn.

Sincethemainpurposeofthecheattablepaneistoallowagamehackertoneatlytrackaddresses,itcanbedynamicallysavedandloaded.GotoFile▸SaveorFile▸SaveAstosavethecurrentcheattablepanetoa.ctdocumentfilecontainingeachaddresswithitsvaluetype,description,displaycolor,anddisplayformat.Toloadthesaved.ctdocuments,gotoFile▸Load.(You’llfindmanyready-madecheattablesforpopulargamesathttp://cheatengine.org/tables.php.)

NowthatI’vedescribedhowtoscanforagamestatevalue,I’lldiscusshowyoucanchangethatvaluewhenyouknowwhereitlivesinmemory.

MemoryModificationinGamesBotscheatagamesystembymodifyingmemoryvaluesinthegame’sstateinordertogiveyoulotsofin-gamemoney,modifyyourcharacter’shealth,changeyourcharacter’sposition,andsoon.Inmostonlinegames,acharacter’svitals(suchashealth,mana,skills,andposition)areheldinmemorybutarecontrolledbythegameserverandrelayedtoyourlocal

memorybutarecontrolledbythegameserverandrelayedtoyourlocalgameclientovertheInternet,somodifyingsuchvaluesduringonlineplayismerelycosmeticanddoesn’taffecttheactualvalues.(Anyusefulmemorymodificationtoanonlinegamerequiresamuchmoreadvancedhackthat’sbeyondCheatEngine’scapabilities.)Inlocalgameswithnoremoteserver,however,youcanmanipulateallofthesevaluesatwill.

ManualModificationwithCheatEngineWe’lluseCheatEnginetounderstandhowthememorymodificationmagicworks.

Tomodifymemorymanually,dothefollowing:

1. AttachCheatEnginetoagame.2. Eitherscanfortheaddressyouwishtomodifyorloadacheattablethat

containsit.3. Double-clickontheValuecolumnfortheaddresstoopenaninput

promptwhereyoucanenteranewvalue.4. Ifyouwanttomakesurethenewvaluecan’tbeoverwritten,selectthe

boxundertheActivecolumntofreezetheaddress,whichwillmakeCheatEnginekeepwritingthesamevaluebacktoiteverytimeitchanges.

Thismethodworkswondersforquick-and-dirtyhacks,butconstantlychangingvaluesbyhandiscumbersome;anautomatedsolutionwouldbemuchmoreappealing.

TrainerGeneratorCheatEngine’strainergeneratorallowsyoutoautomatethewholememorymodificationprocesswithoutwritinganycode.

Tocreateatrainer(asimplebotthatbindsmemorymodificationactionstokeyboardhotkeys),gotoFile▸CreategenerictrainerLuascriptfromtable.ThisopensaTrainergeneratordialogsimilartotheoneshowninFigure1-2.

Figure1-2:CheatEngineTrainergeneratordialog

Thereareanumberoffieldstomodifyhere:

ProcessnameThenameoftheexecutablethetrainershouldattachto.ThisisthenameshownintheprocesslistwhenyouattachwithCheatEngine,anditshouldbeautofilledwiththenameoftheprocessCheatEngineisattachedto.

PopuptraineronkeypressOptionallyenablesahotkey—whichyousetbyenteringakeycombinationintheboxbelowthecheckbox—todisplaythetrainer’smainwindow.

TitleThenameofyourtrainer,whichwillbedisplayedonitsinterface.Thisisoptional.

AbouttextThedescriptionofyourtrainer,tobedisplayedontheinterface;thisisalsooptional.

Freezeinterval(inmilliseconds)Theintervalduringwhichafreezeoperationoverwritesthevalue.Youshouldgenerallyleavethisat250,aslowerintervalscansapresourcesandhighervaluesmaybetooslow.

Oncethesevaluesareconfigured,clickAddHotkeytosetupakeysequencetoactivateyourtrainer.Youwillbepromptedtoselectavalue

fromyourcheattable.Enteravalue,andyouwillbetakentoaSet/ChangehotkeyscreensimilartoFigure1-3.

Figure1-3:CheatEngineSet/Changehotkeyscreen

Onthisscreen,placeyourcursorintheboxlabeledTypethekeysyouwanttosetthehotkeyto➊andenterthedesiredkeycombination.Next,choosethedesiredactionfromthedrop-downmenu➋;youroptionsshouldappearinthefollowingorder:

TogglefreezeTogglesthefreezestateoftheaddress.

TogglefreezeandallowincreaseTogglesthefreezestateoftheaddressbutallowsthevaluetoincrease.Anytimethevaluedecreases,thetraineroverwritesitwithitspreviousvalue.Increasedvalueswillnotbeoverwritten.

TogglefreezeandallowdecreaseDoestheoppositeofTogglefreezeandallowincrease.

FreezeSetstheaddresstofrozenifit’snotfrozenalready.

UnfreezeUnfreezestheaddressifit’sfrozen.

SetvaluetoSetsthevaluetowhateveryouspecifyinthevaluebox➌.

DecreasevaluewithDecreasesthevaluebytheamountyouspecifyinthevaluebox➌.

IncreasevaluewithDoestheoppositeofDecreasevaluewith.

Finally,youcansetadescriptionfortheaction➍.ClickApply,thenOK,andyouractionwillappearinthelistontheTrainergeneratorscreen.Atthispoint,CheatEnginerunsthetrainerinthebackground,andyoucansimplypressthehotkeysyouconfiguredtoexecutethememoryactions.

Tosaveyourtrainertoaportableexecutable,clickGeneratetrainer.RunningthisexecutableafterthegameislaunchedwillattachyourtrainertothegamesoyoucanuseitwithoutstartingCheatEngine.

NowthatyouknowyourwayaroundCheatEngine’smemoryscannerandtrainergenerator,trymodifyingsomememoryyourself.

BASICMEMORYEDITINGDownloadthefilesforthisbookfromhttps://www.nostarch.com/gamehacking/,andrunthefileBasicMemory.exe.Next,startupCheatEngineandattachtothebinary.Then,usingonlyCheatEngine,findtheaddressesforthex-andy-coordinatesofthegrayball.(Hint:Usethe4Bytesvaluetype.)

Onceyou’vefoundthevalues,modifythemtoplacetheballontopoftheblacksquare.Thegamewillletyouknowonceyou’vesucceededbydisplayingthetext“Goodjob!”(Hint:Eachtimetheballismoved,itsposition—storedasa4-byteinteger—inthatplaneischangedby1.Also,trytolookonlyforstatic[green]results.)

PointerScanningAsI’vementioned,onlinegamesoftenstorevaluesindynamicallyallocatedmemory.Whileaddressesthatreferencedynamicmemoryareuselesstous

inandofthemselves,somestaticaddresswillalwayspointtoanotheraddress,whichinturnpointstoanother,andsoon,untilthetailofthechainpointstothedynamicmemorywe’reinterestedin.CheatEnginecanlocatethesechainsusingamethodcalledpointerscanning.

Inthissection,I’llintroduceyoutopointerchainsandthendescribehowpointerscanningworksinCheatEngine.Whenyouhaveagoodgraspoftheuserinterface,youcangetsomehands-onexperiencein“PointerScanning”onpage18.

PointerChainsThechainofoffsetsI’vejustdescribediscalledapointerchainandlookslikethis:

list<int>chain={start,offset1,offset2[,...]}

Thefirstvalueinthispointerchain(start)iscalledamemorypointer.It’sanaddressthatstartsthechain.Theremainingvalues(offset1,offset2,andsoon)makeuptheroutetothedesiredvalue,calledapointerpath.

Thispseudocodeshowshowapointerchainmightberead:

intreadPointerChain(chain){➊ret=read(chain[0])fori=1,chain.len-1,1{offset=chain[i]ret=read(ret+offset)}returnret}

ThiscodecreatesthefunctionreadPointerPath(),whichtakesapointerchaincalledchainasaparameter.ThefunctionreadPointerPath()treatsthepointerpathinchainasalistofmemoryoffsetsfromtheaddressret,whichisinitiallysettothememorypointerat➊.Itthenloopsthroughtheseoffsets,updatingthevalueofrettotheresultofread(ret+offset)oneachiterationandreturningretonceit’sfinished.ThispseudocodeshowswhatreadPointerPath()lookslikewhentheloopisunrolled:

list<int>chain={0xDEADBEEF,0xAB,0x10,0xCC}value=readPointerPath(chain)//thefunctioncallunrollstothisret=read(0xDEADBEEF)//chain[0]

ret=read(0xDEADBEEF)//chain[0]ret=read(ret+0xAB)ret=read(ret+0x10)ret=read(ret+0xCC)intvalue=ret

Thefunctionultimatelycallsreadfourtimes,onfourdifferentaddresses—oneforeachelementinchain.

NOTE

Manygamehackersprefertocodetheirchainreadsinplace,insteadofencapsulatingtheminfunctionslikereadPointerPath().

PointerScanningBasicsPointerchainsexistbecauseeverychunkofdynamicallyallocatedmemorymusthaveacorrespondingstaticaddressthatthegame’scodecanusetoreferenceit.Gamehackerscanaccessthesechunksbylocatingthepointerchainsthatreferencethem.Becauseoftheirmultitierstructure,however,pointerchainscannotbelocatedthroughthelinearapproachthatmemoryscannersuse,sogamehackershavedevisednewwaystofindthem.

Fromareverseengineeringperspective,youcouldlocateandanalyzetheassemblycodeinordertodeducewhatpointerpathitusedtoaccessthevalue,butdoingsoisverytime-consumingandrequiresadvancedtools.Pointerscannerssolvethisproblembyusingbrute-forcetorecursivelyiterateovereverypossiblepointerchainuntiltheyfindonethatresolvestothetargetmemoryaddress.

TheListing1-1pseudocodeshouldgiveyouageneralideaofhowapointerscannerworks.

list<int>pointerScan(target,maxAdd,maxDepth){➊foraddress=BASE,0x7FFFFFF,4{ret=rScan(address,target,maxAdd,maxDepth,1)if(ret.len>0){ret.pushFront(address)returnret}}return{}}

list<int>rScan(address,target,maxAdd,maxDepth,curDepth){➋foroffset=0,maxAdd,4{value=read(address+offset)➌if(value==target)returnlist<int>(offset)}➍if(curDepth<maxDepth){curDepth++➎foroffset=0,maxAdd,4{ret=rScan(address+offset,target,maxAdd,maxDepth,curDepth)➏if(ret.len>0){ret.pushFront(offset)➐returnret}}}return{}}

Listing1-1:Pseudocodeforapointerscanner

ThiscodecreatesthefunctionspointerScan()andrScan().

pointerScan()ThepointerScan()functionistheentrypointtothescan.Ittakestheparameterstarget(thedynamicmemoryaddresstofind),maxAdd(themaximumvalueofanyoffset),andmaxDepth(themaximumlengthofthepointerpath).Itthenloopsthroughevery4-bytealignedaddress➊inthegame,callingrScan()withtheparametersaddress(theaddressinthecurrentiteration),target,maxAdd,maxDepth,andcurDepth(thedepthofthepath,whichisalways1inthiscase).

rScan()TherScan()functionreadsmemoryfromevery4-bytealignedoffsetbetween0andmaxAdd➋,andreturnsifaresultisequaltotarget➌.IfrScan()doesn’treturninthefirstloopandtherecursionisnottoodeep➍,itincrementscurDepthandagainloopsovereachoffset➎,callingitselfforeachiteration.

Ifaselfcallreturnsapartialpointerpath➏,rScan()willprependthe

currentoffsettothepathandreturnuptherecursionchain➐untilitreachespointerScan().WhenacalltorScan()frompointerScan()returnsapointerpath,pointerScan()pushesthecurrentaddresstothefrontofthepathandreturnsitasacompletechain.

PointerScanningwithCheatEngineThepreviousexampleshowedthebasicprocessofpointerscanning,buttheimplementationI’veshownisprimitive.Asidefrombeinginsanelyslowtoexecute,itwouldgeneratecountlessfalsepositives.CheatEngine’spointerscannerusesanumberofadvancedinterpolationstospeedupthescanandmakeitmoreaccurate,andinthissection,I’llintroduceyoutothesmorgasbordofavailablescanningoptions.

ToinitiateapointerscaninCheatEngine,right-clickonadynamicmemoryaddressinyourcheattableandclickPointerscanforthisaddress.Whenyouinitiateapointerscan,CheatEnginewillaskyouwheretostorethescanresultsasa.ptrfile.Onceyouenteralocation,aPointerscannerscanoptionsdialogsimilartotheoneshowninFigure1-4willappear.

Figure1-4:CheatEnginePointerscannerscanoptionsdialog

TheAddresstofindinputfieldatthetopdisplaysyourdynamicmemoryaddress.NowcarefullyselectfromamongCheatEngine’smanyscanoptions.

KeyOptionsSeveralofCheatEngine’sscanoptionstypicallyretaintheirdefaultvalues.Thoseoptionsareasfollows:

Addressesmustbe32-bitsalignedTellsCheatEnginetoscanonlyaddressesthataremultiplesof4,whichgreatlyincreasesthescanspeed.

Asyou’lllearninChapter4,compilersaligndatasothatmostaddresseswillbemultiplesof4anywaybydefault.You’llrarelyneedtodisablethisoption.

OnlyfindpathswithastaticaddressSpeedsupthescanbypreventingCheatEnginefromsearchingpathswithadynamicstartpointer.Thisoptionshouldalwaysbeenabledbecausescanningforapathstartingatanotherdynamicaddresscanbecounterproductive.

Don’tincludepointerswithread-onlynodesShouldalsoalwaysbeenabled.Dynamicallyallocatedmemorythatstoresvolatiledatashouldneverberead-only.

StoptraversingapathwhenastatichasbeenfoundTerminatesthescanwhenitfindsapointerpathwithastaticstartaddress.Thisshouldbeenabledtoreducefalsepositivesandspeedupthescan.

PointerpathmayonlybeinsidethisregionCantypicallybeleftasis.Theotheroptionsavailabletoyoucompensateforthislargerangebyintelligentlynarrowingthescopeofthescan.

FirstelementofpointerstructmustpointtomoduleTellsCheatEnginenottosearchheapchunksinwhichvirtualfunctiontablesarenotfound,undertheassumptionthatthegamewascodedusingobjectorientation.Whilethissettingcanimmenselyspeedupscans,it’shighlyunreliableandyoushouldalmostalwaysleaveitdisabled.

NoloopingpointersInvalidatesanypathsthatpointtothemselves,weedingoutinefficientpathsbutslightlyslowingdownthescan.Thisshouldusuallybeenabled.

MaxlevelDeterminesthemaximumlengthofthepointerpath.(RememberthemaxDepthvariableintheexamplecodeinListing1-1?)Thisshouldbekeptaround6or7.Ofcourse,therewillbetimeswhenyou’llneedtochangetheseoptions

fromthesettingsdescribed.Forexample,failingtoobtainreliableresultswiththeNoloopingpointersorMaxlevelsettingstypicallymeansthatthevalueyou’relookingforexistsinadynamicdatastructure,likealinkedlist,binarytree,orvector.AnotherexampleistheStoptraversingapathwhenastatichasbeenfoundoption,whichinrarecasescanpreventyoufromgettingreliableresults.

SituationalOptionsUnlikethepreviousoptions,yoursettingsfortheremainingoneswilldependonyoursituation.Here’showtodeterminethebestconfigurationforeach:

ImprovepointerscanwithgatheredheapdataAllowsCheatEnginetousetheheapallocationrecordtodetermineoffsetlimits,effectivelyspeedingupthescanbyweedingoutmanyfalsepositives.Ifyourunintoagameusingacustommemoryallocator(whichisbecomingincreasinglycommon),thisoptioncanactuallydotheexactoppositeofwhatit’smeanttodo.Youcanleavethissettingenabledininitialscans,butitshouldbethefirsttogowhenyou’reunabletofindreliablepaths.

OnlyallowstaticandheapaddressesinthepathInvalidatesallpathsthatcan’tbeoptimizedwithheapdata,makingthisapproachevenmoreaggressive.

MaxdifferentoffsetspernodeLimitsthenumberofsame-valuepointersthescannerchecks.Thatis,ifndifferentaddressespointto0x0BADF00D,thisoptiontellsCheatEnginetoconsideronlythefirstmaddresses.Thiscanbeextremelyhelpfulwhenyou’reunabletonarrowdownyourresultset.Inothercases,youmaywanttodisableit,asitwillmissmanyvalidpaths.

Allowstackaddressesofthefirstthread(s)tobehandledasstaticScansthecallstacksofoldestmthreadsinthegame,consideringthefirstnbytesineachone.ThisallowsCheatEnginetoscantheparametersandlocalvariablesoffunctionsinthegame’scallchain(thegoalbeingtofindvariablesusedbythegame’smainloop).Thepathsfoundwiththisoptioncanbebothhighlyvolatileandextremelyuseful;IuseitonlywhenIfailtofindheapaddresses.

StackaddressesasonlystaticaddressTakesthepreviousoptionevenfurtherbyallowingonlystackaddressesinpointerpaths.

PointersmustendwithspecificoffsetsCanbeusefulifyouknowtheoffset(s)attheendofavalidpath.Thisoptionwillallowyoutospecifythoseoffsets(startingwiththelastoffsetatthetop),greatlyreducingthescopeofthescan.

NrofthreadsscanningDetermineshowmanythreadsthescannerwill

use.Anumberequaltothenumberofcoresinyourprocessoroftenworksbest.Adrop-downmenuwithoptionsallowsyoutospecifythepriorityforeachthread.Idleisbestifyouwantyourscantogoveryslowly,Normaliswhatyoushoulduseformostscans,andTimecriticalisusefulforlengthyscansbutwillrenderyourcomputeruselessforthescanduration.

MaximumoffsetvalueDeterminesthemaximumvalueofeachoffsetinthepath.(RememberthemaxAddvariableinListing1-1?)Itypicallystartwithalowvalue,increasingitonlyifmyscanfails;128isagoodstartingvalue.Keepinmindthatthisvalueismostlyignoredifyou’reusingtheheapoptimizationoptions.

NOTE

WhatifbothOnlyallowstaticandheapaddressesinthepathandStackaddressesasonlystaticaddressareenabled?Willthescancomeupempty?Seemslikeafun,albeituseless,experiment.

Onceyouhavedefinedyourscanoptions,clickOKtostartapointerscan.Whenthescancompletes,aresultswindowwillappearwiththelistofpointerchainsfound.Thislistoftenhasthousandsofresults,containingbothrealchainsandfalsepositives.

PointerRescanningThepointerscannerhasarescanfeaturethatcanhelpyoueliminatefalsepositives.Tobegin,pressCTRL-RfromtheresultswindowtoopentheRescanpointerlistdialog,asshowninFigure1-5.

Figure1-5:CheatEngineRescanpointerlistdialog

TherearetwomainoptionstoconsiderwhenyoutellCheatEnginetorescan:

OnlyfilteroutinvalidpointersIfyoucheckthisbox➊,therescanwilldiscardonlypointerchainsthatpointtoinvalidmemory,whichhelpsifyourinitialresultsetisverylarge.Disablethistofilteroutpathsthatdon’tresolvetoaspecificaddressorvalue(asshowninthefigure).

RepeatrescanuntilstoppedIfyoucheckthisbox➋,therescanwillexecuteinacontinuousloop.Ideally,youshouldenablethissettingandletrescanrunwhileyoucreatealargeamountofmemoryentropy.

Fortheinitialrescan,enablebothOnlyfilteroutinvalidpointersandRepeatrescanuntilstopped,andthenpressOKtoinitiatetherescan.Therescanwindowwillgoaway,andaStoprescanloopbuttonwillappearintheresultswindow.TheresultlistwillbeconstantlyrescanneduntilyouclickStoprescanloop,butspendafewminutescreatingmemoryentropybeforedoingso.

Inrarecases,rescanningusingarescanloopmaystillleaveyouwithalargelistofpossiblepaths.Whenthishappens,youmayneedtorestartthegame,findtheaddressthatholdsyourvalue(itmayhavechanged!),andusetherescanfeatureonthisaddresstofurthernarrowresults.Inthisscan,leaveOnlyfilteroutinvalidpointersuncheckedandenterthenewaddress

intheAddresstofindfield.

NOTE

Ifyouhadtoclosetheresultswindow,youcanreopenitandloadtheresultlistbygoingtothemainCheatEnginewindowandpressingtheMemoryViewbuttonbelowtheresultspane.Thisshouldbringupamemorydumpwindow.Whenthewindowappears,pressCTRL-Ptoopenthepointerscanresultslist.ThenpressCTRL-Otoopenthe.ptrfilewhereyousavedthepointerscan.

Ifyourresultsstillaren’tnarrowenough,tryrunningthesamescanacrosssystemrestartsorevenondifferentsystems.Ifthisstillyieldsalargeresultset,eachresultcansafelybeconsideredstaticbecausemorethanonepointerchaincanresolvetothesameaddress.

Onceyou’venarroweddownyourresultset,double-clickonausablepointerchaintoaddittoyourcheattable.Ifyouhaveahandfulofseeminglyusablechains,grabtheonewiththefewestoffsets.Ifyoufindmultiplechainswithidenticaloffsetsthatstartwiththesamepointerbutdivergeafteracertainpoint,yourdatamaybestoredinadynamicdatastructure.

That’sallthereistopointerscanninginCheatEngine.Tryityourself!

POINTERSCANNINGGotohttps://www.nostarch.com/gamehacking/anddownloadMemoryPointers.exe.Unlikethelasttask,whichrequiredyoutowinonlyonce,thisonerequiresthatyouwin50timesin10seconds.Uponeachwin,thememoryaddressesforthex-andy-coordinateswillchange,meaningyouwillbeabletofreezethevalueonlyifyouhavefoundaproperpointerpath.Startthisexercisethesamewayasthepreviousone,butonceyou’vefoundtheaddresses,usethePointerscanfeaturetolocatepointerpathstothem.Then,placetheballontopoftheblacksquare,freezethevalueinplace,andpressTABtobeginthetest.Justasbefore,thegamewillletyouknowonceyou’vewon.(Hint:Trysettingthemaximumlevelto5andthe

maximumoffsetvalueto512.Also,playwiththeoptionstoallowstackaddresses,terminatethescanwhenastaticisfound,andimprovethepointerscanwithheapdata.Seewhichcombinationofoptionsgivesthebestresults.)

LuaScriptingEnvironmentHistorically,botdevelopersrarelyusedCheatEnginetoupdatetheiraddresseswhenagamereleasedapatchbecauseitwasmucheasiertodosoinOllyDbg.ThismadeCheatEngineuselesstogamehackersotherthanforinitialresearchanddevelopment—thatis,untilapowerfulLua-basedembeddedscriptingenginewasimplementedaroundCheatEngine’srobustscanningenvironment.WhilethisenginewascreatedtoenablethedevelopmentofsimplebotswithinCheatEngine,professionalgamehackersfoundtheycouldalsouseittoeasilywritecomplexscriptstoautomaticallylocateaddressesacrossdifferentversionsofagame’sbinary—ataskthatmightotherwisetakehours.

NOTE

You’llfindmoredetailabouttheCheatEngineLuascriptingengineonthewikiathttp://wiki.cheatengine.org/.

TostartusingtheLuaengine,pressCTRL-ALT-LfromthemainCheatEnginewindow.Oncethewindowopens,writeyourscriptinthetextareaandclickExecutescripttorunit.SaveascriptwithCTRL-SandopenasavedscriptwithCTRL-O.

Thescriptingenginehashundredsoffunctionsandinfiniteusecases,soI’llgiveyoujustaglimpseofitsabilitiesbybreakingdowntwoscripts.Everygameisdifferentandeverygamehackerwritesscriptstoaccomplishuniquegoals,sothesescriptsareonlyusefulfordemonstratingconcepts.

SearchingforAssemblyPatternsThisfirstscriptlocatesfunctionsthatcomposeoutgoingpacketsandsendsthemtothegameserver.Itworksbysearchingagame’sassemblycodefor

themtothegameserver.Itworksbysearchingagame’sassemblycodeforfunctionsthatcontainacertaincodesequence.

➊BASEADDRESS=getAddress("Game.exe")➋functionLocatePacketCreation(packetType)➌foraddress=BASEADDRESS,(BASEADDRESS+0x2ffffff)dolocalpush=readBytes(address,1,false)localtype=readInteger(address+1)localcall=readInteger(address+5)➍if(push==0x68andtype==packetTypeandcall==0xE8)thenreturnaddressendendreturn0endFUNCTIONHEADER={0xCC,0x55,0x8B,0xEC,0x6A}➎functionLocateFunctionHead(checkAddress)if(checkAddress==0)thenreturn0end➏foraddress=checkAddress,(checkAddress-0x1fff),-1dolocalmatch=truelocalcheckheader=readBytes(address,#FUNCTIONHEADER,true)➐fori,vinipairs(FUNCTIONHEADER)doif(v~=checkheader[i])thenmatch=falsebreakendend➑if(match)thenreturnaddress+1endendreturn0end

➒localfuncAddress=LocateFunctionHead(LocatePacketCreation(0x64))if(funcAddress~=0)thenprint(string.format("0x%x",funcAddress))elseprint("Notfound!")end

ThecodebeginsbygettingthebaseaddressofthemodulethatCheatEngineisattachedto➊.Onceithasthebaseaddress,thefunctionLocatePacketCreation()isdefined➋.Thisfunctionloopsthroughthefirst0x2FFFFFFbytesofmemoryinthegame➌,searchingforasequencethatrepresentsthisx86assemblercode:

PUSHtype;Datais:0x68[4bytetype]CALLoffset;Datais:0xE8[4byteoffset]

ThefunctionchecksthatthetypeisequaltopacketType,butitdoesn’tcarewhatthefunctionoffsetis➍.Oncethissequenceisfound,thefunctionreturns.

Next,theLocateFunctionHead()functionisdefined➎.Thefunctionbacktracksupto0x1FFFbytesfromagivenaddress➏,andateachaddress,itchecksforastubofassemblercode➐thatlookssomethinglikethis:

INT3;0xCCPUSHEBP;0x55MOVEBP,ESP;0x8B0xECPUSH[-1];0x6A0xFF

Thisstubwillbepresentatthebeginningofeveryfunction,becauseit’spartofthefunctionprologuethatsetsupthefunction’sstackframe.Onceitfindsthecode,thefunctionwillreturntheaddressofthestubplus1➑(thefirstbyte,0xCC,ispadding).

Totiethesestepstogether,theLocatePacketCreation()functioniscalledwiththepacketTypethatI’mlookingfor(arbitrarily0x64)andtheresultingaddressispassedintotheLocateFunctionHead()function➒.ThiseffectivelylocatesthefirstfunctionthatpushespacketTypeintoafunctioncallandstoresitsaddressinfuncAddress.Thisstubshowstheresult:

INT3;LocateFunctionHeadback-trackedtoherePUSHEBP;andreturnedthisaddressMOVEBP,ESPPUSH[-1]--snip--PUSH[0x64];LocatePacketCreationreturnedthisaddressCALL[something]

This35-linescriptcanautomaticallylocate15differentfunctionsinunderaminute.

SearchingforStringsThisnextLuascriptscansagame’smemoryfortextstrings.ItworksmuchastheCheatEngine’smemoryscannerdoeswhenyouusethestringvaluetype.

type.

BASEADDRESS=getAddress("Game.exe")➊functionfindString(str)locallen=string.len(str)➋localchunkSize=4096➌localchunkStep=chunkSize-lenprint("Found'"..str.."'at:")➍foraddress=BASEADDRESS,(BASEADDRESS+0x2ffffff),chunkStepdolocalchunk=readBytes(address,chunkSize,true)if(notchunk)thenbreakend➎forc=0,chunkSize-lendo➏checkForString(address,chunk,c,str,len)endendendfunctioncheckForString(address,chunk,start,str,len)fori=1,lendoif(chunk[start+i]~=string.byte(str,i))thenreturnfalseendend➐print(string.format("\t0x%x",address+start))end

➑findString("hello")➒findString("world")

Aftergettingthebaseaddress,thefindString()functionisdefined➊,whichtakesastring,str,asaparameter.Thisfunctionloopsthroughthegame’smemory➍in4,096-byte-longchunks➋.Thechunksarescannedsequentially,eachonestartinglen(thelengthofstr)bytesbeforetheendofthepreviousone➌topreventmissingastringthatbeginsononechunkandendsonanother.

AsfindString()readseachchunk,ititeratesovereverybyteuntiltheoverlappointinthechunk➎,passingeachsubchunkintothecheckForString()function➏.IfcheckForString()matchesthesubchunktostr,itprintstheaddressofthatsubchunktotheconsole➐.

Lastly,tofindalladdressesthatreferencethestrings"hello"and"world",thefunctionsfindString("hello")➑andfindString("world")➒arecalled.

Byusingthiscodetosearchforembeddeddebugstringsandpairingitwiththepreviouscodetolocatefunctionheaders,I’mabletofindalargenumberofinternalfunctionswithinagameinmereseconds.

OPTIMIZINGMEMORYCODEDuetothehighoverheadofmemoryreading,optimizationisextremelyimportantwhenyou’rewritingcodethatperformsmemoryreads.Inthepreviouscodesnippet,noticethatthefunctionfindString()doesnotusetheLuaengine’sbuiltinreadString()function.Instead,itreadsbigchunksofmemoryandsearchesthemforthedesiredstring.Let’sbreakdownthenumbers.

AscanusingreadString()wouldtrytoreadastringoflenbytesateverypossiblememoryaddress.Thismeansitwouldread,atmost,(0x2FFFFFF*len+len)bytes.However,findString()readschunksof4,096bytesandscansthemlocallyformatchingstrings.Thismeansitwouldread,atmost,(0x2FFFFFF+4096+(0x2FFFFFF/(4096-10))*len)bytes.Whensearchingforastringwithalengthof10,thenumberofbytesthateachmethodwouldreadis503,316,480and50,458,923,respectively.

NotonlydoesfindString()readanorderofmagnitudelessdata,italsoinvokesfarfewermemoryreads.Readinginchunksof4,096byteswouldrequireatotalof(0x2FFFFFF/(4096-len))reads.ComparethattoascanusingreadString(),whichwouldneed0x2FFFFFFreads.ThescanthatusesfindString()isahugeimprovementbecauseinvokingareadismuchmoreexpensivethanincreasingthesizeofdatabeingread.(NotethatIchose4,096arbitrarily.Ikeepthechunkrelativelysmallbecausereadingmemorycanbetime-consuming,anditmightbewastefultoreadfourpagesatatimejusttofindthestringinthefirst.)

ClosingThoughts

Bythispoint,youshouldhaveabasicunderstandingofCheatEngineandhowitworks.CheatEngineisaveryimportanttoolinyourkit,andIencourageyoutogetsomehands-onexperiencewithitbyfollowing“BasicMemoryEditing”onpage11and“PointerScanning”onpage18andplayingaroundwithitonyourown.

2DEBUGGINGGAMESWITHOLLYDBG

YoucanscratchthesurfaceofwhathappensasagamerunswithCheatEngine,butwithagooddebugger,youcandigdeeperuntilyouunderstandthegame’sstructureandexecutionflow.ThatmakesOllyDbgessentialtoyourgame-hackingarsenal.It’spackedwithamyriadofpowerfultoolslikeconditionalbreakpoints,referencedstringsearch,assemblypatternsearch,andexecutiontracing,makingitarobustassembler-leveldebuggerfor32-bitWindowsapplications.

I’llcoverlow-levelcodestructureindetailinChapter4,butforthischapter,Iassumeyou’reatleastfamiliarwithmoderncode-leveldebuggers,suchastheonepackagedwithMicrosoftVisualStudio.OllyDbgisfunctionallysimilartothose,withonemajordifference:itinterfaceswiththeassemblycodeofanapplication,workingevenintheabsenceofsourcecodeand/ordebugsymbols,makingitidealwhenyouneedtodigintotheinternalsofagame.Afterall,gamecompaniesarerarelynice(ordumb)enoughtoshiptheirgameswithdebugsymbols!

Inthischapter,I’llgooverOllyDbg’suserinterface,showyouhowtouseitsmostcommondebuggingfeatures,breakdownitsexpressionengine,andprovidesomereal-worldexamplesofhowyoucantieitintoyourgamehackingendeavors.Asawrap-up,I’llteachyouaboutsomeusefulplug-insandsendyouoffwithatestgamedesignedtogetyoustartedinOllyDbg.

NOTE

ThischapterfocusesonOllyDbg1.10andmaynotbeentirelyaccurateforlaterversions.Iusethisversionbecause,atthetimeofwriting,theplug-ininterfaceforOllyDbg2isstillfarlessrobustthantheoneforOllyDbg1.

WhenyoufeellikeyouhaveahandleonOllyDbg’sinterfaceandfeatures,youcantryitonagameyourselfwith“Patchinganif()Statement”onpage46.

ABriefLookatOllyDbg’sUserInterfaceGototheOllyDbgwebsite(http://www.ollydbg.de/),downloadandinstallOllyDbg,andopentheprogram.YoushouldseethetoolbarshowninFigure2-1aboveamultiplewindowinterfacearea.

Figure2-1:OllyDbgmainwindow

Thistoolbarcontainstheprogramcontrols➊,thedebugbuttons➋,theGotobutton➌,thecontrolwindowbuttons➍,andtheSettingsbutton➎.

Thethreeprogramcontrolsallowyoutoopenanexecutableandattachtotheprocessitcreates,restartthecurrentprocess,orterminateexecutionofthecurrentprocess,respectively.YoucanalsocompletethesefunctionswiththehotkeysF3,CTRL-F2,andALT-F2,respectively.Toattachtoaprocessthatisalreadyrunning,clickFile▸Attach.

Thedebugbuttonscontrolthedebuggeractions.Table2-1describeswhatthesebuttonsdo,alongwiththeirhotkeysandfunctions.Thistablealsoliststhreeusefuldebuggeractionsthatdon’thavebuttonsonthedebug

toolbar.

Table2-1:DebugButtonsandOtherDebuggerFunctions

Button Hotkey Function

Play F9 Resumesnormalexecutionoftheprocess.

Pause F12 PausesexecutionofallthreadswithintheprocessandbringsuptheCPUwindowattheinstructioncurrentlybeingexecuted.

Stepinto

F7 Single-stepstothenextoperationtobeexecuted(willdivedownintofunctioncalls).

Stepover

F8 Stepstothenextoperationtobeexecutedwithincurrentscope(willskipoverfunctioncalls).

Traceinto

CTRL-F11

Runsadeeptrace,tracingeveryoperationthatisexecuted.

Traceover

CTRL-F12

Runsapassivetracethattracesonlyoperationswithinthecurrentscope.

Executeuntilreturn

CTRL-F9

Executesuntilareturnoperationishitwithinthecurrentscope.

CTRL-F7

Automaticallysingle-stepsoneveryoperation,followingexecutioninthedisassemblywindow.Thismakesexecutionappeartobeanimated.

CTRL-F8

Alsoanimatesexecution,butstepsoverfunctionsinsteadofsteppingintothem.

ESC Stopsanimation,pausingexecutiononthecurrentoperation.

TheGotobuttonopensadialogaskingforahexadecimaladdress.Onceyouentertheaddress,OllyDbgopenstheCPUwindowandshowsthedisassemblyatthespecifiedaddress.WhentheCPUwindowisinfocus,youcanalsoshowthatinformationwiththehotkeyCTRL-G.

Thecontrolwindowbuttonsopendifferentcontrolwindows,whichdisplay

usefulinformationabouttheprocessyou’redebuggingandexposemoredebuggingfunctions,liketheabilitytosetbreakpoints.OllyDbghasatotalof13controlwindows,whichcanallbeopensimultaneouslywithinthemultiplewindowinterface.Table2-2describesthesewindows,listedintheorderinwhichtheyappearonthewindowbuttonstoolbar.

Table2-2:OllyDbg’sControlWindows

Window HotkeyFunction

Log ALT-L

Displaysalistoflogmessages,includingdebugprints,threadevents,debuggerevents,moduleloads,andmuchmore.

Modules ALT-E Displaysalistofallexecutablemodulesloadedintotheprocess.Double-clickamoduletoopenitintheCPUwindow.

Memorymap

ALT-M

Displaysalistofallblocksofmemoryallocatedbytheprocess.Double-clickablockinthelisttobringupadumpwindowofthatmemoryblock.

Threads Displaysalistofthreadsrunningintheprocess.Foreachthreadinthislist,theprocesshasastructurecalledaThreadInformationBlock(TIB).OllyDbgallowsyoutovieweachthread’sTIB;simplyright-clickathreadandselectDumpthreaddatablock.

Windows Displaysalistofwindowhandlesheldbytheprocess.Right-clickawindowinthislisttojumptoorsetabreakpointonitsclassprocedure(thefunctionthatgetscalledwhenamessageissenttothewindow).

Handles Displaysalistofhandlesheldbytheprocess.(NotethatProcessExplorerhasamuchbetterhandlelistthanOllyDbg,asIwilldiscussinChapter3.)

CPU ALT-C

Displaysthemaindisassemblerinterfaceandcontrolsamajorityofthedebuggerfunctionality.

Patches CTRL-P

Displaysalistofanyassemblycodemodificationsyouhavemadetomoduleswithintheprocess.

Callstack ALT-K

Displaysthecallstackfortheactivethread.Thewindowupdateswhentheprocesshalts.

BreakpointsALT-B Displaysalistofactivedebuggerbreakpointsandallowsyoutotogglethemonandoff.

References Displaysthereferencelist,whichtypicallyholdsthesearchresultsformanydifferenttypesofsearches.Itpopsuponitsownwhenyourunasearch.

Runtrace Displaysalistofoperationsloggedbyadebuggertrace.

Source Displaysthesourcecodeofthedisassembledmoduleifaprogramdebugdatabaseispresent.

Finally,theSettingsbuttonopenstheOllyDbgsettingswindow.Keepthedefaultsettingsfornow.

Nowthatyou’vehadatourofthemainOllyDbgwindow,let’sexploretheCPU,Patches,andRuntracewindowsmoreclosely.You’llusethosewindowsextensivelyasagamehacker,andknowingyourwayaroundthemiskey.

OllyDbg’sCPUWindowTheCPUwindowinFigure2-2iswheregamehackersspendmostoftheirtimeinOllyDbgbecauseitisthemaincontrolwindowforthedebuggingfeatures.

Figure2-2:OllyDbgCPUwindow

Thiswindowhousesfourdistinctcontrolpanes:thedisassemblerpane➊,theregisterspane➋,thedumppane➌,andthestackpane➍.ThesefourpanesencapsulateOllyDbg’smaindebuggerfunctions,soit’simportanttoknowtheminsideandout.

ViewingandNavigatingaGame’sAssemblyCodeYou’llnavigategamecodeandcontrolmostaspectsofdebuggingfromOllyDbg’sdisassemblerpane.Thispanedisplaystheassemblycodeforthecurrentmodule,anditsdataisneatlydisplayedinatablecomposedoffourdistinctcolumns:Address,Hexdump,Disassembly,andComment.

TheAddresscolumndisplaysthememoryaddressesofeachoperationinthegameprocessyou’reattachedto.Youcandouble-clickanaddressinthis

columntotogglewhetherit’sthedisplaybase.Whenanaddressissetasthedisplaybase,theAddresscolumndisplaysallotheraddressesasoffsetsrelativetoit.

TheHexdumpcolumndisplaysthebytecodeforeachoperation,groupingoperationcodesandparametersaccordingly.Blackbracesspanningmultiplelinesontheleftsideofthiscolumnmarkknownfunctionboundaries.Operationsthathavejumpsgoingtothemareshownwitharight-facingarrowontheinsideofthesebraces.Operationsthatperformjumpsareshownwitheitherup-facingordown-facingarrows,dependingonthedirectioninwhichtheyjump,ontheinsideofthesebraces.Forexample,inFigure2-2,theinstructionataddress0x779916B1(highlightedingray)hasanup-facingarrow,indicatingit’sanupwardjump.Youcanthinkofajumpasagotooperator.

TheDisassemblycolumndisplaystheassemblycodeofeachoperationthegameperforms.So,forexample,youcanconfirmthattheinstructionat0x779916B1inFigure2-2isajumpbylookingattheassembly,whichshowsaJNZ(jumpifnonzero)instruction.Blackbracesinthiscolumnmarktheboundariesofloops.Right-facingarrowsattachedtothesebracespointtotheconditionalstatementsthatcontrolwhethertheloopscontinueorexit.Thethreeright-facingarrowsinthiscolumninFigure2-2pointtoCMP(compare)andTESTinstructions,whichareusedbyassemblycodetocomparevalues.

TheCommentcolumndisplayshuman-readablecommentsabouteachoperationthegameperforms.IfOllyDbgencountersknownAPIfunctionnames,itwillautomaticallyinsertacommentwiththenameofthefunction.Similarly,ifitsuccessfullydetectsargumentsbeingpassedtoafunction,itwilllabelthem(forexample,Arg1,Arg2,...,ArgN).Youcandouble-clickinthiscolumntoaddacustomizedcomment.Blackbracesinthiscolumnmarktheassumedboundariesoffunctioncallparameters.

NOTE

OllyDbginfersfunctionboundaries,jumpdirections,loopstructures,andfunctionparametersduringcodeanalysis,soifthesecolumnslackboundarylinesorjumparrows,justpressCTRL-Atorunacodeanalysisonthebinary.

Whenthedisassemblerpaneisinfocus,thereareafewhotkeysyoucanusetoquicklynavigatecodeandcontrolthedebugger.UseF2forTogglebreakpoint,SHIFT-F12forPlaceconditionalbreakpoint,-(hyphen)forGobackand+(plus)forGoforward(thesetwoworkasyou’dexpectinawebbrowser),*(asterisk)forGotoEIP(whichistheexecutionpointerinthex86architecture),CTRL--(hyphen)forGotopreviousfunction,andCTRL-+

forGotonextfunction.ThedisassemblercanalsopopulatetheReferenceswindowwithdifferent

typesofsearchresults.WhenyouwanttochangetheReferenceswindow’scontents,right-clickinthedisassemblerpane,mouseovertheSearchformenutoexpandit,andselectoneofthefollowingoptions:

AllintermodularcallsSearchesforallcallstofunctionsinremotemodules.Thiscan,forexample,allowyoutoseeeverywherethatagamecallsSleep(),PeekMessage(),oranyotherWindowsAPIfunction,enablingyoutoinspectorsetbreakpointsonthecalls.

AllcommandsSearchesforalloccurrencesofagivenoperationwritteninassembly,wheretheaddedoperatorsCONSTandR32willmatchaconstantvalueoraregistervalue,respectively.OneuseforthisoptionmightbesearchingforcommandslikeMOV[0xDEADBEEF],CONST;MOV[0xDEADBEEF],R32;andMOV[0xDEADBEEF],[R32+CONST]tolistalloperationsthatmodifymemoryattheaddress0xDEADBEEF,whichcouldbeanything,includingtheaddressofyourplayer’shealth.

AllsequencesSearchesforalloccurrencesofagivensequenceofoperations.Thisissimilartothepreviousoptions,butitallowsyoutospecifymultiplecommands.

AllconstantsSearchesforallinstancesofagivenhexadecimalconstant.Forinstance,ifyouentertheaddressofyourcharacter’shealth,thiswilllistallofthecommandsthatdirectlyaccessit.

AllswitchesSearchesforallswitch-caseblocks.

AllreferencedtextstringsSearchesforallstringsreferencedincode.Youcanusethisoptiontosearchthroughallreferencedstringsandseewhatcodeaccessesthem,whichcanbeusefulforcorrelatingin-gametextdisplayswiththecodethatdisplaysthem.Thisoptionisalsoveryusefulforlocatinganydebugassertionorloggingstrings,whichcanbea

tremendoushelpindeterminingthepurposeofcodeparts.

ThedisassemblercanalsopopulatetheNameswindowwithalllabelsinthecurrentmodule(CTRL-N)orallknownlabelsinallmodules(Searchfor▸Nameinallmodules).KnownAPIfunctionswillbeautomaticallylabeledwiththeirnames,andyoucanaddalabeltoacommandbyhighlightingit,pressingSHIFT-;andenteringthelabelwhenprompted.Whenalabeledcommandisreferencedincode,thelabelwillbeshowninplaceoftheaddress.Onewaytousethisfeatureistonamefunctionsthatyou’veanalyzed(justsetalabelonthefirstcommandinafunction)soyoucanseetheirnameswhenotherfunctionscallthem.

ViewingandEditingRegisterContentsTheregisterspanedisplaysthecontentsoftheeightprocessorregisters,alleightflagbits,thesixsegmentregisters,thelastWindowserrorcode,andEIP.Underneaththesevalues,thispanecandisplayeitherFloating-PointUnit(FPU)registersordebugregisters;clickonthepane’sheadertochangewhichregistersaredisplayed.Thevaluesinthispanearepopulatedonlyifyoufreezeyourprocess.Valuesthataredisplayedinredhavebeenchangedsincethepreviouspause.Double-clickonvaluesinthispanetoeditthem.

ViewingandSearchingaGame’sMemoryThedumppanedisplaysadumpofthememoryataspecificaddress.Tojumptoanaddressanddisplaythememorycontents,pressCTRL-Gandentertheaddressintheboxthatappears.YoucanalsojumptotheaddressofanentryintheotherCPUwindowpanesbyright-clickingontheAddresscolumnandselectingFollowindump.

Whiletherearealwaysthreecolumnsinthedumppane,theonlyoneyoushouldalwaysseeistheAddresscolumn,whichbehavesmuchlikeitscousinwithinthedisassemblerpane.Thedatadisplaytypeyouchoosedeterminestheothertwocolumnsshown.Right-clickthedumppanetochangethedisplaytype;fortheoneshowninFigure2-2,you’dright-clickandselectHex▸Hex/ASCII(8bytes).

Youcansetamemorybreakpointonanaddressshowninthedumppanebyright-clickingthataddressandexpandingtheBreakpointsubmenu.Select

Memory▸Onaccessfromthismenutobreakonanycodethatusestheaddressatall,orselectMemory▸Onwritetobreakonlyoncodethatwritestothatspaceinmemory.Toremoveamemorybreakpoint,selectRemovememorybreakpointinthesamemenu;thisoptionappearsonlywhentheaddressyouright-clickhasabreakpoint.

Withoneormorevaluesselectedinthedump,youcanpressCTRL-Rtosearchthecurrentmodule’scodeforreferencestoaddressesoftheselectedvalues;resultsofthissearchappearintheReferenceswindow.YoucanalsosearchforvaluesinthispaneusingCTRL-BforbinarystringsandCTRL-Nforlabels.Afteryouinitiateasearch,pressCTRL-Ltojumptothenextmatch.CTRL-Eallowsyoutoeditanyvaluesyouhaveselected.

NOTE

ThedumpwindowsthatyoucanopenfromtheMemorywindowworkinthesamewayasthedumppane.

ViewingaGame’sCallStackThefinalCPUpaneisthestackpane,andasthenamesuggests,itshowsthecallstack.Likethedumpanddisassemblerpanes,thestackpanehasanAddresscolumn.ThestackpanealsohasaValuecolumn,whichshowsthestackasanarrayof32-bitintegers,andaCommentcolumn,whichshowsreturnaddresses,knownfunctionnames,andotherinformativelabels.Thestackpanesupportsallthesamehotkeysasthedumppane,withtheexceptionofCTRL-N.

MULTICLIENTPATCHINGOnetypeofhack,calledamulticlientpatch,overwritesthesingle-instancelimitationcodewithinagame’sbinarywithno-operationcode,allowingtheusertorunmultiplegameclients,evenwhendoingsoisnormallyforbidden.Becausethecodethatperformsinstancelimitationmustbeexecutedveryearlyafteragameclientislaunched,itcanbenearlyimpossibleforabottoinjectitspatchontime.The

easiestworkaroundforthisistomakemulticlientpatchespersistbyapplyingthemwithinOllyDbgandsavingthemdirectlytothegamebinary.

CreatingCodePatchesOllyDbg’scodepatchesletyoumakeassemblycodemodificationsforagameyouwanttohack,removingtheneedtoengineeratooltailoredtothatspecificgame.Thismakesprototypingcontrolflowhacks—whichmanipulategamebehaviorthroughamixofgamedesignflaws,x86assemblyprotocols,andcommonbinaryconstructs—mucheasier.

Gamehackerstypicallyincludeperfectedpatchesasoptionalfeaturesinabot’stoolsuite,butinsomecases,makingthosefeaturespersistentisactuallymoreconvenientforyourenduser.Luckily,OllyDbgpatchesprovidethecompletefunctionalityyouneedtodesign,test,andpermanentlysavecodemodificationstoanexecutablebinaryusingonlyOllyDbg.

Toplaceapatch,navigatetothelineofassemblycodeyouwanttopatchintheCPUwindow,double-clicktheinstructionyouwishtomodify,placeanewassemblyinstructioninthepop-upprompt,andclickAssemble,asshowninFigure2-3.

Figure2-3:PlacingapatchwithOllyDbg

Alwayspayattentiontothesizeofyourpatch—youcan’tjustresizeandmovearoundassembledcodehoweveryou’dlike.Patcheslargerthanthecodeyouintendtoreplacewilloverflowintosubsequentoperations,potentiallyremovingcriticalfunctionality.Patchessmallerthantheoperationsyouintendtoreplacearesafe,aslongasFillwithNOPsischecked.Thisoptionfillsanyabandonedbyteswithno-operation(NOP)commands,whicharesingle-byteoperationsthatdonothingwhenexecuted.

Allpatchesyouplacearelisted,alongwiththeaddress,size,state,oldcode,newcode,andcomment,inthePatcheswindow.Selectapatchinthislisttoaccessasmallbutpowerfulsetofhotkeys,showninTable2-3.

Table2-3:PatchesWindowHotkeys

OperatorFunction

ENTER Jumpstothepatchinthedisassembler.

spacebar Togglesthepatchonoroff.

F2 Placesabreakpointonthepatch.

SHIFT-F2 Placesaconditionalbreakpointonthepatch.

SHIFT-F4 Placesaconditionallogbreakpointonthepatch.DEL Removesthepatchentryfromthelistonly.

InOllyDbg,youcanalsosaveyourpatchesdirectlytothebinary.First,right-clickinthedisassemblerandclickCopytoexecutable▸Allmodifications.Ifyouwanttocopyonlycertainpatches,highlighttheminthedisassemblypaneandpressCopytoexecutable▸Selectioninstead.

DETERMININGPATCHSIZEThereareafewwaystodeterminewhetheryourpatchwillbeadifferentsizethantheoriginalcode.Forexample,inFigure2-3,youcanseethecommandat0x7790ED2EbeingchangedfromSHRAL,6toSHRAL,7.Ifyoulookatthebytestotheleftofthecommand,yousee3bytesthatrepresentthememoryofthecommand.Thismeansour

newcommandmusteitherbe3bytesorpaddedwithNOPsifit’slessthan3bytes.Furthermore,thesebytesarearrangedintwocolumns.Thefirstcolumncontains0xC0and0x08,whichrepresentthecommandSHRandthefirstoperand,AL.Thesecondcolumncontains0x06,whichrepresentstheoriginaloperand.Becausethesecondcolumnshowsasinglebyte,anyreplacementoperandmustalsobe1byte(between0x00and0xFF).Ifthiscolumnhadshown0x00000006instead,areplacementoperandcouldbeupto4bytesinlength.

TypicalcodepatcheswilleitheruseallNOPstocompletelyremoveacommand(byleavingtheboxemptyandlettingitfilltheentirecommandwithNOPs)orjustreplaceasingleoperand,sothismethodofcheckingpatchsizeisalmostalwayseffective.

TracingThroughAssemblyCodeWhenyourunatraceonanyprogram,OllyDbgsingle-stepsovereveryexecutedoperationandstoresdataabouteachone.Whenthetraceiscomplete,theloggeddataisdisplayedintheRuntracewindow,showninFigure2-4.

Figure2-4:TheRuntracewindow

TheRuntracewindowisorganizedintothefollowingsixcolumns:

BackThenumberofoperationsloggedbetweenanoperationandthecurrentexecutionstate

ThreadThethreadthatexecutedtheoperation

ModuleThemodulewheretheoperationresides

AddressTheaddressoftheoperation

CommandTheoperationthatwasexecuted

ModifiedregistersTheregisterschangedbytheoperationandtheirnewvalues

Whenhackinggames,IfindOllyDbg’stracefeatureveryeffectiveathelpingmefindpointerpathstodynamicmemorywhenCheatEnginescansproveinconclusive.ThisworksbecauseyoucanfollowthelogintheRuntracewindowbackwardfromthepointwhenthememoryisusedtothepointwhereitisresolvedfromastaticaddress.

Thispotentfeature’susefulnessislimitedonlybythecreativityofthehackerusingit.ThoughItypicallyuseitonlytofindpointerpaths,I’vecomeacrossafewothersituationswhereithasproveninvaluable.Theanecdotesin“OllyDbgExpressionsinAction”onpage36willhelptoilluminatethefunctionalityandpoweroftracing.

OllyDbg’sExpressionEngineOllyDbgishometoacustomexpressionenginethatcancompileandevaluateadvancedexpressionswithasimplesyntax.Theexpressionengineissurprisinglypowerfuland,whenutilizedproperly,canbethedifferencebetweenanaverageOllyDbguserandanOllyDbgwizard.Youcanusethisenginetospecifyexpressionsformanyfeatures,suchasconditionalbreakpoints,conditionaltraces,andthecommandlineplug-in.Thissectionintroducestheexpressionengineandtheoptionsitprovides.

NOTE

Partsofthissectionarebasedontheofficialexpressionsdocumentation(http://www.ollydbg.de/Help/i_Expressions.htm).Ihavefound,however,thatafewofthecomponentsdefinedinthedocumentationdon’tseemtowork,

atleastnotinOllyDbgv1.10.TwoexamplesaretheINTandASCIIdatatypes,whichmustbesubstitutedwiththealiasesLONGandSTRING.Forthisreason,hereIincludeonlycomponentsthatI’vepersonallytestedandfullyunderstand.

UsingExpressionsinBreakpointsWhenaconditionalbreakpointistoggledon,OllyDbgpromptsyoutoenteranexpressionforthecondition;thisiswheremostexpressionsareused.Whenthatbreakpointisexecuted,OllyDbgsilentlypausesexecutionandevaluatestheexpression.Iftheresultoftheevaluationisnonzero,executionremainspausedandyouwillseethebreakpointgettriggered.Butiftheresultoftheevaluationis0,OllyDbgsilentlyresumesexecutionasifnothinghappened.

Withthehugenumberofexecutionsthathappenwithinagameeverysecond,you’lloftenfindthatapieceofcodeisexecutedinfartoomanycontextsforabreakpointtobeaneffectivewayofgettingthedatayouarelookingfor.Aconditionalbreakpointpairedwithagoodunderstandingofthecodesurroundingitisafoolproofwaytoavoidthesesituations.

UsingOperatorsintheExpressionEngineFornumericdatatypes,OllyDbgexpressionssupportgeneralC-styleoperators,asseeninTable2-4.Whilethereisnocleardocumentationontheoperatorprecedence,OllyDbgseemstofollowC-styleprecedenceandcanuseparenthesizedscoping.

Table2-4:OllyDbgNumericOperators

OperatorFunction

a==b Returns1ifaisequaltob,elsereturns0.a!=b Returns1ifaisnotequaltob,elsereturns0.a>b Returns1ifaisgreaterthanb,elsereturns0.a<b Returns1ifaislessthanb,elsereturns0.a>=b Returns1ifaisgreaterthanorequaltob,elsereturns0.a<=b Returns1ifaislessthanorequaltob,elsereturns0.

a<=b Returns1ifaislessthanorequaltob,elsereturns0.a&&b

Returns1ifaandbarebothnonzero,elsereturns0.a||b Returns1ifeitheraorbarenonzero,elsereturns0.a^b ReturnstheresultofXOR(a,b).a%b ReturnstheresultofMODULUS(a,b).a&b ReturntheresultofAND(a,b).a|b ReturntheresultofOR(a,b).a<<b Returnstheresultofashiftedbbitstotheleft.a>>b Returnstheresultofashiftedbbitstotheright.a+b Returnsthesumofaplusb.a-b Returnsthedifferenceofaminusb.a/b Returnsthequotientofadividedbyb.a*b Returnstheproductofatimesb.+a Returnsthesignedrepresentationofa.-a Returnsa*-1.!a Returns1ifais0,elsereturns0.

Forstrings,ontheotherhand,theonlyavailableoperatorsare==and!=,whichbothadheretothefollowingsetofrules:

•Stringcomparisonsarecaseinsensitive.

•Ifonlyoneoftheoperandsisastringliteral,thecomparisonwillterminateafteritreachesthelengthoftheliteral.Asaresult,theexpression[STRINGEAX]=="ABC123",whereEAXisapointertothestringABC123XYZ,willevaluateto1insteadof0.

•Ifnotypeisspecifiedforanoperandinastringcomparisonandtheotheroperandisastringliteral(forexample,"MyString"!=EAX),thecomparisonwillfirstassumethenonliteraloperandisanASCIIstring,and,ifthatcomparewouldreturn0,itwilltryasecondcompareassumingthe

operandisaUnicodestring.

Ofcourse,operatorsaren’tmuchusewithoutoperands.Let’slookatsomeofthedatayoucanevaluateinexpressions.

WorkingwithBasicExpressionElementsExpressionsareabletoevaluatemanydifferentelements,including:

CPUregistersEAX,EBX,ECX,EDX,ESP,EBP,ESI,andEDI.Youcanalsousethe1-byteand2-byteregisters(forexample,ALforthelowbyteandAXforthelowwordofEAX).EIPcanalsobeused.

SegmentregistersCS,DS,ES,SS,FS,andGS.

FPUregistersST0,ST1,ST2,ST3,ST4,ST5,ST6,andST7.

SimplelabelsCanbeAPIfunctionnames,suchasGetModuleHandle,oruser-definedlabels.

WindowsconstantsSuchasERROR_SUCCESS.

IntegersArewritteninhexadecimalformatordecimalformatiffollowedbyatrailingdecimalpoint(forexample,FFFFor65535.).

Floating-pointnumbersAllowexponentsindecimalformat(forexample,654.123e-5).

StringliteralsArewrappedinquotationmarks(forexample,"mystring").

Theexpressionsenginelooksfortheseelementsintheorderthey’relistedhere.Forexample,ifyouhavealabelthatmatchesthenameofaWindowsconstant,theengineusestheaddressofthelabelinsteadoftheconstant’svalue.Butifyouhavealabelnamedafteraregister,suchasEAX,theengineusestheregistervalue,notthelabelvalue.

AccessingMemoryContentswithExpressionsOllyDbgexpressionsarealsopowerfulenoughtoincorporatememoryreading,whichyoucandobywrappingamemoryaddress,oranexpressionthatevaluatestoone,insquarebrackets.Forexample,[EAX+C]and[401000]

representthecontentsattheaddressesEAX+Cand401000.ToreadthememoryasatypeotherthanDWORD,youcanspecifythedesiredtypeeitherbeforethebrackets,asinBYTE[EAX],orasthefirsttokenwithinthem,asin[STRINGESP+C].SupportedtypesarelistedinTable2-5.

Table2-5:OllyDbgDataTypes

DatatypeInterpretationBYTE 8-bitinteger(unsigned)CHAR 8-bitinteger(signed)WORD 16-bitinteger(unsigned)SHORT 16-bitinteger(signed)DWORD 32-bitinteger(unsigned)LONG 32-bitinteger(signed)FLOAT 32-bitfloating-pointnumberDOUBLE 64-bitfloating-pointnumberSTRING PointertoanASCIIstring(null-terminated)UNICODE PointertoaUnicodestring(null-terminated)

PluggingmemorycontentsdirectlyintoyourOllyDbgexpressionsisincrediblyusefulingamehacking,inpartbecauseyoucantellthedebuggertocheckacharacter’shealth,name,gold,andsooninmemorybeforebreaking.You’llseeanexampleofthisin“PausingExecutionWhenaSpecificPlayer’sNameIsPrinted”onpage37.

OllyDbgExpressionsinActionExpressionsinOllyDbguseasyntaxsimilartothatofmostprogramminglanguages;youcanevencombinemultipleexpressionsandnestoneexpressionwithinanother.Gamehackers(really,allhackers)commonlyusethemtocreateconditionalbreakpoints,asIdescribedin“UsingExpressionsinBreakpoints”onpage34,butyoucanusetheminmanydifferentplacesinOllyDbg.Forinstance,OllyDbg’scommandlineplug-incanevaluate

expressionsinplaceanddisplaytheirresults,allowingyoutoeasilyreadarbitrarymemory,inspectvaluesthatarebeingcalculatedbyassemblycode,orquicklygettheresultsofmathematicalequations.Furthermore,hackerscanevencreateintelligent,position-agnosticbreakpointsbycouplingexpressionswiththetracefeature.

Inthissection,I’llsharesomeanecdoteswheretheexpressionenginehascomeinhandyduringmywork.Iwillexplainmythoughtprocess,walkthroughmyentiredebuggingsession,andbreakeachexpressiondownintoitscomponentpartssoyoucanseesomewaystouseOllyDbgexpressionsingamehacking.

NOTE

Theseexamplescontainsomeassemblycode,butifyoudon’thavemuchexperiencewithassembly,don’tworry.JustignorethefinedetailsandknowthatvalueslikeECX,EAX,andESPareprocessregistersliketheonesdiscussedin“ViewingandEditingRegisterContents”onpage29.Fromthere,I’llexplaineverythingelse.

Ifyougetconfusedaboutanoperator,element,ordatatypeinanexpressionasIwalkthroughtheseanecdotes,justreferto“OllyDbg’sExpressionEngine”onpage33.

PausingExecutionWhenaSpecificPlayer’sNameIsPrintedDuringoneparticulardebuggingsession,Ineededtofigureoutexactlywhatwashappeningwhenagamewasdrawingthenamesofplayersonscreen.Specifically,Ineededtoinvokeabreakpointbeforethegamedrewthename“Player1,”ignoringallothernamesthatweredrawn.

FiguringOutWheretoPauseAsastartingpoint,IusedCheatEnginetofindtheaddressofPlayer1’snameinmemory.OnceIhadtheaddress,IusedOllyDbgtosetamemorybreakpointonthefirstbyteofthestring.Everytimethisbreakpointgothit,IquicklyinspectedtheassemblycodetodeterminehowitwasusingPlayer

1’sname.Eventually,IfoundthenamebeingaccesseddirectlyaboveacalltoafunctionthatIhadpreviouslygiventhenameprintText().Ihadfoundthecodethatwasdrawingthename.

IremovedmymemorybreakpointandreplaceditwithacodebreakpointonthecalltoprintText().Therewasaproblem,however:becausethecalltoprintText()wasinsidealoopthatiteratedovereveryplayerinthegame,mynewbreakpointwasgettinghiteverytimeanamewasdrawn—andthatwasmuchtoooften.Ineededtofixittohitonlyonaspecificplayer.

Inspectingtheassemblycodeatmypreviousmemorybreakpointtoldmethateachplayer’snamewasaccessedusingthefollowingassemblycode:

PUSHDWORDPTRDS:[EAX+ECX*90+50]

TheEAXregistercontainedtheaddressofanarrayofplayerdata;I’llcallitplayerStruct.ThesizeofplayerStructwas0x90bytes,theECXregistercontainedtheiterationindex(thefamousvariablei),andeachplayer’snamewasstored0x50bytesafterthestartofitsrespectiveplayerStruct.ThismeantthatthisPUSHinstructionessentiallyputEAX[ECX].name(thenameoftheplayeratindexi)onthestacktobepassedasanargumenttotheprintText()functioncall.Theloop,then,brokedowntosomethinglikethefollowingpsuedocode:

playerStructEAX[MAX_PLAYERS];//thisisfilledelsewherefor(int➊ECX=0;ECX<MAX_PLAYERS;ECX++){char*name=➋EAX[ECX].name;breakpoint();//mycodebreakpointwasbasicallyrighthereprintText(name);}

Purelythroughanalysis,IdeterminedthattheplayerStruct()functioncontaineddataforallplayers,andtheloopiteratedoverthetotalnumberofplayers(countingupwithECX➊),fetchedthecharactername➋foreachindex,andprintedthename.

CraftingtheConditionalBreakpointKnowingthat,topauseexecutiononlywhenprinting“Player1”allIhadtodowascheckthecurrentplayernamebeforeexecutingmybreakpoint.Inpseudocode,thenewbreakpointwouldlooklikethis:

if(EAX[ECX].name=="Player1")breakpoint();

OnceIfiguredouttheformofmynewbreakpoint,IneededtoaccessEAX[ECX].namefromwithintheloop.That’swhereOllyDbg’sexpressionenginecamein:Icouldachievemygoalbymakingslightmodificationstotheexpressionthattheassemblycodeused,leavingmewiththisexpression:

[STRINGEAX+ECX*0x90+0x50]=="Player1"

IremovedthecodebreakpointonprintText()andreplaceditwithaconditionalbreakpointthatusedthisexpression,whichtoldOllyDbgtobreakonlyifthestringvaluestoredatEAX+ECX*0x90+0x50matchedPlayer1’sname.Thisbreakpointhitonlywhen"Player1"wasbeingdrawn,allowingmetocontinuemyanalysis.

Theamountofworkittooktoengineerthisbreakpointmightseemextensive,butwithpractice,theentireprocessbecomesasintuitiveaswritingcode.Experiencedhackerscandothisinamatterofseconds.

Inpractice,thisbreakpointenabledmetoinspectcertainvaluesintheplayerStruct()functionfor"Player1"assoonasheappearedonscreen.Doingitthiswaywasimportant,asthestatesofthesevalueswererelevanttomyanalysisonlyinthefirstfewframesaftertheplayerenteredthescreen.Creativelyusingbreakpointslikethiscanenableyoutoanalyzeallsortsofcomplexgamebehavior.

PausingExecutionWhenYourCharacter’sHealthDropsDuringanotherdebuggingsession,Ineededtofindthefirstfunctioncalledaftermycharacter’shealthdroppedbelowthemaximum.Iknewtwowaystoapproachthisproblem:

•Findeverypieceofcodethataccessesthehealthvalueandplaceaconditionalbreakpointthatchecksthehealthoneachone.Then,onceoneofthesebreakpointsishit,single-stepthroughthecodeuntilthenextfunctioncall.

•UseOllyDbg’stracefunctiontocreateadynamicbreakpointthatcanstopexactlywhereIneed.

Thefirstmethodrequiredmoresetupandwasnoteasilyrepeatable,mostlyduetothesheernumberofbreakpointsneededandthefactthatI’d

mostlyduetothesheernumberofbreakpointsneededandthefactthatI’dhavetosingle-stepbyhand.Incontrast,thelattermethodhadaquicksetup,andsinceitdideverythingautomatically,itwaseasilyrepeatable.Thoughusingthetracefunctionwouldslowthegamedownconsiderably(everysingleoperationwascapturedbythetrace),Ichosethelattermethod.

WritinganExpressiontoCheckHealthOnceagain,IstartedbyusingCheatEnginetofindtheaddressthatstoredmyhealth.Usingthemethoddescribedin“CheatEngine’sMemoryScanner”onpage5,Ideterminedtheaddresstobe0x40A000.

Next,IneededanexpressionthattoldOllyDbgtoreturn1whenmyhealthwasbelowmaximumandreturn0otherwise.Knowingthatmyhealthwasstoredat0x40A000andthatthemaximumvaluewas500,Iinitiallydevisedthisexpression:

[0x40A000]<500.

Thisexpressionwouldinvokeabreakwhenmyhealthwasbelow500(remember,decimalnumbersmustbesuffixedwithaperiodintheexpressionengine),butinsteadofwaitingforafunctiontobecalled,thebreakwouldhappenimmediately.Toensurethatitwaiteduntilafunctionwascalled,Iappendedanotherexpressionwiththe&&operator:

[0x40A000]<500.&&[➊BYTEEIP]==0xE8

Onx86processors,theEIPregisterstorestheaddressoftheoperationbeingexecuted,soIdecidedtocheckthefirstbyteatEIP➊toseeifitwasequalto0xE8.Thisvaluetellstheprocessortoexecuteanearfunctioncall,whichisthetypeofcallIwaslookingfor.

Beforestartingmytrace,Ihadtodoonelastthing.Becausethetracefeaturerepeatedlysingle-steps(TraceintousesstepintoandTraceoverusesstepover,asdescribedin“ABriefLookatOllyDbg’sUserInterface”onpage24),Ineededtostartthetraceatalocationscopedatorabovethelevelofanycodethatcouldpossiblyupdatethehealthvalue.

FiguringOutWheretoStarttheTraceTofindagoodlocation,Iopenedthegame’smainmoduleinOllyDbg’s

CPUwindow,right-clickedinthedisassemblerpane,andselectedSearchfor▸Allintermodularcalls.TheReferenceswindowpoppedupanddisplayedalistofexternalAPIfunctionsthatwerecalledbythegame.NearlyallgamingsoftwarepollsfornewmessagesusingtheWindowsUSER32.PeekMessage()function,soIsortedthelistusingtheDestinationcolumnandtypedPEEK(youcansearchthelistbysimplytypinganamewiththewindowinfocus)tolocatethefirstcalltoUSER32.PeekMessage().

ThankstotheDestinationsorting,everycalltothisfunctionwaslistedinacontiguouschunkfollowingthefirst,asshowninFigure2-5.IsetabreakpointoneachbyselectingitandpressingF2.

Figure2-5:OllyDbg’sFoundintermodularcallswindow

ThoughtherewerearoundadozencallstoUSER32.PeekMessage(),onlytwoofthemweresettingoffmybreakpoints.Evenbetter,theactivecallswerebesideoneanotherinanunconditionalloop.Atthebottomofthisloopwereanumberofinternalfunctioncalls.Thislookedexactlylikeamaingameloop.

ActivatingtheTrace

Tofinallysetmytrace,Iremovedallofmypreviousbreakpointsandplacedoneatthetopofthesuspectedmainloop.Iremovedthebreakpointassoonasitwashit.IthenpressedCTRL-TfromtheCPUwindow,whichbroughtupadialogcalledConditiontopauseruntrace,showninFigure2-6.Withinthisnewdialog,IenabledtheConditionisTRUEoption,placedmyexpressionintheboxbesideit,andpressedOK.Then,IwentbacktotheCPUwindowandpressedCTRL-F11tobeginaTraceIntosession.

Figure2-6:Conditiontopauseruntracedialog

Oncethetracebegan,thegameransoslowlyitwasnearlyunplayable.Todecreasemytestcharacter’shealth,Iopenedasecondinstanceofthegame,loggedintoadifferentcharacter,andattackedmytestcharacter.Whentheexecutionofthetracecaughtuptorealtime,OllyDbgsawmyhealthchangeandtriggeredthebreakpointonthefollowingfunctioncall—justasexpected.

Inthisgame,themainpiecesofcodethatwouldmodifythehealthvalueweredirectlyinvokedfromthenetworkcode.Usingthistrace,Iwasabletofindthefunctionthatthenetworkmodulecalleddirectlyafteranetworkpackettoldthegametochangetheplayer’shealth.Here’sthepsuedocodeofwhatthegamewasdoing:

voidnetwork::check(){while(this->hasPacket()){packet=this->getPacket();if(packet.type==UPDATE_HEALTH){oldHealth=player->health;player->health=packet.getInteger();➊observe(HEALTH_CHANGE,oldHealth,player->health);}}}

Iknewthegamehadcodethatneededtoexecuteonlywhentheplayer’shealthwaschanged,andIneededtoaddcodethatcouldalsorespondtosuchchanges.Withoutknowingtheoverallcodestructure,Iguessedthatthehealth-dependentcodewouldbeexecutedfromsomefunctioncalldirectlyafterhealthwasupdated.Mytraceconditionalbreakpointconfirmedthishunch,asitbrokedirectlyontheobserve()function➊.Fromthere,Iwasabletoplaceahookonthefunction(hooking,awaytointerceptfunctioncalls,isdescribedin“HookingtoRedirectGameExecution”onpage153)andexecutemyowncodewhentheplayer’shealthchanged.

OllyDbgPlug-insforGameHackersOllyDbg’shighlyversatileplug-insystemisperhapsoneofitsmostpowerfulfeatures.ExperiencedgamehackersoftenconfiguretheirOllyDbgenvironmentswithdozensofusefulplug-ins,bothpubliclyavailableandcustom-made.

Youcandownloadpopularplug-insfromtheOpenRCE(http://www.openrce.org/downloads/browse/OllyDbg_Plugins)andtuts4you(http://www.tuts4you.com/download.php?list.9/)plug-inrepositories.Installingthemiseasy:justunziptheplug-infilesandplacetheminsideOllyDbg’sinstallationfolder.

Onceinstalled,someplug-inscanbeaccessedfromtheOllyDbg’sPluginmenuitem.Otherplug-ins,however,mightbefoundonlyinspecificplacesthroughouttheOllyDbginterface.

Youcanfindhundredsofpotentplug-insusingtheseonlinerepositories,butyoushouldbecarefulwhenconstructingyourarsenal.Workinginanenvironmentbloatedbyunusedplug-inscanactuallyimpedeproductivity.Inthissection,I’vecarefullyselectedfourplug-insthatIbelievearenotonly

thissection,I’vecarefullyselectedfourplug-insthatIbelievearenotonlyintegraltoagamehacker’stoolkitbutalsononinvasivetotheenvironment.

CopyingAssemblyCodewithAsm2ClipboardAsm2Clipboardisaminimalisticplug-infromtheOpenRCErepositorythatallowsyoutocopychunksofassemblycodefromthedisassemblerpanetotheclipboard.Thiscanbeusefulforupdatingaddressoffsetsanddevisingcodecaves,twogame-hackingessentialsIcoverdeeplyinChapters5and7.

WithAsm2Clipboardinstalled,youcanhighlightablockofassemblycodeinthedisassembler,right-clickthehighlightedcode,expandtheAsm2Clipboardsubmenu,andselecteitherCopyfixedAsmcodetoclipboardorCopyAsmcodetoclipboard.Thelatterprependsthecodeaddressofeachinstructionasacomment,whiletheformercopiesonlythepurecode.

AddingCheatEnginetoOllyDbgwithCheatUtilityTheCheatUtilityplug-infromtuts4youprovidesahighlyslimmed-downversionofCheatEnginewithinOllyDbg.WhileCheatUtilityonlyallowsyoutodoexact-valuescanswithaverylimitednumberofdatatypes,itcanmakesimplescansmucheasierwhenyoudon’tneedthefullfunctionalityofCheatEnginetofindwhatyou’relookingfor.AfterinstallingCheatUtility,toopenitsinterface(showninFigure2-7),selectPlugins▸Cheatutility▸Start.

Figure2-7:CheatUtilityinterface

CheatUtility’suserinterfaceandoperationmimicCheatEngineclosely,soreviewChapter1ifyouneedarefresher.

NOTE

GamesInvader,anupdatedversionofCheatUtilityalsofromtuts4you,wascreatedtoprovidemorefunctionality.I’vefounditbuggy,however,andIpreferCheatUtilitysinceIcanalwaysuseCheatEngineforadvancedscans.

ControllingOllyDbgThroughtheCommandLineThecommandlineplug-inenablesyoutocontrolOllyDbgthroughasmallcommandlineinterface.Toaccesstheplug-in,eitherpressALT-F1orselectPlugins▸Commandline▸Commandline.Youshouldthenseeawindow,showninFigure2-8,whichactsasthecommandlineinterface.

Figure2-8:Commandlineinterface

Toexecuteacommand,typeitintotheinputbox➊andpressENTER.Youwillseeasession-levelcommandhistoryinthecenterlist➋,andthebottomlabeldisplaysthecommand’sreturnvalue➌(ifany).

Thoughtherearemanycommandsavailable,Ifindamajorityofthemuseless.Iprimarilyusethistoolasawaytotestthatexpressionsareparsingasexpectedandasahandycalculator,butthereareafewadditionalusecasesthatarealsoworthmentioning.I’vedescribedtheseinTable2-6.

Table2-6:CommandLinePlug-inCommands

Command Function

BCidentifier

Removesanybreakpointspresentonidentifier,whichcanbeacodeaddressorAPIfunctionname.

BPidentifier[,condition]

Placesadebuggerbreakpointonidentifier,whichcanbeacodeaddressorAPIfunctionname.WhenidentifierisanAPIfunctionname,thebreakpointwillbeplacedonthefunctionentrypoint.Theconditionparameterisanoptionalexpressionthat,ifpresent,willbesetasthebreakpointcondition.

BPXlabel Placesadebuggerbreakpointoneveryinstanceoflabelwithinthemodulecurrentlybeingdisassembled.ThislabelwilltypicallybeanAPIfunctionname.

CALCexpression?expression

Evaluatesexpressionanddisplaystheresult.

HDaddress Removesanyhardwarebreakpointspresentonaddress.HEaddress Placesahardwareon-executebreakpointonaddress.HRaddress Placesahardwareon-accessbreakpointonaddress.Onlyfour

hardwarebreakpointscanexistatatime.HWaddress Placesahardwareon-writebreakpointonaddress.MD Removesanyexistingmemorybreakpoint,ifpresent.MRaddress1,address2

Placesamemoryon-accessbreakpointstartingataddress1andspanninguntiladdress2.Willreplaceanyexistingmemorybreakpoint.

MWaddress1,address2

Placesamemoryon-writebreakpointstartingataddress1and

spanninguntiladdress2.Willreplaceanyexistingmemorybreakpoint.

WATCHexpressionWexpression

OpenstheWatcheswindowandaddsexpressiontothewatchlist.Expressionsinthislistwillbereevaluatedeverytimetheprocessreceivesamessageandtheevaluationresultswillbedisplayedbesidethem.

Thecommandlineplug-inwasmadebytheOllyDbgdeveloperandshouldcomepreinstalledwithOllyDbg.

VisualizingControlFlowwithOllyFlowOllyFlow,whichcanbefoundintheOpenRCEplug-indirectory,isapurelyvisualplug-inthatcangeneratecodegraphsliketheoneinFigure2-9anddisplaythemusingWingraph32.

Figure2-9:AnOllyFlowfunctionflowchart

NOTE

Wingraph32isnotprovidedwithOllyFlow,butitisavailablewiththefreeversionofIDAhere:https://www.hex-rays.com/products/ida/.Downloaditanddropthe.exeinyourOllyDbginstallationfolder.

Thoughnotinteractive,thesegraphsallowyoutoeasilyidentifyconstructssuchasloopsandnestedif()statementsingamecode,whichcanbeparamountincontrolflowanalysis.WithOllyFlowinstalled,youcangenerateagraphbygoingtoPlugins▸OllyFlow(alternatively,right-clickinthedisassemblerpaneandexpandtheOllyFlowgraphsubmenu)andselectingoneofthefollowingoptions:

GeneratefunctionflowchartGeneratesagraphofthefunctioncurrentlyinscope,breakingapartdifferentcodeblocksandshowingjumppaths.Figure2-9showsafunctionflowchart.Withoutadoubt,thisisOllyFlow’smostusefulfeature.

GeneratexrefsfromgraphGeneratesagraphofallfunctionscalledbythefunctionthatiscurrentlyinscope.

GeneratexrefstographGeneratesagraphofallfunctionsthatcallthefunctioncurrentlyinscope.

GeneratecallstackgraphGeneratesagraphoftheassumedcallpathfromtheprocessentrypointtothefunctioncurrentlyinscope.

GeneratemodulegraphTheoreticallygeneratesacompletegraphofallfunctioncallsintheentiremodule,butrarelyactuallyworks.

TogetanideaoftheusefulnessofOllyFlow,takealookatthegraphinFigure2-9andcompareittotherelativelysimpleassemblyfunctionthatgeneratedit:

76f86878:➊MOVEAX,DWORDPTRDS:[76FE7E54]TESTAL,1JEntdll.76F8689B76f86881:➋MOVEAX,DWORDPTRFS:[18]MOVEAX,DWORDPTRDS:[EAX+30]ORDWORDPTRDS:[EAX+68],2000000

MOVEAX,DWORDPTRDS:[76FE66E0]ORDWORDPTRDS:[EAX],1JMPntdll.76F868B276f8689b:➌TESTEAX,8000JEntdll.76F868B276f868a2:➍MOVEAX,DWORDPTRFS:[18]MOVEAX,DWORDPTRDS:[EAX+30]ORDWORDPTRDS:[EAX+68],200000076f868b2:➎MOVAL,1RETN

TherearefiveboxesinFigure2-9,andtheymaptothefivepiecesofthisfunction.Thefunctionstartswith➊,anditfallsthroughto➋ifthebranchfailsorjumpsto➌ifitsucceeds.After➋executes,itjumpsdirectlytopiece➎,whichthenreturnsoutofthefunction.After➌executes,iteitherfallsthroughto➍orbranchesto➎toreturndirectly.After➍executes,itunconditionallyfallsthroughto➎.WhatthisfunctiondoesisirrelevanttounderstandingOllyFlow;fornow,justfocusonseeinghowthecodemapstothegraph.

PATCHINGANIF()STATEMENTIfyouthinkyou’rereadytogetyourhandsdirtywithOllyDbg,keepreading.Gotohttps://www.nostarch.com/gamehacking/,downloadthebook’sresourcefiles,grabBasicDebugging.exe,andexecuteit.Atfirstglance,you’llseethatitlooksliketheclassicgamePong.InthisversionofPong,theballisinvisibletoyouwhenitisonyouropponent’sscreen.Yourtaskistodisablethisfeaturesothatyoucanalwaysseetheball.Tomakeiteasierforyou,I’vemadethegameautonomous.Youdon’thavetoplay,onlyhack.

Tostart,attachOllyDbgtothegame.ThenfocustheCPUwindowonthemainmodule(findthe.exeinthemodulelistanddouble-clickit)andusetheReferencedtextstringsfeaturetolocatethestringthatisdisplayedwhentheballishidden.Next,double-clickthestringtobringitupinthecodeandanalyzethesurroundingcode

untilyoufindtheif()statementthatdetermineswhethertohidetheball.Lastly,usingthecode-patchingfeature,patchtheif()statementsotheballisalwaysdrawn.Asanaddedbonus,youmighttryusingOllyFlowtographthisfunctionsoyoucangetabetterunderstandingofwhatexactlyitisdoing.(Hint:Theif()statementcheckswhethertheball’sx-coordinateislessthan0x140.Ifso,itjumpstocodethatdrawstheball.Ifnot,itdrawsthescenewithouttheball.Ifyoucanchange0x140to,say,0xFFFF,theballwillnevergethidden.)

ClosingThoughtsOllyDbgisamuchmorecomplexbeastthanCheatEngine,butyou’lllearnbestbyusingit,sodiveinandgetyourhandsdirty!Youcanstartbypairingthecontrolstaughtinthischapterwithyourdebuggingskillsandgoingtoworkonsomerealgames.Ifyouarenotyetreadytotamperwithyourvirtualfate,however,trytacklingtheexamplein“Patchinganif()Statement”forapracticeenvironment.Whenyou’redone,readontoChapter3,whereI’llintroduceyoutoProcessMonitorandProcessExplorer,twotoolsyou’llfindinvaluableingame-hackingreconnaissance.

3RECONNAISSANCEWITHPROCESSMONITORANDPROCESSEXPLORER

CheatEngineandOllyDbgcanhelpyoutearapartagame’smemoryandcode,butyoualsoneedtounderstandhowthegameinteractswithfiles,registryvalues,networkconnections,andotherprocesses.Tolearnhowthoseinteractionswork,youmustusetwotoolsthatexcelatmonitoringtheexternalactionsofprocesses:ProcessMonitorandProcessExplorer.Withthesetools,youcantrackdownthecompletegamemap,locatesavefiles,identifyregistrykeysusedtostoresettings,andenumeratetheInternetProtocol(IP)addressesofremotegameservers.

Inthischapter,I’llteachyouhowtousebothProcessMonitorandProcessExplorertologsystemeventsandinspectthemtoseehowagamewasinvolved.Usefulmainlyforinitialreconnaissance,thesetoolsareamazingatgivingaclear,verbosepictureofexactlyhowagameinteractswithyoursystem.YoucandownloadbothprogramsfromtheWindowsSysinternalswebsite(https://technet.microsoft.com/en-us/sysinternals/).

ProcessMonitorYoucanlearnalotaboutagamesimplybyexploringhowitinteractswiththeregistry,filesystem,andnetwork.ProcessMonitorisapowerfulsystem-monitoringtoolthatlogssucheventsinrealtimeandletsyouseamlessly

monitoringtoolthatlogssucheventsinrealtimeandletsyouseamlesslyintegratethedataintoadebuggingsession.Thistoolprovidesextensiveamountsofusefuldataregardingagame’sinteractionwiththeexternalenvironment.Withcalculatedreview(andsometimes,spontaneousintuition)onyourpart,thisdatacanrevealdetailsaboutdatafiles,networkconnections,andregistryeventsthatarehelpfultoyourabilitytoseeandmanipulatehowthegamefunctions.

Inthissection,I’llshowyouhowtouseProcessMonitortologdata,navigateit,andmakeeducatedguessesaboutthefilesagameinteractswith.Afterthisinterfacetour,you’llhaveachancetotryoutProcessMonitorforyourselfin“FindingaHighScoreFile”onpage55.

LoggingIn-GameEventsProcessMonitor’slogscanholdallsortsofpotentiallyusefulinformation,buttheirmostpracticaluseistohelpyoufigureoutwheredatafiles,suchasin-gameitemdefinitions,mightbestored.WhenyoustartProcessMonitor,thefirstdialogyouseeistheProcessMonitorFilter,showninFigure3-1.

Figure3-1:ProcessMonitorFilterdialog

Thisdialogallowsyoutoshoworsuppresseventsbasedonanumberofdynamicpropertiestheypossess.Tostartmonitoringprocesses,selectProcessName▸Is▸YourGameFilename.exe▸Includeandthenpress

Add,Apply,andOK.ThistellsProcessMonitortoshoweventsinvokedbyYourGameFilename.exe.Withtheproperfiltersset,youwillbetakentothemainwindowshowninFigure3-2.

Figure3-2:ProcessMonitormainwindow

ToconfigurethecolumnsdisplayedinProcessMonitor’slogarea,right-clickontheheaderandchooseSelectColumns.There’sanimpressivenumberofoptions,butIrecommendseven.

TimeofDayLetsyouseewhenactionsarehappening.

ProcessNameIsusefulifyou’remonitoringmultipleprocesses,butwiththesingle-processfilterthatistypicallyusedforgames;disablingthisoptioncansavepreciousspace.

ProcessIDIslikeProcessName,butitshowstheIDratherthanthename.

OperationShowswhatactionwasperformed;thus,thisoptioniscompulsory.

PathShowsthepathoftheaction’starget;alsocompulsory.

DetailIsusefulonlyinsomecases,butenablingitwon’thurt.

ResultShowswhenactions,suchasloadingfiles,fail.

Asyoushowmorecolumns,thelogcangetverycrowded,butstickingwiththeseoptionsshouldhelpkeeptheoutputsuccinct.

withtheseoptionsshouldhelpkeeptheoutputsuccinct.Oncethemonitorisrunningandyou’vedefinedthecolumnsyouwishto

see,therearefiveeventclassfilters,outlinedinblackinFigure3-2,thatyoucantoggletocleanupyourlogsevenfurther.Eventclassfiltersletyouchoosewhicheventstoshowinthelog,basedontype.Fromlefttoright,thesefiltersareasfollows:

RegistryShowsallregistryactivity.Therewillbealotofwhitenoiseintheregistryuponprocesscreation,asgamesrarelyusetheregistryandWindowslibrariesalwaysuseit.Leavingthisfilterdisabledcansavealotofspaceinthelog.

FilesystemShowsallfilesystemactivity.Thisisthemostimportanteventclassfilter,sinceknowingwheredatafilesarestoredandhowtheyareaccessedisintegraltowritinganeffectivebot.

NetworkShowsallnetworkactivity.Thecallstackonnetworkeventscanbeusefulinfindingnetwork-relatedcodewithinagame.

ProcessandthreadactivityShowsallprocessandthreadactions.Thecallstackontheseeventscangiveyouinsightintohowagame’scodehandlesthreads.

ProcessprofilingPeriodicallyshowsinformationaboutthememoryandCPUusageofeachrunningprocess;agamehackerwillrarelyuseit.

Ifclass-leveleventfilteringisstillnotpreciseenoughtofilteroutunwantedpollutioninyourlogs,right-clickonspecificeventsforevent-levelfilteringoptions.Onceyouhaveyoureventfilteringconfiguredtologonlywhatyouneed,youcanbeginnavigatingthelog.Table3-1listssomeusefulhotkeysforcontrollingthelog’sbehavior.

Table3-1:ProcessMonitorHotkeys

HotkeyAction

CTRL-E Toggleslogging.

CTRL-A Togglesautomaticscrollingofthelog.

CTRL-X Clearsthelog.

CTRL-L DisplaystheFilterdialog.

CTRL- DisplaystheHighlightdialog.Thisdialoglooksverysimilartothe

CTRL-H

DisplaystheHighlightdialog.ThisdialoglooksverysimilartotheFilterdialog,butitisusedtoindicatewhicheventsshouldbehighlighted.

CTRL-F DisplaystheSearchdialog.

CTRL-P DisplaystheEventPropertiesdialogfortheselectedevent.

Asyounavigatethelog,youcanexaminetheoperationsrecordedtoseethefine-graineddetailsofanevent.

InspectingEventsintheProcessMonitorLogProcessMonitorlogseverydatapointitpossiblycanaboutanevent,enablingyoutolearnmoreabouttheseeventsthanjustthefilestheyactupon.Carefullyinspectingdata-richcolumns,suchasResultandDetail,canyieldsomeveryinterestinginformation.

Forexample,I’vefoundthatgamessometimesreaddatastructures,elementbyelement,directlyfromfiles.Thisbehaviorisapparentwhenalogcontainsalargenumberofreadstothesamefile,whereeachreadhassequentialoffsetsbutdifferinglengths.ConsiderthehypotheticaleventlogshowninTable3-2.

Table3-2:ExampleEventLog

OperationPath Detail

CreateFileC:\file.datDesiredAccess:Read

ReadFile C:\file.datOffset:0Size:4

ReadFile C:\file.datOffset:4Size:2

ReadFile C:\file.datOffset:6Size:2

ReadFile C:\file.datOffset:8Size:4

ReadFile C:\file.datOffset:12Size:4

... ... ...Continuestoreadchunksof4bytesforawhile

Thislogrevealsthatthegameisreadingastructurefromthefilepiecebypiece,disclosingsomehintsaboutwhatthestructurelookslike.Forexample,let’ssaythatthesereadsreflectthefollowingdatafile:

structmyDataFile{intheader;//4bytes(offset0)shorteffectCount;//2bytes(offset4)shortitemCount;//2bytes(offset6)int*effects;int*items;};

ComparetheloginTable3-2withthisstructure.First,thegamereadsthe4headerbytes.Then,itreadstwo2-bytevalues:effectCountanditemCount.Itthencreatestwointegerarrays,effectsanditems,ofrespectivelengthseffectCountanditemCount.Thegamethenfillsthesearrayswithdatafromthefile,reading4byteseffectCount+itemCounttimes.

NOTE

Developersdefinitelyshouldn’tuseaprocesslikethistoreaddatafromafile,butyou’dbeamazedathowoftenithappens.Fortunatelyforyou,naïvetélikethisjustmakesyouranalysiseasier.

Inthiscase,theeventlogcanidentifysmallpiecesofinformationwithinafile.Butkeepinmindthat,whilecorrelatingthereadswiththeknownstructureiseasy,it’smuchhardertoreverseengineeranunknownstructurefromnothingbutaneventlog.Typically,gamehackerswilluseadebuggertogetmorecontextabouteachinterestingevent,andthedatafromProcessMonitorcanbeseamlesslyintegratedintoadebuggingsession,effectivelytyingtogetherthetwopowerfulreverseengineeringparadigms.

DebuggingaGametoCollectMoreDataLet’sstepawayfromthishypotheticalfilereadandlookathowProcessMonitorletsyoutransitionfromeventloggingtodebugging.ProcessMonitorstoresacompletestacktraceforeachevent,showingthefullexecutionchainthatledtotheeventbeingtriggered.YoucanviewthesestacktracesintheStacktaboftheEventPropertieswindow(double-clicktheeventorpressCTRL-P),asshowninFigure3-3.

Figure3-3:ProcessMonitoreventcallstack

ThestacktraceisdisplayedinatablestartingwithaFramecolumn➊,whichshowstheexecutionmodeandstackframeindex.ApinkKinthiscolumnmeansthecallhappenedinkernelmode,whileablueUmeansithappenedinusermode.Sincegamehackerstypicallyworkinusermode,kernelmodeoperationsareusuallymeaningless.

TheModulecolumn➋showstheexecutablemodulewherethecallingcodewaslocated.Eachmoduleisjustthenameofthebinarythatmadethecall;thismakesiteasytoidentifywhichcallswereactuallymadefromwithinagamebinary.

TheLocationcolumn➌showsthenameofthefunctionthatmadeeachcall,aswellasthecalloffset.Thesefunctionnamesarededucedfromthe

exporttableofthemoduleandwillgenerallynotbepresentforthefunctionswithinagamebinary.Whennofunctionnamesarepresent,theLocationcolumninsteadshowsthemodulenameandthecall’soffset(howmanybytespasttheoriginaddressthecallisinmemory)fromthemodule’sbaseaddress.

NOTE

Inthecontextofcode,theoffsetishowmanybytesofassemblycodearebetweenanitemanditsorigin.

TheAddresscolumn➍showsthecodeaddressofthecall,whichisveryusefulbecauseyoucanjumptotheaddressintheOllyDbgdisassembler.Finally,thePathcolumn➎showsthepathtothemodulethatmadethecall.

Inmyopinion,thestacktraceis,byfar,themostpowerfulfeatureinProcessMonitor.Itrevealstheentirecontextthatledtoanevent,whichcanbeimmenselyusefulwhenyouaredebuggingagame.Youcanuseittofindtheexactcodethattriggeredanevent,crawlupthecallchaintoseehowitgotthere,andevendetermineexactlywhatlibrarieswereusedtocompleteeachaction.

ProcessMonitor’ssisterapplication,ProcessExplorer,doesn’thavemanycapabilitiesbeyondthoseinProcessMonitororOllyDbg.Butitdoesexposesomeofthosecapabilitiesmuchmoreeffectively,makingitanidealpickincertainsituations.

FINDINGAHIGHSCOREFILEIfyou’rereadytotestyourProcessMonitorskills,you’vecometotherightplace.OpentheGameHackingExamples/Chapter3_FindingFilesdirectoryandexecuteFindingFiles.exe.You’llseethatitisagameofPong,liketheonein“Patchinganif()Statement”onpage46.UnlikeinChapter2,though,nowthegameisactuallyplayable.Italsodisplaysyourcurrentscoreandyourall-time-highscore.

Nowrestartthegame,firingupProcessMonitorbeforeexecutingitforthesecondtime.Filteringforfilesystemactivityandcreatinganyotherfiltersyouseefit,trytolocatewherethegamestoresthe

anyotherfiltersyouseefit,trytolocatewherethegamestoresthehigh-scorefile.Forbonuspoints,trytomodifythisfiletomakethegameshowthehighestpossiblescore.

ProcessExplorerProcessExplorerisanadvancedtaskmanager(itevenhasabuttonyoucanpresstomakeityourdefaulttaskmanager),andit’sveryhandywhenyou’restartingtounderstandhowagameoperates.Itprovidescomplexdataaboutrunningprocesses,suchasparentandchildprocesses,CPUusage,memoryusage,loadedmodules,openhandles,andcommandlinearguments,anditcanmanipulatethoseprocesses.Itexceedsatshowingyouhigh-levelinformation,suchasprocesstrees,memoryconsumption,fileaccess,andprocessIDs,allofwhichcanbeveryuseful.

Ofcourse,noneofthisdataisspecificallyusefulinisolation.Butwithakeeneye,youcanmakecorrelationsanddrawsomeusefulconclusionsaboutwhatglobalobjects—includingfiles,mutexes,andsharedmemorysegments—agamehasaccessto.Additionally,thedatashowninProcessExplorercanbeevenmorevaluablewhencross-referencedwithdatagatheredinadebuggingsession.

ThissectionintroducestheProcessExplorerinterface,discussesthepropertiesitshows,anddescribeshowyoucanusethistooltomanipulatehandles(referencestosystemresources).Afterthisintroduction,use“FindingandClosingaMutex”onpage60tohoneyourskills.

ProcessExplorer’sUserInterfaceandControlsWhenyouopenProcessExplorer,youseeawindowthatissplitintothreedistinctsections,asinFigure3-4.

Figure3-4:ProcessExplorermainwindow

Thosethreesectionsarethetoolbar➊,anupperpane➋,andalowerpane➌.Theupperpaneshowsalistofprocesses,utilizingatreestructuretodisplaytheirparent/childrelationships.Differentprocessesarehighlightedwithdifferentcolors;ifyoudon’tlikethecurrentcolors,clickOptions▸ConfigureColorstodisplayadialogthatallowsyoutoviewandchangethem.

JustasinProcessMonitor,thedisplayforthistableishighlyversatile,andyoucancustomizeitbyright-clickingonthetableheaderandchoosingSelectColumns.Thereareprobablymorethan100customizationoptions,butIfindthatthedefaultswiththeadditionoftheASLREnabledcolumnworkjustfine.

NOTE

AddressSpaceLayoutRandomization(ASLR)isaWindowssecurity

featurethatallocatesexecutableimagesatunpredictablelocations,andknowingwhetherit’sonisinvaluablewhenyou’retryingtoaltergamestatevaluesinmemory.

Thelowerpanehasthreepossiblestates:Hidden,DLLs,andHandles.TheHiddenoptionhidesthepanefromview,DLLsdisplaysalistofDynamicLinkLibrariesloadedwithinthecurrentprocess,andHandlesshowsalistofhandlesheldbytheprocess(visibleinFigure3-4).YoucanhideorunhidetheentirelowerpanebytogglingView▸ShowLowerPane.Whenitisvisible,youcanchangetheinformationdisplaybyselectingeitherView▸LowerPaneView▸DLLsorView▸LowerPaneView▸Handles.

Youcanalsousehotkeystoquicklychangebetweenlowerpanemodeswithoutaffectingprocessesintheupperpane.ThesehotkeysarelistedinTable3-3.

Table3-3:ProcessExplorerHotkeys

Hotkey Action

CTRL-F Searchthroughlowerpanedatasetsforavalue.

CTRL-L Togglethelowerpanebetweenhiddenandvisible.

CTRL-D TogglethelowerpanetodisplayDLLs.

CTRL-H Togglethelowerpanetodisplayhandles.

spacebar Toggleprocesslistautorefresh.ENTER DisplaythePropertiesdialogfortheselectedprocess.DEL Killtheselectedprocess.

SHIFT-DELKilltheselectedprocessandallchildprocesses.

UsetheGUIorhotkeystopracticechangingmodes.Whenyou’reacquaintedwiththemainwindow,we’lllookatanotherimportantProcessExplorerdialog,calledProperties.

ExaminingProcessPropertiesMuchlikeProcessMonitor,ProcessExplorerhasaverykineticapproachto

datagathering;theendresultisabroadandverbosespectrumofinformation.Infact,ifyouopenthePropertiesdialog(showninFigure3-5)foraprocess,you’llseeamassivetabbarcontaining10tabs.

TheImagetab,selectedbydefaultandshowninFigure3-5,displaystheexecutablename,version,builddate,andcompletepath.ItalsodisplaysthecurrentworkingdirectoryandtheAddressSpaceLayoutRandomizationstatusoftheexecutable.ASLRstatusisthemostimportantpieceofinformationhere,becauseithasadirecteffectonhowabotcanreadthememoryfromagame.I’lltalkaboutthismoreinChapter6.

Figure3-5:ProcessExplorerPropertiesdialog

ThePerformance,PerformanceGraph,DiskandNetwork,andGPUGraphtabsdisplayamyriadofmetricsabouttheCPU,memory,disk,network,andGPUusageoftheprocess.Ifyoucreateabotthatinjectsintoa

network,andGPUusageoftheprocess.Ifyoucreateabotthatinjectsintoagame,thisinformationcanbeveryusefultodeterminehowmuchofaperformanceimpactyourbothasonthegame.

TheTCP/IPtabdisplaysalistofactiveTCPconnections,whichyoucanusetofindanygameserverIPaddressesthatagameconnectsto.Ifyou’retryingtotestconnectionspeed,terminateconnections,orresearchagame’snetworkprotocol,thisinformationiscritical.

TheStringstabdisplaysalistofstringsfoundineitherthebinaryorthememoryoftheprocess.UnlikethestringlistinOllyDbg,whichshowsonlystringsreferencedbyassemblycode,thelistincludesanyoccurrencesofthreeormoreconsecutivereadablecharacters,followedbyanullterminator.Whenagamebinaryisupdated,youcanuseadiffingtoolonthislistfromeachgameversiontodeterminewhetherthereareanynewstringsthatyouwanttoinvestigate.

TheThreadstabshowsyoualistofthreadsrunningwithintheprocessandallowsyoutopause,resume,orkilleachthread;theSecuritytabdisplaysthesecurityprivilegesoftheprocess;andtheEnvironmenttabdisplaysanyenvironmentvariablesknowntoorsetbytheprocess.

NOTE

IfyouopenthePropertiesdialogfora.NETprocess,you’llnoticetwoadditionaltabs:.NETAssembliesand.NETPerformance.Thedatainthesetabsisprettyself-explanatory.Pleasekeepinmindthatamajorityofthetechniquesinthisbookwon’tworkwithgameswrittenin.NET.

HandleManipulationOptionsAsyou’veseen,ProcessExplorercanprovideyouwithawealthofinformationaboutaprocess.That’snotallit’sgoodfor,though:itcanalsomanipulatecertainpartsofaprocess.Forexample,youcanviewandmanipulateopenhandlesfromthecomfortofProcessExplorer’slowerpane(seeFigure3-4).ThisalonemakesastrongargumentforaddingProcessExplorertoyourtoolbox.Closingahandleisassimpleasright-clickingonitandselectingCloseHandle.Thiscancomeinhandywhenyouwant,forinstance,toclosemutexes,whichisessentialtocertaintypesofhacks.

NOTE

Youcanright-clickonthelowerpaneheaderandclickSelectColumnstocustomizethedisplay.OnecolumnyoumightfindparticularlyusefulisHandleValue,whichcanhelpwhenyouseeahandlebeingpassedaroundinOllyDbgandwanttoknowwhatitdoes.

ClosingMutexesGamesoftenallowonlyoneclienttorunatatime;thisiscalledsingle-instancelimitation.Youcanimplementsingle-instancelimitationinanumberofways,butusingasystemmutexiscommonbecausemutexesaresessionwideandcanbeaccessedbyasimplename.It’strivialtolimitinstanceswithmutexes,andthankstoProcessExplorer,it’sjustastrivialtoremovethatlimit,allowingyoutorunmultipleinstancesofagameatthesametime.

First,here’showagamemighttacklesingle-instancelimitationwithamutex:

intmain(intargc,char*argv[]){//createthemutexHANDLEmutex=CreateMutex(NULL,FALSE,"onlyoneplease");if(GetLastError()==ERROR_ALREADY_EXISTS){//themutexalreadyexists,soexitErrorBox("Aninstanceisalreadyrunning.");return0;}//themutexdidn'texist;itwasjustcreated,so//letthegamerunRunGame();//thegameisover;closethemutextofreeitup//forfutureinstancesif(mutex)CloseHandle(mutex);return0;}

Thisexamplecodecreatesamutexnamedonlyoneplease.Next,thefunctionchecksGetLastError()toseewhetherthemutexwasalreadycreated,andifso,itclosesthegame.Ifthemutexdoesn’talreadyexist,thegamecreatesthefirstinstance,therebyblockinganyfuturegameclientsfromrunning.Inthisexample,thegamerunsnormally,andonceitfinishes,

CloseHandle()iscalledtoclosethemutexandallowfuturegameinstancestorun.

YoucanuseProcessExplorertocloseinstance-limitingmutexesandrunmanygameinstancessimultaneously.Todoso,choosetheHandlesviewofthelowerpane,lookforallhandleswithatypeofMutant,determinewhichoneislimitinginstancesofthegame,andclosethatmutex.

WARNING

Mutexesarealsousedtosynchronizedataacrossthreadsandprocesses.Closeoneonlyifyou’resurethatitssolepurposeistheoneyou’retryingtosubvert!

Multiclienthacksaregenerallyinhighdemand,sobeingabletoquicklydevelopthemforemerginggamesiscrucialtoyouroverallsuccessasabotdeveloperwithinthatmarket.Sincemutexesareoneofthemostcommonwaystoachievesingle-instancelimitation,ProcessExplorerisanintegraltoolforprototypingthesekindsofhacks.

InspectingFileAccessesUnlikeProcessMonitor,ProcessExplorercan’tshowalistoffilesystemcalls.Ontheotherhand,theHandlesviewofProcessExplorer’slowerpanecanshowallfilehandlesthatagamecurrentlyhasopen,revealingexactlywhatfilesareincontinuoususewithouttheneedtosetupadvancedfilteringcriteriainProcessMonitor.JustlookforhandleswithatypeofFiletoseeallfilesthegameiscurrentlyusing.

Thisfunctionalitycancomeinhandyifyou’retryingtolocatelogfilesorsavefiles.Moreover,youcanlocatenamedpipesthatareusedforinterprocesscommunication(IPC);thesearefilesprefixedwith\Device\NamedPipe\.Seeingoneofthesepipesisoftenahintthatthegameistalkingtoanotherprocess.

FINDINGANDCLOSINGAMUTEXToputyourProcessExplorerskillstouse,gototheGameHackingExamples/Chapter3_CloseMutexdirectoryandexecute

CloseMutex.exe.Thisgameplaysexactlyliketheonein“FindingaHighScoreFile”onpage55,butitpreventsyoufromsimultaneouslyrunningmultipleinstances.Asyoumighthaveguessed,itdoesthisusingasingle-instance-limitationmutex.UsingProcessExplorer’sHandlesviewinthelowerpane,findthemutexresponsibleforthislimitationandcloseit.Ifyousucceed,you’llbeabletoopenasecondinstanceofthegame.

ClosingThoughtsTobeeffectivewhenusingProcessMonitorandProcessExplorer,youneed,aboveallelse,adeepfamiliaritywiththedatathattheseapplicationsdisplayaswellastheinterfacestheyusetodisplayit.Whilethischapter’soverviewisagoodbaseline,theintricaciesoftheseapplicationscanbelearnedonlythroughexperience,soIencourageyoutoplayaroundwiththemonyoursystem.

Youwon’tusethesetoolsonaregularbasis,butatsomepoint,they’llsavetheday:asyoustruggletofigureouthowsomecodeworks,you’llrecallanobscurepieceofinformationthatcaughtyoureyeduringapreviousProcessExplorerorProcessMonitorsession.That’swhyIconsiderthemusefulreconnaissancetools.

PART2GAMEDISSECTION

4FROMCODETOMEMORY:AGENERAL

PRIMER

Atthelowestlevel,agame’scode,data,input,andoutputarecomplexabstractionsoferraticallychangingbytes.Manyofthesebytesrepresentvariablesormachinecodegeneratedbyacompilerthatwasfedthegame’ssourcecode.Somerepresentimages,models,andsounds.Othersexistonlyforaninstant,postedbythecomputer’shardwareasinputanddestroyedwhenthegamefinishesprocessingthem.Thebytesthatremaininformtheplayerofthegame’sinternalstate.Buthumanscan’tthinkinbytes,sothecomputermusttranslatetheminawaywecanunderstand.

There’sahugedisconnectintheoppositedirectionaswell.Acomputerdoesn’tactuallyunderstandhigh-levelcodeandvisceralgamecontent,sothesemustbetranslatedfromtheabstractintobytes.Somecontent—suchasimages,sounds,andtext—isstoredlosslessly,readytobepresentedtotheplayeratamicrosecond’snotice.Agame’scode,logic,andvariables,ontheotherhand,arestrippedofallhumanreadabilityandcompileddowntomachinedata.

Bymanipulatingagame’sdata,gamehackersobtainhumanlyimprobableadvantageswithinthegame.Todothis,however,theymustunderstandhowadeveloper’scodemanifestsonceithasbeencompiledandexecuted.Essentially,theymustthinklikecomputers.

Togetyouthinkinglikeacomputer,thischapterwillbeginbyteachingyouhownumbers,text,simplestructures,andunionsarerepresentedin

youhownumbers,text,simplestructures,andunionsarerepresentedinmemoryatthebytelevel.Thenyou’lldivedeepertoexplorehowclassinstancesarestoredinmemoryandhowabstractinstancesknowwhichvirtualfunctionstocallatruntime.Inthelasthalfofthechapter,you’lltakeanx86assemblylanguagecrashcoursethatcoverssyntax,registers,operands,thecallstack,arithmeticoperations,branchingoperations,functioncalls,andcallingconventions.

Thischapterfocusesveryheavilyongeneraltechnicaldetails.Thereisn’talotofjuicyinformationthatimmediatelyrelatestohackinggames,buttheknowledgeyougainherewillbecentralinthecomingchapters,whenwetalkabouttopicslikeprogrammaticallyreadingandwritingmemory,injectingcode,andmanipulatingcontrolflow.

SinceC++isthedefactostandardforbothgameandbotdevelopment,thischapterexplainstherelationshipsbetweenC++codeandthememorythatrepresentsit.Mostnativelanguageshaveverysimilar(sometimesidentical)low-levelstructureandbehavior,however,soyoushouldbeabletoapplywhatyoulearnheretojustaboutanypieceofsoftware.

AlloftheexamplecodeinthischapterisintheGameHackingExamples/Chapter4_CodeToMemorydirectoryofthisbook’ssourcefiles.TheincludedprojectscanbecompiledwithVisualStudio2010butshouldalsoworkwithanyotherC++compiler.Downloadthemathttps://www.nostarch.com/gamehacking/andcompilethemifyouwanttofollowalong.

HowVariablesandOtherDataManifestinMemoryProperlymanipulatingagame’sstatecanbeveryhard,andfindingthedatathatcontrolsitisnotalwaysaseasyasclickingNextScanandhopingCheatEnginewon’tfailyou.Infact,manyhacksmustmanipulatedozensofrelatedvaluesatonce.Findingthesevaluesandtheirrelationshipsoftenrequiresyoutoanalyticallyidentifystructuresandpatterns.Moreover,developinggamehackstypicallymeansre-creatingtheoriginalstructureswithinyourbot’scode.

Todothesethings,youneedanin-depthunderstandingofexactlyhowvariablesanddataarelaidoutinthegame’smemory.Throughexample

variablesanddataarelaidoutinthegame’smemory.Throughexamplecode,OllyDbgmemorydumps,andsometablestotieeverythingtogether,thissectionwillteachyoueverythingthereistoknowabouthowdifferenttypesofdatamanifestinmemory.

NumericDataMostofthevaluesgamehackersneed(liketheplayer’shealth,mana,location,andlevel)arerepresentedbynumericdatatypes.Becausenumericdatatypesarealsoabuildingblockforallotherdatatypes,understandingthemisextremelyimportant.Luckily,theyhaverelativelystraightforwardrepresentationsinmemory:theyarepredictablyalignedandhaveafixedbitwidth.Table4-1showsthefivemainnumericdatatypesyou’llfindinWindowsgames,alongwiththeirsizesandranges.

Table4-1:NumericDataTypes

Typename(s)

SizeSignedrange Unsignedrange

char,BYTE 8bits

-128to127 0to255

short,WORD,wchar_t

16bits

-32,768to-32,767 0to65535

int,long,DWORD

32bits

-2,147,483,648to2,147,483,647

0to4,294,967,295

longlong 64bits

-9,223,372,036,854,775,808to9,223,372,036,854,775,807

0to18,446,744,073,709,551,615

float 32bits

+/-1.17549*10-38to+/-3.40282*1038

N/A

Thesizesofnumericdatatypescandifferbetweenarchitecturesandevencompilers.Sincethisbookfocusesonhackingx86gamesonWindows,I’musingtypenamesandsizesmadestandardbyMicrosoft.Withtheexceptionoffloat,thedatatypesinTable4-1arestoredwithlittle-endianordering,

meaningtheleastsignificantbytesofanintegerarestoredinthelowestaddressesoccupiedbythatinteger.Forexample,Figure4-1showsthatDWORD0x0A0B0C0Disrepresentedbythebytes0x0D0x0C0x0B0x0A.

Figure4-1:Little-endianorderingdiagram

Thefloatdatatypecanholdmixednumbers,soitsrepresentationinmemoryisn’tassimpleasthatofotherdatatypes.Forexample,ifyousee0x0D0x0C0x0B0x0Ainmemoryandthatvalueisafloat,youcan’tsimplyconvertitto0x0A0B0C0D.Instead,floatvalueshavethreecomponents:thesign(bit0),exponent(bits1–8),andmantissa(bits9–31).

Thesigndetermineswhetherthenumberisnegativeorpositive,theexponentdetermineshowmanyplacestomovethedecimalpoint(startingbeforethemantissa),andthemantissaholdsanapproximationofthevalue.Youcanretrievethestoredvaluebyevaluatingtheexpressionmantissa×10n

(wherenistheexponent)andmultiplyingtheresultby–1ifthesignisset.Nowlet’slookatsomenumericdatatypesinmemory.Listing4-1

initializesninevariables.

unsignedcharubyteValue=0xFF;charbyteValue=0xFE;unsignedshortuwordValue=0x4142;shortwordValue=0x4344;unsignedintudwordValue=0xDEADBEEF;intdwordValue=0xDEADBEEF;unsignedlonglongulongLongValue=0xEFCDAB8967452301;longlonglongLongValue=0xEFCDAB8967452301;floatfloatValue=1337.7331;

Listing4-1:CreatingvariablesofnumericdatatypesinC++

Startingfromthetop,thisexampleincludesvariablesoftypeschar,short,int,longlong,andfloat.Fouroftheseareunsigned,andfivearesigned.(InC++,afloatcan’tbeunsigned.)Takingintoaccountwhatyou’velearnedsofar,carefullystudytherelationshipbetweenthecodeinListing4-1andthememorydumpinFigure4-2.Assumethatthevariablesaredeclaredinglobalscope.

Figure4-2:OllyDbgmemorydumpofournumericdata

Youmightnoticethatsomevaluesseemarbitrarilyspacedout.Sinceit’smuchfasterforprocessorstoaccessvaluesresidingataddressesthataremultiplesoftheaddresssize(whichis32bitsinx86),compilerspadvalueswithzerosinordertoalignthemonsuchaddresses—hence,paddingisalsocalledalignment.Single-bytevaluesarenotpadded,sinceoperationsthataccessthemperformthesameregardlessofalignment.

Keepingthisinmind,takealookatTable4-2,whichprovidesasortofmemory-to-codecrosswalkbetweenthememorydumpinFigure4-2andthevariablesdeclaredinListing4-1.

Table4-2:Memory-to-CodeCrosswalkforListing4-1andFigure4-2

Address Size Data Object

0x00BB3018 1byte

0xFF ubyteValue

0x00BB3019 1byte

0xFE byteValue

0x00BB301A 2bytes

0x000x00 PaddingbeforeuwordValue

0x00BB301C 2bytes

0x420x41 uwordValue

0x00BB301E 2bytes

0x000x00 PaddingbeforewordValue

bytes wordValue

0x00BB3020 2bytes

0x440x43 wordValue

0x00BB3022 2bytes

0x000x00 PaddingbeforeudwordValue

0x00BB3024 4bytes

0xEF0xBE0xAD0xDE udwordValue

0x00BB3028 4bytes

0xEF0xBE0xAD0xDE dwordValue

0x00BB302C 4bytes

0x760x370xA70x44 floatValue

0x00BB3030 8bytes

0x010x230x450x670x890xAB0xCD0xEF

ulongLongValue

0x00BB3038 8bytes

0x010x230x450x670x890xAB0xCD0xEF

LongLongValue

TheAddresscolumnlistslocationsinmemory,andtheDatacolumntellsyouexactlywhat’sstoredthere.TheObjectcolumntellsyouwhichvariablefromListing4-1eachpieceofdatarelatesto.NoticethatfloatValueisplacedbeforeulongLongValueinmemory,eventhoughit’sthelastvariabledeclaredinListing4-1.Becausethesevariablesaredeclaredinglobalscope,thecompilercanplacethemwhereveritwants.Thisparticularmoveislikelyaresultofeitheralignmentoroptimization.

StringDataMostdevelopersusethetermstringasifit’ssynonymouswithtext,buttextisonlythemostcommonuseforstrings.Atalowlevel,stringsarejustarraysofarbitrarynumericobjectsthatappearlinearandunalignedinmemory.Listing4-2showsfourtextstringdeclarations.

//charwillbe1bytepercharacterchar*thinStringP="my_thin_terminated_value_pointer";charthinStringA[40]="my_thin_terminated_value_array";

//wchar_twillbe2bytespercharacter

//wchar_twillbe2bytespercharacterwchar_t*wideStringP=L"my_wide_terminated_value_pointer";wchar_twideStringA[40]=L"my_wide_terminated_value_array";

Listing4-2:DeclaringseveralstringsinC++

Inthecontextoftext,stringsholdcharacterobjects(charfor8-bitencodingorwchar_tfor16-bitencoding),andtheendofeachstringisspecifiedbyanullterminator,acharacterequalto0x0.Let’slookatthememorywherethesevariablesarestored,asshowninthetwomemorydumpsinFigure4-3.

Figure4-3:InthisOllyDbgmemorydumpofstringdata,thehuman-readabletextintheASCIIcolumnisthetextwestoredinListing4-2.

Ifyou’renotusedtoreadingmemory,theOllyDbgdumpmightbeabitdifficulttofollowatthispoint.Table4-3showsadeeperlookatthecorrelationbetweenthecodeinListing4-2andthememoryinFigure4-3.

Table4-3:Memory-to-CodeCrosswalkforListing4-2andFigure4-3

Address Size Data Object

Pane1

0x012420F8 32bytes

0x6D0x790x5F{...}0x740x650x72

thinStringPcharacters

0x01242118 4bytes

0x000x000x000x00 thinStringPterminatorandpadding

0x0124211C 4bytes

0x000x000x000x00 Unrelateddata

0x01242120 64bytes

0x6D0x000x79{...}0x000x720x00

wideStringPcharacters

0x01242160 4bytes

0x000x000x000x00 wideStringPterminatorandpadding

{...} Unrelateddata

Pane2

0x01243040 4bytes

0xF80x200x240x01 PointertothinStringPat0x012420F8

0x01243044 30bytes

0x6D0x790x5F{...}0x720x610x79

thinStringAcharacters

0x01243062 10bytes

0x00repeated10times thinStringAterminatorandarrayfill

0x0124306C 4bytes

0x200x210x240x01 PointertowideStringPat0x01242120

0x01243070 60bytes

0x6D0x000x79{...}0x000x790x00

wideStringAcharacters

0x012430AC 20bytes

0x00repeated10times wideStringAterminatorandarrayfill

InFigure4-3,pane1showsthatthevaluesstoredwherethinStringP(address0x01243040)andwideStringP(address0x0124306C)belonginmemoryareonly4byteslongandcontainnostringdata.That’sbecausethesevariablesareactuallypointerstothefirstcharactersoftheirrespectivearrays.Forexample,thinStringPcontains0x012420F8,andinpane2inFigure4-3,youcansee"my_thin_terminated_value_pointer"locatedataddress0x012420F8.

Lookatthedatabetweenthesepointersinpane1,andyoucanseethetextbeingstoredbythinStringAandwideStringA.Furthermore,noticethatthinStringAandwideStringAarepaddedbeyondtheirnullterminators;thisisbecausethesevariablesweredeclaredasarrayswithlength40,sotheyarefilledupto40characters.

DataStructuresUnlikethedatatypeswehavepreviouslydiscussed,structuresarecontainersthatholdmultiplepiecesofsimple,relateddata.Gamehackerswhoknowhowtoidentifystructuresinmemorycanmimicthosestructuresintheirowncode.Thiscangreatlyreducethenumberofaddressestheymustfind,astheyneedtofindonlytheaddresstothestartofthestructure,nottheaddressofeveryindividualitem.

NOTE

Thissectiontalksaboutstructuresassimplecontainersthatlackmemberfunctionsandcontainonlysimpledata.Objectsthatexceedtheselimitationswillbediscussedin“ClassesandVFTables”onpage74.

StructureElementOrderandAlignmentSincestructuressimplyrepresentanassortmentofobjects,theydon’tvisiblymanifestinmemorydumps.Instead,amemorydumpofastructureshowstheobjectsthatarecontainedwithinthatstructure.ThedumpwouldlookmuchliketheothersI’veshowninthischapter,butwithimportantdifferencesinbothorderandalignment.

Toseethesedifferences,startbytakingalookatListing4-3.

structMyStruct{unsignedcharubyteValue;charbyteValue;unsignedshortuwordValue;shortwordValue;unsignedintudwordValue;intdwordValue;unsignedlonglongulongLongValue;longlonglongLongValue;floatfloatValue;};MyStruct&m=0;printf("Offsets:%d,%d,%d,%d,%d,%d,%d,%d,%d\n",&m->ubyteValue,&m->byteValue,&m->uwordValue,&m->wordValue,&m->udwordValue,&m->dwordValue,&m->ulongLongValue,&m->longLongValue,&m->floatValue);

Listing4-3:AC++structureandsomecodethatusesit

ThiscodedeclaresastructurenamedMyStructandcreatesavariablenamedmthatsupposedlypointstoaninstanceofthestructureataddress0.There’snotactuallyaninstanceofthestructureataddress0,butthistrickletsmeusetheampersandoperator(&)intheprintf()calltogettheaddressofeachmemberofthestructure.Sincethestructureislocatedataddress0,theaddressprintedforeachmemberisequivalenttoitsoffsetfromthestartofthestructure.

Theultimatepurposeofthisexampleistoseeexactlyhoweachmemberislaidoutinmemory,relativetothestartofthestructure.Ifyouweretorunthecode,you’dseethefollowingoutput:

Offsets:0,1,2,4,8,12,16,24,32

Asyoucansee,thevariablesinMyStructareorderedexactlyastheyweredefinedincode.Thissequentialmemberlayoutisamandatorypropertyofstructures.ComparethistotheexamplefromListing4-1,whenwedeclaredanidenticalsetofvariables;inthememorydumpfromFigure4-2,thecompilerclearlyplacedsomevaluesoutoforderinmemory.

Furthermore,youmayhavenoticedthatthemembersarenotalignedlikethegloballyscopedvariablesinListing4-1;iftheywere,forexample,therewouldbe2paddingbytesbeforeuwordValue.Thisisbecausestructuremembersarealignedonaddressesdivisiblebyeitherthestructmemberalignment(acompileroptionthataccepts1,2,4,8,or16bytes;inthisexample,it’ssetto4)orthesizeofthemember—whicheverissmaller.IarrangedthemembersofMyStructsothatthecompilerdidn’tneedtopadthevalues.

If,however,weputacharimmediatelyafterulongLongValue,theprintf()callwouldgivethefollowingoutput:

Offsets:0,1,2,4,8,12,16,28,36

Now,takealookattheoriginalandthemodifiedoutputstogether:

Original:Offsets:0,1,2,4,8,12,16,24,32Modified:Offsets:0,1,2,4,8,12,16,28,36

Inthemodifiedversion,thelasttwovalues,whicharetheoffsetsfor

longLongValueandfloatValuefromthestartofthestructure,havechanged.Thankstothestructmemberalignment,thevariablelongLongValuemovesby4bytes(1forthecharvalueand3followingit)toensureitgetsplacedonanaddressdivisibleby4.

HowStructuresWorkUnderstandingstructures—howtheyarealignedandhowtomimicthem—canbeveryuseful.Forinstance,ifyoureplicateagame’sstructuresinyourowncode,youcanreadorwritethoseentirestructuresfrommemoryinasingleoperation.Consideragamethatdeclarestheplayer’scurrentandmaxhealthlikeso:

struct{intcurrent;intmax;}vital;vitalhealth;

Ifaninexperiencedgamehackerwantstoreadthisinformationfrommemory,theymightwritesomethinglikethistofetchthehealthvalues:

intcurrentHealth=readIntegerFromMemory(currentHealthAddress);intmaxHealth=readIntegerFromMemory(maxHealthAddress);

Thisgamehackerdoesn’trealizethatseeingthesevaluesrightnexttoeachotherinmemorycouldbemorethanaluckyhappenstance,sothey’veusedtwoseparatevariables.Butifyoucamealongwithyourknowledgeofstructures,youmightconcludethat,sincethesevaluesarecloselyrelatedandareadjacentinmemory,ourhackercouldhaveusedastructureinstead:

struct{intcurrent;intmax;}_vital;➊_vitalhealth=readTypeFromMemory<_vital>(healthStructureAddress);

Sincethiscodeassumesastructureisbeingusedandcorrectlymimicsit,itcanfetchbothhealthandmaxhealthinjustoneline➊.We’lldivedeeperintohowtowriteyourowncodetoreadmemoryfrominChapter6.

UnionsUnlikestructures,whichencapsulatemultiplepiecesofrelateddata,unionscontainasinglepieceofdatathatisexposedthroughmultiplevariables.Unionsfollowthreerules:

•Thesizeofaunioninmemoryisequaltothatofitslargestmember.

•Membersofaunionallreferencethesamememory.

•Aunioninheritsthealignmentofitslargestmember.

Theprintf()callinthefollowingcodehelpsillustratethefirsttworules:

union{BYTEbyteValue;struct{WORDfirst;WORDsecond;}words;DWORDvalue;}dwValue;dwValue.value=0xDEADBEEF;printf("Size%d\nAddresses0x%x,0x%x\nValues0x%x,0x%x\n",sizeof(dwValue),&dwValue.value,&dwValue.words,dwValue.words.first,dwValue.words.second);

Thiscalltoprintf()outputsthefollowing:

Size4Addresses0x2efda8,0x2efda8Values0xbeef,0xdead

ThefirstruleisillustratedbytheSizevalue,whichisprintedfirst.EventhoughdwValuehasthreemembersthatoccupyatotalof9bytes,ithasasizeofonly4bytes.Thesizeresultvalidatesthesecondruleaswell,becausedwValue.valueanddwValue.wordsbothpointtoaddress0x2efda8,asshownbythevaluesprintedafterthewordAddresses.ThesecondruleisalsovalidatedbythefactthatdwValue.words.firstanddwValue.words.secondcontain0xbeefand0xdead,printedafterValues,whichmakessenseconsideringthatdwValue.valueis0xdeadbeef.Thethirdruleisn’tdemonstratedinthisexamplebecausewedon’thaveenoughmemorycontext,butifyouweretoputthisunioninsideastructureandsurrounditwithwhatevertypesyoulike,itwouldinfactalwaysalignlikeaDWORD.

ClassesandVFTablesMuchlikestructures,classesarecontainersthatholdandisolatemultiplepiecesofdata,butclassescanalsocontainfunctiondefinitions.

ASimpleClassClasseswithnormalfunctions,suchasbarinListing4-4,conformtothesamememorylayoutsasstructures.

classbar{public:bar():bar1(0x898989),bar2(0x10203040){}voidmyfunction(){bar1++;}intbar1,bar2;};

bar_bar=bar();printf("Size%d;Address0x%x:_bar\n",sizeof(_bar),&_bar);

Listing4-4:AC++class

Theprintf()callinListing4-4wouldoutputthefollowing:

Size8;Address0x2efd80:_bar

Eventhoughbarhastwomemberfunctions,thisoutputshowsthatitspansonlythe8bytesneededtoholdbar1andbar2.Thisisbecausethebarclassdoesn’tincludeabstractionsofthosememberfunctions,sotheprogramcancallthemdirectly.

NOTE

Accesslevelssuchaspublic,private,andprotecteddonotmanifestinmemory.Regardlessofthesemodifiers,membersofclassesarestillorderedastheyaredefined.

AClasswithVirtualFunctionsInclassesthatdoincludeabstractfunctions(oftencalledvirtualfunctions),theprogrammustknowwhichfunctiontocall.ConsidertheclassdefinitionsinListing4-5:

classfoo{public:foo():myValue1(0xDEADBEEF),myValue2(0xBABABABA){}intmyValue1;staticintmyStaticValue;virtualvoidbar(){printf("callfoo::bar()\n");}virtualvoidbaz(){printf("callfoo::baz()\n");}virtualvoidbarbaz(){}intmyValue2;};

intfoo::myStaticValue=0x12121212;

classfooa:publicfoo{public:fooa():foo(){}virtualvoidbar(){printf("callfooa::bar()\n");}virtualvoidbaz(){printf("callfooa::baz()\n");}};

classfoob:publicfoo{public:foob():foo(){}virtualvoidbar(){printf("callfoob::bar()\n");}virtualvoidbaz(){printf("callfoob::baz()\n");}};

Listing4-5:Thefoo,fooa,andfoobclasses

Theclassfoohasthreevirtualfunctions:bar,baz,andbarbaz.Classesfooaandfoobinheritfromclassfooandoverloadbothbarandbaz.Sincefooaandfoobhaveapublicbaseclassoffoo,afoopointercanpointtothem,buttheprogrammuststillcallthecorrectversionsofbarandbaz.Youcanseethisbyexecutingthefollowingcode:

foo*_testfoo=(foo*)newfooa();_testfoo->bar();//callsfooa::bar()

Andhereistheoutput:

callfooa::bar()

Theoutputshowsthat_testfoo->bar()invokedfooa::bar()eventhough_testfooisafoopointer.Theprogramknewwhichversionofthefunctiontocall,becausethecompilerincludedaVF(virtualfunction)tableinthememoryof_testfoo.VFtablesarearraysoffunctionaddressesthatabstractclassinstancesusetotellaprogramwheretheiroverloadedfunctionsare

located.

ClassInstancesandVirtualFunctionTablesTounderstandtherelationshipbetweenclassinstancesandVFtables,let’sinspectamemorydumpofthethreeobjectsdeclaredinthislisting:

foo_foo=foo();fooa_fooa=fooa();foob_foob=foob();

TheseobjectsareofthetypesdefinedinListing4-5.YoucanseetheminmemoryinFigure4-4.

Figure4-4:OllyDbgmemorydumpofclassdata

Pane1showsthateachclassinstancestoresitsmembersjustlikeastructure,butitprecedesthemwithaDWORDvaluethatpointstotheclassinstance’sVFtable.Pane2showstheVFtablesforeachofourthreeclassinstances.Thememory-to-codecrosswalkinTable4-4showshowthesepanesandthecodetietogether.

Table4-4:Memory-to-CodeCrosswalkforListing4-5andFigure4-4

Address Size Data Object

Pane1

0x0018FF20 4bytes

0x004022B0 Startof_fooandpointertofooVFtable

0x0018FF24 8bytes

0xDEADBEEF0xBABABABA

_foo.myValue1and_foo.myValue2

0x0018FF2C 4bytes

0x004022C0 Startof_fooaandpointertofooaVFtable

0x0018FF30 8bytes

0xDEADBEEF0xBABABABA

_fooa.myValue1and_fooa.myValue2

0x0018FF38 4bytes

0x004022D0 Startof_foobandpointertofoobVFtable

0x0018FF3C 8bytes

0xDEADBEEF0xBABABABA

_foob.myValue1and_foob.myValue2

{...} Unrelateddata

Pane2

0x004022B0 4bytes

0x00401060 StartoffooVFtable;addressoffoo::bar

0x004022B4 4bytes

0x00401080 Addressoffoo::baz

0x004022B8 4bytes

0x004010A0 Addressoffoo::barbaz

0x004022BC 4bytes

0x0040243C Unrelateddata

0x004022C0 4bytes

0x004010D0 StartoffooaVFtable;addressoffooa::bar

0x004022C4 4bytes

0x004010F0 Addressoffooa::baz

0x004022C8 4bytes

0x004010A0 Addressoffoo::barbaz

0x004022CC 4bytes

0x004023F0 Unrelateddata

0x004022D0 4bytes

0x00401130 StartoffoobVFtable;addressoffoob::bar

0x004022D4 4bytes

0x00401150 Addressoffoob::baz

bytes

0x004022D8 4bytes

0x004010A0Addressoffoo::barbaz

ThiscrosswalkshowshowtheVFtablesforthecodeinListing4-5arelaidoutinmemory.EachVFtableisgeneratedbythecompilerwhenthebinaryismade,andthetablesremainconstant.Tosavespace,instancesofthesameclassallpointtothesameVFtable,whichiswhytheVFtablesaren’tplacedinlinewiththeclass.

SincewehavethreeVFtables,youmightwonderhowaclassinstanceknowswhichVFtabletouse.Thecompilerplacescodesimilartothefollowingbitofassemblyineachvirtualclassconstructor:

MOVDWORDPTRDS:[EAX],VFADDR

ThisexampletakesthestaticaddressofaVFtable(VFADDR)andplacesitinmemoryasthefirstmemberoftheclass.

Nowlookataddresses0x004022B0,0x004022C0,and0x004022D0inTable4-4.Theseaddressescontainthebeginningofthefoo,fooa,andfoobVFtables.Noticethatfoo::barbazexistsinallthreeVFtables;thisisbecausethefunctionisnotoverloadedbyeithersubclass,meaninginstancesofeachsubclasswillcalltheoriginalimplementationdirectly.

Notice,too,thatfoo::myStaticValuedoesnotappearinthiscrosswalk.Sincethevalueisstatic,itdoesn’tactuallyneedtoexistasapartofthefooclass;it’splacedinsidethisclassonlyforbettercodeorganization.Inreality,itgetstreatedlikeaglobalvariableandisplacedelsewhere.

VFTABLESANDCHEATENGINERememberCheatEngine’sFirstelementofpointerstructmustpointtomoduleoptionforpointerscansfromFigure1-4onpage14?Nowthatyou’vereadabitaboutVFtables,thatknowledgeshouldhelpyouunderstandhowthisoptionworks:itmakesCheatEngineignoreallheapchunkswherethefirstmemberisnotapointertoavalidVFtable.Itspeedsupscans,butitworksonlyifeverystepinapointerpathispartofanabstractclassinstance.

Thememorytourendshere,butifyouhavetroubleidentifyingachunkofdatainthefuture,comebacktothissectionforreference.Next,we’lllookathowacomputercanunderstandagame’shigh-levelsourcecodeinthefirstplace.

x86AssemblyCrashCourseWhenaprogram’ssourcecodeiscompiledintoabinary,itisstrippedofallunnecessaryartifactsandtranslatedintomachinecode.Thismachinecode,madeupofonlybytes(commandbytesarecalledopcodes,buttherearealsobytesrepresentingoperands),getsfeddirectlytotheprocessorandtellsitexactlyhowtobehave.Those1sand0sfliptransistorstocontrolcomputation,andtheycanbeextremelydifficulttounderstand.Tomakecomputersalittleeasiertotalkto,engineersworkingwithsuchcodeuseassemblylanguage,ashorthandthatrepresentsrawmachineopcodeswithabbreviatednames(calledmnemonics)andasimplisticsyntax.

Assemblylanguageisimportantforgamehackerstoknowbecausemanypowerfulhackscanbeachievedonlythroughdirectmanipulationofagame’sassemblycode,viamethodssuchasNOPingorhooking.Inthissection,you’lllearnthebasicsofx86assemblylanguage,aspecificflavorofassemblymadeforspeakingto32-bitprocessors.Assemblylanguageisveryextensive,soforthesakeofbrevitythissectiontalksonlyaboutthesmallsubsetofassemblyconceptsthataremostusefultogamehackers.1

NOTE

Throughoutthissection,manysmallsnippetsofassemblycodeincludecommentssetoffbyasemicolon(;)todescribeeachinstructioningreaterdetail.

CommandSyntaxAssemblylanguageisusedtodescribemachinecode,soitssyntaxisprettysimplistic.Whilethissyntaxmakesitveryeasyforsomeonetounderstandindividualcommands(alsocalledoperations),italsomakesunderstanding

complexblocksofcodeveryhard.Evenalgorithmsthatareeasilyreadableinhigh-levelcodeseemobfuscatedwhenwritteninassembly.Forexample,thefollowingsnippetofpseudocode:

if(EBX>EAX)ECX=EDXelseECX=0

wouldlooklikeListing4-6inx86assembly.

CMPEBX,EAXJGlabel1MOVECX,0JMPlabel2label1:MOVECX,EDXlabel2:

Listing4-6:Somex86assemblycommands

Therefore,ittakesextensivepracticetounderstandeventhemosttrivialfunctionsinassembly.Understandingindividualcommands,however,isverysimple,andbytheendofthissection,you’llknowhowtoparsethecommandsIjustshowedyou.

InstructionsThefirstpartofanassemblycommandiscalledaninstruction.Ifyouequateanassemblycommandtoaterminalcommand,theinstructionistheprogramtorun.Atthemachinecodelevel,instructionsaretypicallythefirstbyteofacommand;2therearealsosome2-byteinstructions,wherethefirstbyteis0x0F.Regardless,aninstructiontellstheprocessorexactlywhattodo.InListing4-6,CMP,JG,MOV,andJMPareallinstructions.

OperandSyntaxWhilesomeinstructionsarecompletecommands,thevastmajorityareincompleteunlessfollowedbyoperands,orparameters.EverycommandinListing4-6hasatleastoneoperand,likeEBX,EAX,andlabel1.

Assemblyoperandscomeinthreeforms:

ImmediatevalueAnintegervaluethatisdeclaredinline(hexadecimalvalueshaveatrailingh).

RegisterAnamethatreferstoaprocessorregister.

MemoryoffsetAnexpression,placedinbrackets,thatrepresentsthememorylocationofavalue.Theexpressioncanbeanimmediatevalueoraregister.Alternatively,itcanbeeitherthesumordifferenceofaregisterandimmediatevalue(somethinglike[REG+Ah]or[REG-10h]).

Eachinstructioninx86assemblycanhavebetweenzeroandthreeoperands,andcommasareusedtoseparatemultipleoperands.Inmostcases,instructionsthatrequiretwooperandshaveasourceoperandandadestinationoperand.Theorderingoftheseoperandsisdependentontheassemblysyntax.Forexample,Listing4-7showsagroupofpseudocommandswrittenintheIntelsyntax,whichisusedbyWindows(and,thus,byWindowsgamehackers):

MOVR1,1;setR1(register)to1(immediate)➊MOVR1,[BADF00Dh];setR1tovalueat[BADFOODh](memoryoffset)MOVR1,[R2+10h];setR1tovalueat[R2+10h](memoryoffset)MOVR1,[R2-20h];setR1tovalueat[R2+20h](memoryoffset)

Listing4-7:DemonstratingIntelsyntax

IntheIntelsyntax,thedestinationoperandcomesfirst,followedbythesource,soat➊,R1isthedestinationand[BADFOODh]isthesource.Ontheotherhand,compilerslikeGCC(whichcanbeusedtowritebotsonWindows)useasyntaxknownasAT&T,orUNIX,syntax.Thissyntaxdoesthingsalittledifferently,asyoucanseeinthefollowingexample:

MOV$1,%R1;setR1(register)to1(immediate)MOV0xBADF00D,%R1;setR1tovalueat0xBADFOOD(memoryoffset)MOV0x10(%R2),%R1;setR1tovalueat0x10(%R2)(memoryoffset)MOV-0x20(%R2),%R1;setR1tovalueat-0x20(%R2)(memoryoffset)

ThiscodeistheAT&TversionofListing4-7.AT&Tsyntaxnotonlyreversestheoperandorderbutalsorequiresoperandprefixingandhasadifferentformatformemoryoffsetoperands.

AssemblyCommandsOnceyouunderstandassemblyinstructionsandhowtoformattheir

Onceyouunderstandassemblyinstructionsandhowtoformattheiroperands,youcanstartwritingcommands.Thefollowingcodeshowsanassemblyfunction,consistingofsomeverybasiccommands,thatessentiallydoesnothing.

PUSHEBP;putEBP(register)onthestackMOVEBP,ESP;setEBPtovalueofESP(register,topofstack)PUSH-1;put-1(immediate)onthestackADDESP,4;negatethe'PUSH-1'toputESPbackwhereitwas(aPUSH;subtracts4fromESP,sinceitgrowsthestack)MOVESP,EBP;setESPtothevalueofEBP(theywillbethesameanyway,;sincewehavekeptESPinthesameplace)POPEBP;setEBPtothevalueontopofthestack(itwillbewhat;EBPstartedwith,putonthestackbyPUSHEBP)XOREAX,EAX;exclusive-orEAX(register)withitself(sameeffectas;'MOVEAX,0'butmuchfaster)RETN;returnfromthefunctionwithavalueof0(EAXtypically;holdsthereturnvalue)

Thefirsttwolines,aPUSHcommandandaMOVcommand,setupastackframe.Thenextlinepushes–1tothestack,whichisundonewhenthestackissetbacktoitsoriginalpositionbytheADDESP,4command.Followingthat,thestackframeisremoved,thereturnvalue(storedinEAX)issetto0withanXORinstruction,andthefunctionreturns.

You’lllearnmoreaboutstackframesandfunctionsin“TheCallStack”onpage86and“FunctionCalls”onpage94.Fornow,turnyourattentiontotheconstantsinthecode—namelyEBP,ESP,andEAX,whichareusedfrequentlyinthecodeasoperands.Thesevalues,amongothers,arecalledprocessorregisters,andunderstandingthemisessentialtounderstandingthestack,functioncalls,andotherlow-levelaspectsofassemblycode.

ProcessorRegistersUnlikehigh-levelprogramminglanguages,assemblylanguagedoesnothaveuser-definedvariablenames.Instead,itaccessesdatabyreferencingitsmemoryaddress.Duringintensivecomputation,however,itcanbeextremelycostlyfortheprocessortoconstantlydealwiththeoverheadofreadingandwritingdatatoRAM.Tomitigatethishighcost,x86processorsprovideasmallsetoftemporaryvariables,calledprocessorregisters,which

provideasmallsetoftemporaryvariables,calledprocessorregisters,whicharesmallstoragespaceswithintheprocessoritself.SinceaccessingtheseregistersrequiresfarlessoverheadthanaccessingRAM,assemblyusesthemtodescribeitsinternalstate,passvolatiledataaround,andstorecontext-sensitivevariables.

GeneralRegistersWhenassemblycodeneedstostoreoroperateonarbitrarydata,itusesasubsetofprocessregisterscalledgeneralregisters.Theseregistersareusedexclusivelytostoreprocess-specificdata,suchasafunction’slocalvariables.Eachgeneralregisteris32bitsandthuscanbethoughtofasaDWORDvariable.Generalregistersarealsooptimizedforspecificpurposes:

EAX,theaccumulatorThisregisterisoptimizedformathematicalcomputations.Someoperations,suchasmultiplicationanddivision,canonlyoccurinEAX.

EBX,thebaseregisterThisregisterisusedarbitrarilyforextrastorage.Sinceits16-bitpredecessor,BX,wastheonlyregisterthatoperationscouldusetoreferencememoryaddresses,EBXwasusedasareferencetoRAM.Inx86assembly,however,allregisterscanbeaddressreferences,leavingEBXwithoutatruepurpose.

ECX,thecounterThisregisterisoptimizedtoactasthecountervariable(oftencallediinhigh-levelcode)inaloop.

EDX,thedataregisterThisregisterisoptimizedtoactasahelpertoEAX.In64-bitcomputations,forinstance,EAXactsasbits0–31andEDXactsasbits32–63.

Theseregistersalsohaveasetof8-and16-bitsubregistersthatyoucanusetoaccesspartialdata.Thinkofeverygeneralregisterasaunion,wherearegisternamedescribesthe32-bitmemberandthesubregistersarealternatemembersthatallowaccesstosmallerpiecesoftheregister.ThefollowingcodeshowswhatthisunionmightlooklikeforEAX:

union{DWORDEAX;WORDAX;struct{BYTEL;

BYTEH;}A;}EAX;

Inthisexample,AXallowsaccesstothelowerWORDofEAX,whileALallowsaccesstothelowerBYTEofAXandAHtoitshigherBYTE.Everygeneralregisterhasthisstructure,andIoutlinetheotherregisters’subregistersinFigure4-5.

Figure4-5:x86registersandsubregisters

EAX,EBC,ECX,andEDXhavehigherwords,too,butthecompilerwillalmostneveraccessthemonitsown,asitcanjustusethelowerwordwhenitneedsword-onlystorage.

IndexRegistersx86assemblyalsohasfourindexregisters,whichareusedtoaccessdatastreams,referencethecallstack,andkeeptrackoflocalinformation.Likethegeneralregisters,indexregistersare32bits,butindexregistershavemorestrictlydefinedpurposes:

EDI,thedestinationindexThisregisterisusedtoindexmemorytargetedbywriteoperations.Iftherearenowriteoperationsinapieceofcode,thecompilercanuseEDIforarbitrarystorageifneeded.

ESI,thesourceindexThisregisterisusedtoindexmemorytargetedby

readoperations.Itcanalsobeusedarbitrarily.

ESP,thestackpointerThisregisterisusedtoreferencethetopofthecallstack.Allstackoperationsdirectlyaccessthisregister.YoumustuseESPonlywhenworkingwiththestack,anditmustalwayspointtothetopofthestack.

EBP,thestackbasepointerThisregistermarksthebottomofthestackframe.Functionsuseitasareferencetotheirparametersandlocalvariables.Somecodemaybecompiledwithanoptiontoomitthisbehavior,inwhichcaseEBPcanbeusedarbitrarily.

Likethegeneralregisters,eachindexregisterhasa16-bitcounterpart:DI,SI,SP,andBP,respectively.However,theindexregistershaveno8-bitsubregisters.

WHYDOSOMEX86REGISTERSHAVESUBREGISTERS?

Thereisahistoricalreasonwhybothgeneralandindexregistershave16-bitcounterparts.Thex86architecturewasbasedona16-bitarchitecture,fromwhichitextendedtheregistersAX,BX,CX,DX,DI,SI,SP,andBP.Appropriately,theextensionsretainthesamenamesbutareprefixedwithanE,for“extended.”The16-bitversionsremainforbackwardcompatibility.Thisalsoexplainswhyindexregistershaveno8-bitabstractions:theyareintendedtobeusedasmemory-addressoffsets,andthereisnopracticalneedtoknowpartialbytesofsuchvalues.

TheExecutionIndexRegisterTheExecutionIndexregister,referredtoasEIP,hasaveryconcretepurpose:itpointstotheaddressofthecodecurrentlybeingexecutedbytheprocessor.Becauseitcontrolstheflowofexecution,itisdirectlyincrementedbytheprocessorandisoff-limitstoassemblycode.TomodifyEIP,assemblycodemustindirectlyaccessitusingoperationssuchasCALL,

JMP,andRETN.

TheEFLAGSRegisterUnlikehigh-levelcode,assemblylanguagedoesn’thavebinarycomparisonoperatorslike==,>,and<.Instead,itusestheCMPcommandtocomparetwovalues,storingtheresultinginformationintheEFLAGSregister.Then,thecodechangesitscontrolflowusingspecialoperationsthatdependonthevaluestoredinELFAGS.

Whilecomparisoncommandsaretheonlyuser-modeoperationsthatcanaccessEFLAGS,theyuseonlythisregister’sstatusbits:0,2,4,6,7,and11.Bits8–10actascontrolflags,bits12–14and16–21actassystemflags,andtheremainingbitsarereservedfortheprocessor.Table4-5showsthetype,name,anddescriptionofeachEFLAGSbit.

Table4-5:EFLAGSbits

Bit(s)Type Name Description

0 Status Carry Setifacarryorborrowwasgeneratedfromthemostsignificantbitduringthepreviousinstruction.

2 Status Parity Setiftheleastsignificantbyteresultingfromthepreviousinstructionhasanevennumberofbitsset.

4 Status Adjust Sameasthecarryflag,butconsidersthe4leastsignificantbits.

6 Status Zero Setiftheresultingvaluefromthepreviousinstructionisequalto0.

7 Status Sign Setiftheresultingvaluefromthepreviousinstructionhasitssignbit(mostsignificantbit)set.

8 Control Trap Whenset,theprocessorsendsaninterrupttotheoperatingsystemkernelafterexecutingthenextoperation.

9 Control Interrupt Whennotset,thesystemignoresmaskable

interrupts.

10 Control Direction Whenset,ESIandEDIaredecrementedbyoperationsthatautomaticallymodifythem.Whennotset,theyareincremented.

11 Status Overflow Setwhenavalueisoverflowedbythepreviousinstruction,suchaswhenADDisperformedonapositivevalueandtheresultisanegativevalue.

TheEFLAGSregisteralsocontainsasystembitandareservedbit,butthoseareirrelevantinuser-modeassemblyandgamehacking,soI’veomittedthemfromthistable.KeepEFLAGSinmindwhenyou’redebugginggamecodetofigureouthowitworks.Forexample,ifyousetabreakpointonaJE(jumpifequal)instruction,youcanlookattheEFLAGS0bittoseewhetherthejumpwillbetaken.

SegmentRegistersFinally,assemblylanguagehasasetof16-bitregisterscalledsegmentregisters.Unlikeotherregisters,segmentregistersarenotusedtostoredata;theyareusedtolocateit.Intheory,theypointtoisolatedsegmentsofmemory,allowingdifferenttypesofdatatobestoredincompletelyseparatememorysegments.Theimplementationofsuchsegmentationisleftuptotheoperatingsystem.Thesearethex86segmentregistersandtheirintendedpurposes:

CS,thecodesegmentThisregisterpointstothememorythatholdsanapplication’scode.

DS,thedatasegmentThisregisterpointstothememorythatholdsanapplication’sdata.

ES,FS,andGS,theextrasegmentsTheseregisterspointtoanyproprietarymemorysegmentsusedbytheoperatingsystem.

SS,thestacksegmentThisregisterpointstomemorythatactsasadedicatedcallstack.

Inassemblycode,segmentregistersareusedasprefixestomemoryoffsetoperands.Whenasegmentregisterisn’tspecified,DSisusedbydefault.

ThismeansthatthecommandPUSH[EBP]iseffectivelythesameasPUSHDS:[EBP].ButthecommandPUSHFS:[EBP]isdifferent:itreadsmemoryfromtheFSsegment,nottheDSsegment.

IfyoulookcloselyattheWindowsx86implementationofmemorysegmentation,youmightnoticethatthesesegmentregisterswerenotexactlyusedasintended.Toseethisinaction,youcanrunthefollowingcommandswiththeOllyDbgcommandlineplug-inwhileOllyDbgisattachedtoapausedprocess:

?CALC(DS==SS&&SS==GS&&GS==ES)?1?CALCDS-CS?8?CALCFS-DS;returnsnonzero(andchangesbetweenthreads)

Thisoutputtellsusthreedistinctthings.First,itshowsthatthereareonlythreesegmentsbeingusedbyWindows:FS,CS,andeverythingelse.ThisisdemonstratedbyDS,SS,GS,andESbeingequal.Forthesamereason,thisoutputshowsthatDS,SS,GS,andEScanallbeusedinterchangeably,astheyallpointtothesamememorysegments.Lastly,sinceFSchangesdependingonthethread,thisoutputshowsthatitisthreaddependent.FSisaninterestingsegmentregister,anditpointstocertainthread-specificdata.In“BypassingASLRinProduction”onpage128,we’llexplorehowthedatainFScanbeusedtobypassASLR—somethingmostbotswillneedtodo.

Infact,inassemblycodegeneratedforWindowsbyacompiler,you’donlyeverseethreesegmentsused:DS,FS,andSS.Interestinglyenough,eventhoughCSseemstoshowaconstantoffsetfromDS,ithasnorealpurposeinuser-modecode.Knowingallofthesethings,youcanfurtherconcludethatthereareonlytwosegmentsbeingusedbyWindows:FSandeverythingelse.

Thesetwosegmentsactuallypointtodifferentlocationsinthesamememory(there’snosimplewaytoverifythis,butitistrue),whichshowsthatWindowsactuallydoesn’tusememorysegmentsatall.Instead,itusesaflatmemorymodelinwhichsegmentregistersarenearlyirrelevant.Whileallsegmentregisterspointtothesamememory,onlyFSandCSpointtodifferentlocations,andCSisnotused.

Inconclusion,thereareonlythreethingsyouneedtoknowabout

Inconclusion,thereareonlythreethingsyouneedtoknowaboutsegmentregisterswhenworkingwithx86assemblyinWindows.First,DS,SS,GS,andESareinterchangeable,butforclarityDSshouldbeusedtoaccessdataandSSshouldbeusedtoaccessthecallstack.Second,CScanbesafelyforgotten.Third,FSistheonlysegmentregisterwithaspecialpurpose;itshouldbeleftalonefornow.

TheCallStackRegistersarepowerful,butunfortunatelytheycomeinverylimitedsupply.Inorderforassemblycodetoeffectivelystoreallofitslocaldata,itmustalsousethecallstack.Thestackisusedtostoremanydifferentvalues,includingfunctionparameters,returnaddresses,andsomelocalvariables.

Understandingtheinsandoutsofthecallstackwillcomeinhandywhenyou’rereverseengineeringagame.Moreover,you’llrelyonthisknowledgeheavilywhenwejumpintocontrolflowmanipulationinChapter8.

StructureYoucanthinkofthecallstackasaFILO(first-in-last-out)listofDWORDvaluesthatcanbedirectlyaccessedandmanipulatedbyassemblycode.Thetermstackisusedbecausethestructureresemblesastackofpaper:objectsarebothaddedtoandremovedfromthetop.DataisaddedtothestackthroughthePUSHoperandcommand,anditisremoved(andplacedinaregister)throughthePOPregistercommand.Figure4-6showshowthisprocessmightlook.

Figure4-6:Thestructureofastack

InWindows,thestackgrowsfromhighermemoryaddressestolowerones.Itoccupiesafiniteblockofmemory,pilinguptoaddress0x00000000(theabsolutetop)fromaddressn(theabsolutebottom).ThismeansthatESP(thepointertothetopofthestack)decreasesasitemsareaddedandincreasesasitemsareremoved.

TheStackFrameWhenanassemblyfunctionusesthestacktostoredata,itreferencesthedatabycreatingastackframe.ItdoessobystoringESPinEBPandthensubtractingnbytesfromESP,effectivelyopeningann-bytegapthatisframedbetweentheregistersEBPandESP.Tobetterunderstandthis,firstimaginethatthestackinFigure4-7ispassedtoafunctionthatrequires0x0Cbytesoflocalstoragespace.

Figure4-7:Initialexamplestack(readfrombottomtotop)

Inthisexample,address0x0000istheabsolutetopofthestack.Wehaveunusedmemoryfromaddresses0x0000to0xFF00–4,andatthetimeofthefunctioncall,0xFF00isthetopofthestack.ESPpointstothisaddress.Thestackmemoryafter0xFF00isusedbyprecedingfunctionsinthecallchain(from0xFF04to0xFFFF).Whenthefunctioniscalled,thefirstthingitdoesisexecutethefollowingassemblycode,whichcreatesastackframeof0x0C(12indecimal)bytes:

PUSHEBP;savesthebottomofthelowerstackframeMOVEBP,ESP;storesthebottomofthecurrentstackframe,inEBP;(also4bytesabovethelowerstackframe)SUBESP,0x0C;subtracts0x0CbytesfromESP,movingitupthestack;tomarkthetopofthestackframe

Afterthiscodeexecutes,thestacklooksmoreliketheoneshowninFigure4-8.Aftercreatingthisstack,thefunctioncanworkwiththe0x0Cbytesitallocatedonthestack.

0x0000isstilltheabsolutetopofthestack.Wehaveunusedstackmemoryfromaddresses0x0000to0xFF00–20,andthememoryataddress0xFF00–16containsthefinal4bytesoflocalstorage(referencedby[EBP-Ch]).Thisisalsothetopofthecurrentstackframe,soESPpointshere.0xFF00–12containsthemiddle4bytesoflocalstorage(referencedby[EBP-8h]),and0xFF00–8containsthefirst4bytesoflocalstorage(referencedby[EBP-4h]).EBPpointsto0xFF00–4,whichisthebottomofthecurrentstackframe;thisaddressholdstheoriginalvalueofEBP.0xFF00isthetopofthelowerstackframe,andtheoriginalESPinFigure4-7pointedhere.Finally,youcanstillseethestackmemoryfromprecedingfunctionsinthecallchainfrom0xFF04to0xFFFF.

Figure4-8:Examplestackwithstackframesetup(readfrombottomtotop)

Withthestackinthisstate,thefunctionisfreetouseitslocaldataasitpleases.Ifthisfunctioncalledanotherfunction,thenewfunctionwouldbuilditsownstackframeusingthesametechnique(thestackframesreallystackup).Onceafunctionfinishesusingastackframe,however,itmustrestorethestacktoitspreviousstate.Inourcase,thatmeansmakingthestacklooklikeitdidinFigure4-7.Whenthesecondfunctionfinishes,ourfirstfunctioncleansthestackusingthefollowingtwocommands:

MOVESP,EBP;demolishesthestackframe,bringingESPto4bytesabove;itsoriginalvalue(0xFF00-4)POPEBP;restoresthebottomoftheoldstackframethatwassavedby;'PUSHEBP'.Alsoadds4bytestoESP,puttingitbackat;itsoriginalvalue

Butifyouwanttochangetheparameterspassedtoafunctioninagame,don’tlookfortheminthatfunction’sstackframe.Afunction’sparametersarestoredinthestackframeofthefunctionthatcalledit,andthey’re

referencedthrough[EBP+8h],[EBP+Ch],andsoon.Theystartat[EBP+8h]because[EBP+4h]storesthefunction’sreturnaddress.(“FunctionCalls”onpage94explainsthistopicfurther.)

NOTE

Codecanbecompiledwithstackframesdisabled.Whenthisisthecase,you’llnoticethatfunctionsdon’topenwithPUSHEBPandinsteadreferenceeverythingrelativetoESP.Moreoftenthannot,though,stackframesareenabledincompiledgamecode.

Nowthatyouhaveagrasponthefundamentalsofassemblycode,let’sexploresomespecificsthatwillcomeinhandywhenhackinggames.

Importantx86InstructionsforGameHackingWhileassemblylanguagehashundredsofinstructions,manywell-equippedgamehackersunderstandonlyasmallsubsetofthem,whichIcoverindetailhere.Thissubsettypicallyencapsulatesallinstructionsthatareusedtomodifydata,callfunctions,comparevalues,orjumparoundwithincode.

DataModificationDatamodificationoftenhappensoverseveralassemblyoperations,buttheendresulthastobestoredeitherinmemoryorinaregister,typicallywiththeMOVinstruction.TheMOVoperationtakestwooperands:adestinationandasource.Table4-6showsallpossiblesetsofMOVoperandsandtheresultsyoucanexpectfromthosecalls.

Table4-6:OperandstotheMOVInstruction

Instructionsyntax Result

MOVR1,R2 CopiesR2’svaluetoR1.MOVR1,[R2] CopiesthevaluefromthememoryreferencedbyR2to

R1.MOVR1,[R2+Ah] Copiesthevaluefromthememoryreferencedby

R2+0xAtoR1.

MOVR1,[DEADBEEFh] Copiesthevaluefromthememoryat0xDEADBEEFtoR1.

MOVR1,BADF00Dh Copiesthevalue0xBADF00DtoR1.MOV[R1],R2 CopiesR2’svaluetothememoryreferencedbyR1.MOV[R1],BADF00Dh Copiesthevalue0xBADF00Dtothememory

referencedbyR1.MOV[R1+4h],R2 CopiesR2’svaluetothememoryreferencedbyR1+0x4.MOV[R1+4h],BADF00Dh

Copiesthevalue0xBADF00DtothememoryreferencedbyR1+0x4.

MOV[DEADBEEFh],R1 CopiesR1’svaluetothememoryat0xDEADBEEF.MOV[DEADBEEFh],BADF00Dh

Copiesthevalue0xBADF00Dtothememoryat0xDEADBEEF.

TheMOVinstructioncantakealotofoperandcombinations,butsomearen’tallowed.First,thedestinationoperandcan’tbeanimmediatevalue;itmustbearegisterormemoryaddress,becauseimmediatevaluescan’tbemodified.Second,valuescan’tbedirectlycopiedfromonememoryaddresstoanother.Copyingavaluerequirestwoseparateoperations,likeso:

MOVEAX,[EBP+10h];copymemoryfromEBP+0x10toEAXMOV[DEADBEEFh],EAX;MOVthecopiedmemorytomemoryat0xDEADBEEF

TheseinstructionscopywhateverisstoredatEBP+0x10tothememoryat0xDEADBEEF.

ArithmeticLikemanyhigh-levellanguages,assemblylanguagehastwotypesofarithmetic:unaryandbinary.Unaryinstructionstakeasingleoperandthatactsasbothadestinationandasource.Thisoperandcanbearegisteroramemoryaddress.Table4-7showsthecommonunaryarithmeticinstructionsinx86.

Table4-7:UnaryArithmeticInstructions

Instructionsyntax

Result

INCoperand Adds1totheoperandvalue.

DECoperand Subtracts1fromtheoperandvalue.NOToperand Logicallynegatestheoperandvalue(flipsallbits).NEGoperand Performstwo’s-complementnegation(flipsallbitsandadds1;

essentiallymultipliesby-1).

Binaryinstructions(whichmakeupthemajorityofx86arithmetic),ontheotherhand,aresyntacticallysimilartotheMOVinstruction.Theyrequiretwooperandsandhavesimilaroperandlimitations.UnlikeMOV,however,theirdestinationoperandservesasecondpurpose:itisalsotheleft-handvalueinthecalculation.Forexample,theassemblyoperationADDEAX,EBXequatestoEAX=EAX+EBXorEAX+=EBXinC++.Table4-8showsthecommonx86binaryarithmeticinstructions.

Table4-8:BinaryArithmeticInstructions

Instructionsyntax

Function Operandnotes

ADDdestination,source

destination+=source

SUBdestination,source

destination-=source

ANDdestination,source

destination&=source

ORdestination,source

destination|=source

XORdestination,source

destination^=source

SHLdestination,source

destination=destination<<source

sourcemustbeCLoran8-bitimmediatevalue.

source source

SHRdestination,

source

destination=destination>>

source

sourcemustbeCLoran8-bitimmediatevalue.

IMULdestination,source

destination*=source

destinationmustbearegister;sourcecannotbeanimmediatevalue.

Ofthesearithmeticinstructions,IMULisspecialbecauseyoucanpassitathirdoperand,intheformofanimmediatevalue.Withthisprototype,thedestinationoperandisnolongerinvolvedinthecalculation,whichinsteadtakesplacebetweentheremainingoperands.Forexample,theassemblycommandIMULEAX,EBX,4hequatestoEAX=EBX*0x4inC++.

YoucanalsopassasingleoperandtoIMUL.3Inthiscase,theoperandactsasthesourceandcanbeeitheramemoryaddressoraregister.Dependingonthesizeofthesourceoperand,theinstructionwillusedifferentpartsoftheEAXregisterforinputsandoutput,asshowninTable4-9.

Table4-9:PossibleIMULRegisterOperands

Sourcesize

InputOutput

8bits AL 16bit,storedinAH:AL(whichisAX)

16bits AX 32bit,storedinDX:AX(bits0–15inAXandbits16–31inDX)

32bits EAX 64bit,storedinEDX:EAX(bits0–31inEAXandbits32–64inEDX)

Noticethateventhoughtheinputisonlyoneregister,eachoutputusestworegisters.That’sbecauseinmultiplication,theresultgenerallyislargerthantheinputs.

Let’slookatanexamplecalculationusingIMULwithasingle32-bitoperand:

IMUL[BADFOODh];32-bitoperandisataddress0xBADFOOD

Thiscommandbehaveslikethefollowingpseudocode:

EDX:EAX=EAX*[BADFOODh]

Similarly,here’sanoperationthatusesIMULwithasingle16-bitoperand:

IMULCX;16-bitoperandisstoredinCX

Anditscorrespondingpseudocode:

DX:AX=AX*CX

Finally,thisisanIMULcommandwithasingle8-bitoperand:

IMULCL;8-bitoperandisstoredinCL

Anditscorrespondingpseudocode:

AX=AL*CL

x86assemblylanguagehasdivisionaswell,throughtheIDIVinstruction.4TheIDIVinstructionacceptsasinglesourceoperandandfollowsregisterrulessimilartothoseforIMUL.AsTable4-10shows,IDIVoperationsrequiretwoinputsandtwooutputs.

Table4-10:PossibleIDIVRegisterOperands

Sourcesize

Input Output

8bit 16bit,storedinAH:AL(whichisAX)

RemainderinAH;quotientinAL

16bit 32bit,storedinDX:AX RemainderinDX;quotientinAX

32bit 64bit,storedinEDX:EAX RemainderinEDX;quotientinEAX

Indivision,theinputsaregenerallylargerthantheoutput,soheretheinputstaketworegisters.Moreover,divisionoperationsmuststorearemainder,whichgetsstoredinthefirstinputregister.Forexample,here’showa32-bitIDIVcalculationwouldlook:

MOVEDX,0;there'snohigh-orderDWORDintheinput,soEDXis0MOVEAX,inputValue;32-bitinputvalueIDIVECX;divideEDX:EAXbyECX

Andhere’ssomepseudocodethatexpresseswhathappensunderthehood:

EAX=EDX:EAX/ECX;quotientEDX=EDX:EAX%ECX;remainder

ThesedetailsofIDIVandIMULareimportanttoremember,asthebehaviorcanotherwisebequiteobfuscatedwhenyou’resimplylookingatthecommands.

BranchingAfterevaluatinganexpression,programscandecidewhattoexecutenextbasedontheresult,typicallyusingconstructssuchasif()statementsorswitch()statements.Thesecontrolflowstatementsdon’texistattheassemblylevel,however.Instead,assemblycodeusestheEFLAGSregistertomakedecisionsandjumpoperationstoexecutedifferentblocks;thisprocessiscalledbranching.

TogetthepropervalueinEFLAGS,assemblycodeusesoneoftwoinstructions:TESTorCMP.Bothcomparetwooperands,setthestatusbitsofEFLAGS,andthendiscardanyresults.TESTcomparestheoperandsusingalogicalAND,whileCMPusessignedsubtractiontosubtractthelatteroperandfromtheformer.

Inordertobranchproperly,thecodehasajumpcommandimmediatelyfollowingthecomparison.Eachtypeofjumpinstructionacceptsasingleoperandthatspecifiestheaddressofthecodetojumpto.HowaparticularjumpinstructionbehavesdependsonthestatusbitsofEFLAGS.Table4-11describessomex86jumpinstructions.

Table4-11:Commonx86JumpInstructions

InstructionName Behavior

JMPdest Unconditionaljump

Jumpstodest(setsEIPtodest).

JEdest Jumpifequal JumpsifZF(zeroflag)is1.

JNEdest Jumpifnotequal JumpsifZFis0.JGdest Jumpifgreater JumpsifZFis0andSF(signflag)isequalto

OF(overflowflag).JGEdest Jumpifgreater

orequalJumpsifSFisequaltoOF.

JAdest UnsignedJG JumpsifCF(carryflag)is0andZFis0.JAEdest UnsignedJGE JumpsifCFis0.JLdest Jumpifless JumpsifSFisnotequaltoOF.JLEdest Jumpiflessor

equalJumpsifZFis1orSFisnotequaltoOF.

JBdest UnsignedJL JumpsifCFis1.JBEdest UnsignedJLE JumpsifCFis1orZFis1.JOdest Jumpifoverflow JumpsifOFis1.JNOdest Jumpifnot

overflowJumpsifOFis0.

JZdest Jumpifzero JumpsifZFis1(identicaltoJE).JNZdest Jumpifnotzero JumpsifZFis0(identicaltoJNE).

Rememberingwhichflagscontrolwhichjumpinstructionscanbeapain,buttheirpurposeisclearlyexpressedintheirname.AgoodruleofthumbisthatajumpprecededbyaCMPisthesameasitscorrespondingoperator.Forexample,Table4-11listsJEas“jumpifequal,”sowhenJEfollowsaCMPoperation,it’sthesameasthe==operator.Similarly,JGEwouldbe>=,JLEwouldbe>=,andsoon.

Asanexample,considerthehigh-levelcodeshowninListing4-8.

--snip--if(EBX>EAX)ECX=EDX;elseECX=0;

--snip--

Listing4-8:Asimpleconditionalstatement

Thisif()statementjustcheckswhetherEBXisgreaterthanEAXandsetsECXbasedontheresult.Inassembly,thesamestatementmaylooksomethinglikethis:

--snip--CMPEBX,EAX;if(EBX>EAX)JGlabel1;jumptolabel1ifEBX>EAXMOVECX,0;ECX=0(elseblock)JMPlabel2;jumpovertheifblocklabel1:➊MOVECX,EDX;ECX=EDX(ifblock)label2:--snip--

Theassemblyfortheif()statementinListing4-8beginswithaCMPinstructionandbranchesifEBXisgreaterthanEAX.Ifthebranchistaken,EIPissettotheifblockat➊courtesyoftheJGinstruction.Ifthebranchisnottaken,thecodecontinuesexecutinglinearlyandhitstheelseblockimmediatelyaftertheJGinstruction.Whentheelseblockfinishesexecuting,anunconditionalJMPsetsEIPto0x7,skippingovertheifblock.

FunctionCallsInassemblycode,functionsareisolatedblocksofcommandsexecutedthroughtheCALLinstruction.TheCALLinstruction,whichtakesafunctionaddressastheonlyoperand,pushesareturnaddressontothestackandsetsEIPtoitsoperandvalue.ThefollowingpseudocodeshowsaCALLinaction,withmemoryaddressesontheleftinhex:

0x1:CALLEAX0x2:...

WhenCALLEAXisexecuted,thenextaddressispushedtothestackandEIPissettoEAX,showingthatCALLisessentiallyaPUSHandJMP.Thefollowingpseudocodeunderscoresthispoint:

0x1:PUSH3h0x2:JMPEAX0x3:...

Whilethere’sanextraaddressbetweenthePUSHinstructionandthecodetoexecute,theresultisthesame:beforetheblockofcodeatEAXisexecuted,theaddressofthecodethatfollowsthebranchispushedtothestack.Thishappenssothecallee(thefunctionbeingcalled)knowswheretojumptointhecaller(thefunctiondoingthecall)whenitreturns.

Ifafunctionwithoutparametersiscalled,aCALLcommandisallthat’snecessary.Ifthecalleetakesparameters,however,theparametersmustfirstbepushedontothestackinreverseorder.Thefollowingpseudocodeshowshowafunctioncallwiththreeparametersmightlook:

PUSH300h;arg3PUSH200h;arg2PUSH100h;arg1CALLECX;call

Whenthecalleeisexecuted,thetopofthestackcontainsareturnaddressthatpointstothecodeafterthecall.Thefirstparameter,0x100,isbelowthereturnaddressonthestack.Thesecondparameter,0x200,isbelowthat,followedbythethirdparameter,0x300.Thecalleesetsupitsstackframe,usingmemoryoffsetsfromEBPtoreferenceeachparameter.Oncethecalleehasfinishedexecuting,itrestoresthecaller’sstackframeandexecutestheRETinstruction,whichpopsthereturnaddressoffthestackandjumpstoit.

Sincetheparametersarenotapartofthecallee’sstackframe,theyremainonthestackafterRETisexecuted.Ifthecallerisresponsibleforcleaningthestack,itadds12(3parameters,at4byteseach)toESPimmediatelyafterCALLECXcompletes.Ifthecalleeisresponsible,itcleansupbyexecutingRET12insteadofRET.Thisresponsibilityisdeterminedbythecallee’scallingconvention.

Afunction’scallingconventiontellsthecompilerhowtheassemblycodeshouldpassparameters,storeinstancepointers,communicatethereturnvalue,andcleanthestack.Differentcompilershavedifferentcallingconventions,buttheoneslistedinTable4-12aretheonlyfourthatagamehackerislikelytoencounter.

Table4-12:CallingConventionstoKnowforGameHacking

Directive CleanerNotes__cdecl caller DefaultconventioninVisualStudio.

__stdcall callee ConventionusedbyWin32APIfunctions.__fastcallcallee FirsttwoDWORD(orsmaller)parametersarepassedin

ECXandEDX.__thiscallcallee Usedformemberfunctions.Thepointertotheclass

instanceispassedinECX.

TheDirectivecolumninTable4-12givesthenameofthecallingconvention,andtheCleanercolumntellsyouwhetherthecallerorcalleeisresponsibleforcleaningthestackgiventhatdirective.Inthecaseofthesefourcallingconventions,parametersarealwayspushedrighttoleft,andreturnvaluesarealwaysstoredinEAX.Thisisastandard,butnotarule;itcandifferacrossothercallingconventions.

ClosingThoughtsMygoalinwritingthischapterwastohelpyouunderstandmemoryandassemblyinageneralsense,beforewedigintogame-hackingspecifics.Withyournewfoundabilitytothinklikeacomputer,youshouldbeadequatelyarmedtostarttacklingmoreadvancedmemoryforensicstasks.Ifyou’reitchingforapeekathowyou’llapplyallofthistosomethingreal,flipto“ApplyingCallHookstoAdobeAIR”onpage169or“ApplyingJumpHooksandVFHookstoDirect3D”onpage175.

Ifyouwantsomehands-ontimewithmemory,compilethischapter’sexamplecodeanduseCheatEngineorOllyDbgtoinspect,tweak,andpokeatthememoryuntilyou’vegotthehangofit.Thisisimportant,asthenextchapterwillbuildontheseskillsbyteachingyouadvancedmemoryforensictechniques.

5ADVANCEDMEMORYFORENSICS

Whetheryouhackgamesasahobbyorabusiness,you’lleventuallyfindyourselfbetweenarockand...anunintelligiblememorydump.Beitaracewitharivalbotdevelopertoreleaseahighlyrequestedfeature,abattleagainstagamecompany’sconstantbarrageofupdates,orastruggletolocatesomecomplexdatastructureinmemory,you’llneedtop-notchmemoryforensicsskillstoprevail.

Successfulbotdevelopmentisprecariouslybalancedatopspeedandskill,andtenacioushackersmustrisetothechallengebyswiftlyreleasingingeniousfeatures,promptlyrespondingtogameupdates,andreadilysearchingforeventhemostelusivepiecesofdata.Doingthis,however,requiresacomprehensiveunderstandingofcommonmemorypatterns,advanceddatastructures,andthepurposeofdifferentpiecesofdata.

Thosethreeaspectsofmemoryforensicsareperhapsthemosteffectiveweaponsinyourarsenal,andthischapterwillteachyouhowtousethem.First,I’lldiscussadvancedmemory-scanningtechniquesthatfocusonsearchingfordatabyunderstandingitspurposeandusage.Next,I’llteachyouhowtousememorypatternstotacklegameupdatesandtweakyourbotswithouthavingtorelocateallofyouraddressesfromscratch.Towrapup,I’lldissectthefourmostcommoncomplexdatastructuresintheC++standardlibrary(std::string,std::vector,std::list,andstd::map)soyoucanrecognizetheminmemoryandenumeratetheircontents.Bytheendofthechapter,myhopeisthatyou’llhaveadeepunderstandingofmemory

forensicsandbeabletotakeonanychallengerelatedtomemoryscanning.

AdvancedMemoryScanningWithinagame’ssourcecode,eachpieceofdatahasacold,calculateddefinition.Whenthegameisbeingplayed,however,allofthatdatacomestogethertocreatesomethingnew.Playersonlyexperiencethebeautifulscenery,visceralsounds,andintenseadventures;thedatathatdrivestheseexperiencesisirrelevant.

Withthatinmind,imagineHackerAhasjuststartedtearingintohisfavoritegame,wantingtoautomatesomeoftheboringbitswithabot.Hedoesn’thaveacompleteunderstandingofmemoryyet,andtohim,thedataisnothingbutassumptions.Hethinks,“Ihave500health,soIcanfindthehealthaddressbytellingCheatEnginetolookfora4-byteintegerwithavalueof500.”HackerAhasanaccurateunderstandingofdata:it’sjustinformation(values)storedatparticularlocations(addresses)usingdefinedstructures(types).

NowimagineHackerB,whoalreadyunderstandsthegamebothinsideandout;sheknowshowplayingthegamealtersitsstateinmemory,andthedatanolongerhasanysecrets.Sheknowsthateverydefinedpropertyofthedatacanbedeterminedgivenitspurpose.UnlikeHackerA,HackerBhasanunderstandingofdatathattranscendstheconfinesofasinglevariabledeclaration:sheconsidersthedata’spurposeandusage.Inthissection,we’lldiscussboth.

Eachpieceofdatainagamehasapurpose,andtheassemblycodeofthegamemust,atsomepoint,referencethedatatofulfillthatpurpose.Findingtheuniquecodethatusesapieceofdatameansfindingaversion-agnosticmarkerthatpersistsacrossgameupdatesuntilthedataiseitherremovedoritspurposeischanged.Letmeshowyouwhythisisimportant.

DeducingPurposeSofar,I’veonlyshownyouhowtoblindlysearchmemoryforagivenpieceofdatawithoutconsideringhowit’sbeingused.Thismethodcanbeeffective,butitisnotalwaysefficient.Inmanycases,it’smuchquickertodeducethepurposeofdata,determinewhatcodemightusethatdata,andthenlocatethatcodetoultimatelyfindtheaddressofthedata.

thenlocatethatcodetoultimatelyfindtheaddressofthedata.Thismightnotsoundeasy,butneitherdoes“scanthegame’smemoryfor

aspecificvalueofaspecificdatatype,andthencontinuouslyfiltertheresultlistbasedonchangingcriteria,”whichiswhatyou’velearnedtodothusfar.Solet’slookathowwemightlocatetheaddressforhealthgivenitspurpose.ConsiderthecodeinListing5-1.

structPlayerVital{intcurrent,maximum;};PlayerVitalhealth;--snip--printString("Health:%dof%d\n",health.current,health.maximum);

Listing5-1:Astructurecontainingtheplayer’svitals,andafunctionthatdisplaysthem

IfyoupretendthatprintString()isafancyfunctiontodrawtextonanin-gameinterface,thenthiscodeisprettyclosetowhatyoumightfindinagame.ThePlayerVitalstructurehastwoproperties:thecurrentvalueandamaximumvalue.ThevaluehealthisaPlayerVitalstructure,soithastheseproperties,too.Basedonthenamealone,youcandeducethathealthexiststodisplayinformationabouttheplayer’shealth,andyoucanseethispurposefulfilledwhenprintString()usesthedata.

Evenwithoutthecode,youcanintuitivelydrawsimilarconclusionsbyjustlookingatthehealthtextdisplayedinthegame’sinterface;acomputercan’tdoanythingwithoutcode,afterall.Asidefromtheactualhealthvariable,thereareafewcodeelementsthatneedtoexisttoshowaplayerthistext.First,thereneedstobesomefunctiontodisplaytext.Second,thestringsHealthandofmustbenearby.

NOTE

WhydoIassumethetextissplitintotwoseparatestringsinsteadofone?Thegameinterfaceshowsthatthecurrenthealthvalueisbetweenthesetwostrings,buttherearemanywaysthatcouldhappen,includingformatstrings,strcat(),ortextalignedwithmultipledisplaytextcalls.Whenyou’reanalyzingdata,it’sbesttokeepyourassumptionsbroadtoaccountforallpossibilities.

Tofindhealthwithoutusingamemoryscanner,wecouldutilizethesetwodistinctstrings.Weprobablywouldn’thaveacluewhatthefunctionto

displaytextlookslike,whereitis,orhowmanytimesit’scalled,though.Realistically,thestringsareallwewouldknowtolookfor,andthat’senough.Let’swalkthroughit.

FindingthePlayer’sHealthwithOllyDbgI’llwalkyouthroughhowtotrackdownthehealthstructureinthissection,butI’vealsoincludedthebinaryIanalyzeinthebook’sresourcefiles.Tofollowalongandgetsomehands-onpractice,usethefileChapter5_AdvancedMemoryForensics_Scanning.exe.

First,openOllyDbgandattachittotheexecutable.Then,openOllyDbg’sExecutablemoduleswindowanddouble-clickthemainmodule;inmyexample,themainmoduleistheonly.exeinthemodule’swindow.TheCPUwindowshouldpopup.Now,right-clickintheDisassemblerpaneandselectSearchfor▸Allreferencedtextstrings.ThisshouldopentheReferenceswindow,showninFigure5-1.

Figure5-1:OllyDbg’sReferenceswindow,showingonlyalistofstrings.Therewouldbealotmorethanfourinarealgame.

Fromthiswindow,right-clickandselectSearchfortext.Asearchdialogappears.Enterthestringyou’relookingfor,asshowninFigure5-2,andmakethesearchasbroadaspossiblebydisablingCasesensitiveandenablingEntirescope.

Figure5-2:SearchingforstringsinOllyDbg

ClickOKtoexecutethesearch.TheReferenceswindowcomesbackintofocuswiththefirstmatchhighlighted.Double-clickthematchtoseetheassemblycodethatusesthestringinsidetheCPUwindow.TheDisassemblerpanefocusesonthelineofcodeat0x401030,whichpushestheformatstringparametertoprintString().YoucanseethislineinFigure5-3,whereI’vehighlightedtheentirefunctioncallblock.

Figure5-3:ViewingtheprintString()callintheCPUwindow’sDisassemblerpane

Byreadingtheassemblycode,youcangetaveryaccurateunderstandingofexactlywhatthegameisdoing.TheblackbracketontheleftshowsthatthestringHealthisinsideafunctioncall.Noticetheargumentstothatfunction.Inorder,theseareEAX➊,ECX➋,andtheformatstringat0x4020D0➌.EAXisthevalueat0x40301C,ECXisthevalueat0x403018,andtheformatstringcontainsHealth.Sincethestringcontainstwoformatplaceholders,youcanassumethattheremainingtwoparametersaretheargumentsforthoseplaceholders.

Knowingwhattheargumentsareandthattheyarepushedinreverseorder,youcanworkbackwardandconcludethattheoriginalcodelookedsomethinglikeListing5-2.

intcurrentHealth;//valueat0x403018

intmaxHealth;//valueat0x40301C--snip--someFunction("Health:%dof%d\n",currentHealth,maxHealth);

Listing5-2:HowagamehackermightinterprettheassemblythatFigure5-3compilesto

ThevaluesstoredinEAXandECXareadjacentinmemory,whichmeanstheymaybepartofastructure.Tokeepitsimple,though,thisexamplejustshowsthemasvariabledefinitions.Eitherway,thesearethetwonumbersusedtodisplaytheplayer’shealth.Becausebothoftheseimportantvaluesweredisplayedinthegame’sUI,itwaseasytomakeassumptionsabouttheunderlyingcodethatdisplaysthem.Whenyouknowthepurposeofapieceofdata,youcanquicklyfindthecoderesponsibleforfulfillingit;inthiscase,thatknowledgehelpedusquicklyfindbothaddresses.

Inmanycases,findingaddressescanbethiseasy,butsomepiecesofdatahavesuchcomplexpurposesthatit’shardertoguesswhattolookfor.FiguringouthowtosearchformapdataorcharacterlocationsinOllyDbg,forinstance,canbeprettytricky.

Stringsarefarfromtheonlymarkersthatyoucanusetofindthedatayouwanttochangeinagame,buttheyaredefinitelytheeasiesttoteachwithoutgivingcontrivedexamples.Moreover,somegameshaveloggingorerrorstringsembeddedintheircode,andpokingaroundintheReferencedtextstringswindowofOllyDbgcanbeaquickwaytodeterminewhetherthesestringsarepresent.Ifyoubecomefamiliarwithagame’sloggingpractices,you’llbeabletofindvaluesevenmoreeasily.

DeterminingNewAddressesAfterGameUpdatesWhenapplicationcodeismodifiedandrecompiled,abrand-newbinarythatreflectsthechangesisproduced.Thisbinarymightbeverysimilartothepreviousone,orthebinariesmightbenothingalike;thedifferencebetweenthetwoversionshasadirectcorrelationtothecomplexityofthehigh-levelchanges.Smallchanges,likemodifiedstringsorupdatedconstants,canleavebinariesnearlyidenticalandoftenhavenoeffectontheaddressesofcodeordata.Butmorecomplexchanges—likeaddedfeatures,anewuserinterface,refactoredinternals,ornewin-gamecontent—oftencauseshiftsinthelocationofcrucialmemory.

AUTOMATICALLYFINDCURRENTHEALTHANDMAXHEALTH

In“SearchingforAssemblyPatterns”onpage19and“SearchingforStrings”onpage21,IshowedafewCheatEngineLuascriptsandexplainedhowtheyworked.UsingthefindString()functionintheseexamples,youcanmakeCheatEngineautomaticallylocatetheaddressoftheformatstringthatwejustfoundmanuallyinOllyDbg.Next,youcanwriteasmallfunctiontoscanforthisaddressfollowingbyte0x68(thebyteforthePUSHcommand,asyoucanseebesideitat0x401030inFigure5-3)tolocatetheaddressofthecodethatpushesittothestack.Then,youcanread4bytesfrompushAddress-5andpushAddress-12tolocatecurrentHealthandmaxHealth,respectively.

Thismaynotseemusefulsincewe’vealreadyfoundtheaddresses,butifthiswerearealgame,theseaddresseswouldchangewhenanupdateisreleased.Usingthisknowledgetoautomatefindingthemcanbeveryhelpful.Ifyou’reuptothechallenge,giveitawhirl!

Duetoconstantbugfixes,contentimprovements,andfeatureadditions,onlinegamesareamongthemostrapidlyevolvingtypesofsoftware.Somegamesreleaseupdatesasoftenasonceaweek,andgamehackersoftenspendamajorityoftheirtimereverseengineeringthenewbinariesinordertoaccordinglyupdatetheirbots.

Ifyoucreateadvancedbots,theywillbecomeincreasinglysupportedbyafoundationofmemoryaddresses.Whenanupdatecomes,determiningthenewaddressesforalargenumberofvaluesandfunctionsisthemosttime-consuminginevitabilityyouwillface.Relyingonthe“TipsforWinningtheUpdateRace”canbeverybeneficial,butthetipswon’thelpyoulocatetheupdatedaddresses.YoucanautomaticallylocatesomeaddressesusingCheatEnginescripts,butthatwon’talwaysworkeither.Sometimesyou’llhavetodothedirtyworkbyhand.

Ifyoutrytoreinventthewheelandfindtheseaddressesthesamewayyoudidinitially,you’llbewastingyourtime.Youactuallyhaveabigadvantage,though:theoldbinaryandtheaddressesthemselves.Usingthesetwothings,itispossibletofindeverysingleaddressyouneedtoupdateina

twothings,itispossibletofindeverysingleaddressyouneedtoupdateinafractionofthetime.

Figure5-4showstwodifferentdisassemblies:anewgamebinaryontheleftandthepreviousversionontheright.Ihavetakenthisimagefromanactualgame(whichwillremainnameless)inordertogiveyouarealisticexample.

Figure5-4:Side-by-sidedisassembliesoftwoversionsofonegame

Mybotmodifiedthecodeat0x047B542(right),andIneededtofindthecorrespondingcodeinthenewversion,whichIdiscoveredat0x047B672(left).Thisfunctioncallinvokesapacket-parsingfunctionwhenapackethasbeenreceived.Inordertofindthisaddressoriginally(andby“originally,”Imeanabout100updatesprevious),Ifiguredouthowthegame’snetworkprotocolworked,setbreakpointsonmanynetwork-relatedAPIcalls,steppedthroughexecution,andinspecteddataonthestackuntilIfoundsomethingthatlookedsimilartowhatIexpectedgivenmyknowledgeoftheprotocol.

TIPSFORWINNINGTHEUPDATERACEInsaturatedmarkets,beingthefirstbotdevelopertoreleaseastableupdateiscriticaltosuccess.Theracestartsthesecondthegameupdates,andhackersdeterminedtobethefastestwillspendhundredsofhourspreparing.Thesearethemostcommonwaystostayontop:

CreateupdatealarmsBywritingsoftwarethatalertsyouassoonasthegamepatches,youcanbeginworkingonyourupdatesassoonaspossible.

AutomatebotinstallsGamesoftenscheduleexpectedupdatesattimeswhenthefewestplayersareonline.Bottershatewakingupand

downloadingnewsoftwarebeforetheybot,buttheylovewakinguptofinditsilentlyinstalledwhilethegameispatching.

UsefeweraddressesThelessthereistoupdate,thebetter.Consolidatingrelateddataintostructuresandeliminatingunnecessarymemoryaddressusagecansaveabunchoftime.

HavegreattestcasesDatachanges,andhackersmakemistakes.Havingwaystoquicklytesteveryfeaturecanbethedifferencebetweenastablebotandonethatrandomlycrashes,getsuserskilled,orevenleadstotheircharactersbeingbannedfromthegame.

Attackingupdateswiththesepracticeswillgiveyouasizableheadstart,buttheymightnotalwaysbeenoughtoleadyoutovictory.Aboveallelse,strivetounderstandreverseengineeringasmuchaspossibleandusethatunderstandingtoyouradvantage.

Icouldhavefollowedthesamestepsforeachofthe100+updatessincethen,butthatwouldhavebeenunnecessary.Thecodestayedrelativelythesamethroughouttheyears,whichletmeusepatternsfromtheoldcodetofindthatfunctioncall’saddressinthenewcode.

Now,considerthischunkofassemblycode:

PUSHEDIPUSHEAXLEAEAX,DWORDPTRSS:[EBP-C]MOVDWORDPTRFS:[0],EAXMOVDWORDPTRSS:[EBP-10],ESPMOVDWORDPTRSS:[EBP-220],-1MOVDWORDPTRSS:[EBP-4],0

Doesitlookfamiliar?CompareittoFigure5-4,andyou’llseethatthisexactcodeexistsrightabovethehighlightedfunctioncallinbothversionsofthegame.Regardlessofwhatitdoes,thecombinationofoperationslooksprettydistinctive;becauseofthenumberofdifferentoffsetsthecodeisusingrelativetoEBP,it’sunlikelythatanidenticalchunkofcodeexistsinanyotherpartofthebinary.

EverytimeIhavetoupdatethisaddress,IopentheoldbinaryinOllyDbg,highlightthischunkofoperations,right-click,andselect

Asm2Clipboard▸Copyfixedasmtoclipboard.Then,IopenthenewbinaryinOllyDbg,navigatetotheCPUWindow,pressCTRL-S,pastetheassemblycode,andhitFind.In9.5casesoutof10,thisplacesmedirectlyabovethefunctioncallIneedtofindinthenewversion.

Whenanupdatecomes,youcanusethesamemethodtofindnearlyallofyourknownaddresses.Itshouldworkforeveryaddressyoucanfindeasilyinassemblycode.Thereareafewcaveats,though:

•OllyDbglimitssearchtoeightoperations,soyoumustfindcodemarkersofthatsizeorsmaller.

•Theoperationsyouusecannotcontainanyotheraddresses,asthoseaddresseshavelikelychanged.

•Ifpartsofthegamehavechangedthatusetheaddressyou’relookingfor,thecodemightbedifferent.

•Ifthegamechangescompilersorswitchesoptimizationsettings,almostallcodewillbeentirelydifferent.

Asdiscussedin“AutomaticallyFindcurrentHealthandmaxHealth”onpage102,youcanbenefitfromwritingscriptsthatcarryoutthesetasksforyou.Seriousgamehackersworkveryhardtoautomaticallylocateasmanyaddressesaspossible,andsomeofthebestbotsareengineeredtoautomaticallydetecttheiraddressesatruntime,everytime.Itcanbealotofworkinitially,buttheinvestmentcandefinitelypayoff.

IdentifyingComplexStructuresinGameDataChapter4describedhowagamemightstoredatainstaticstructures.Thisknowledgewillsufficewhenyou’retryingtofindsimpledata,butitfallsshortfordatathatisstoredthroughdynamicstructures.Thisisbecausedynamicstructuresmightbescatteredacrossdifferentmemorylocations,followlongpointerchains,orrequirecomplexalgorithmstoactuallyextractthedatafromthem.

Thissectionexplorescommondynamicstructuresyou’llfindinvideogamecode,andhowtoreaddatafromthemoncethey’refound.Tobegin,I’lltalkabouttheunderlyingcompositionofeachdynamicstructure.Next,I’lloutlinethealgorithmsneededtoreadthedatafromthesestructures.(For

simplicity,eachalgorithmdiscussionassumesyouhaveapointertoaninstanceofthestructureaswellassomewaytoreadfrommemory.)Lastly,I’llcovertipsandtricksthatcanhelpyoudeterminewhenavalueyou’researchingforinmemoryisactuallyencapsulatedinoneofthesestructures,soyou’llknowwhentoapplythisknowledge.I’llfocusonC++,asitsobject-orientednatureandheavilyusedstandardlibraryaretypicallyresponsibleforsuchstructures.

NOTE

Someofthesestructuresmightdifferslightlyfrommachinetomachinebasedoncompilers,optimizationsettings,orstandardlibraryimplementations,butthebasicconceptswillremainthesame.Also,intheinterestofbrevity,Iwillbeomittingirrelevantpartsofthesestructures,suchascustomallocatorsorcomparisonfunctions.Workingexamplecodecanbefoundathttps://www.nostarch.com/gamehacking/intheresourcefilesforChapter5.

Thestd::stringClassInstancesofstd::stringareamongthemostcommonculpritsofdynamicstorage.ThisclassfromtheC++StandardTemplateLibrary(STL)abstractsstringoperationsawayfromthedeveloperwhilepreservingefficiency,makingitwidelyusedinalltypesofsoftware.Avideogamemightusestd::stringstructureforanystringdata,suchascreaturenames.

ExaminingtheStructureofastd::stringWhenyoustripawaythememberfunctionsandothernondatacomponentsofthestd::stringclass,thisisthestructurethatremains:

classstring{union{char*dataP;chardataA[16];};intlength;};

//pointtoastringinmemory

string*_str=(string*)stringAddress;

Theclassreserves16charactersthatarepresumablyusedtostorethestringinplace.Italso,however,declaresthatthefirst4bytescanbeapointertoacharacter.Thismightseemodd,butit’saresultofoptimization.Atsomepoint,thedevelopersofthisclassdecidedthat15characters(plusanullterminator)wasasuitablelengthformanystrings,andtheychosetosaveonmemoryallocationsandde-allocationsbyreserving16bytesofmemoryinadvance.Toaccommodatelongerstrings,theyallowedthefirst4bytesofthisreservedmemorytobeusedasapointertothecharactersoftheselongerstrings.

NOTE

Ifthecodewerecompiledto64bits,thenitwouldactuallybethefirst8(not4)bytesthatpointtoacharacter.Throughoutthisexample,however,youcanassume32-bitaddressesandthatintisthesizeofanaddress.

Accessingstringdatathiswaytakessomeoverhead.Thefunctiontolocatetherightbufferlookssomethinglikethis:

constchar*c_str(){if(_str->length<=15)return(constchar*)&_str->dataA[0];elsereturn(constchar*)_str->dataP;}

Thefactthatastd::stringcanbeeitheracompletestringorapointertoalongerstringmakesthisparticularstructurequitetrickyfromagame-hackingperspective.Somegamesmayusestd::stringtostorestringsthatonlyrarelyexceed15characters.Whenthisisthecase,youmightimplementbotsthatrelyonthesestrings,neverknowingthattheunderlyingstructureisinfactmorecomplicatedthanasimplestring.

Overlookingastd::stringCanRuinYourFunNotknowingthetruenatureofthestructurecontainingthedatayouneedcanleadyoutowriteabotthatworksonlysomeofthetimeandfailswhenitcounts.Imagine,forexample,thatyou’retryingtofigureouthowagame

storescreaturedata.Inyourhypotheticalsearch,youfindthatallthecreaturesinthegamearestoredinanarrayofstructuresthatlooksomethinglikeListing5-3.

structcreatureInfo{intuniqueID;charname[16];intnameLength;inthealthPercent;intxPosition;intyPosition;intmodelID;

intcreatureType;};

Listing5-3:Howyoumightinterpretcreaturedatafoundinmemory

Afterscanningthecreaturedatainmemory,sayyounoticethatthefirst4bytesofeachstructureareuniqueforeachcreature,soyoucallthosebytestheuniqueIDandassumetheyformasingleintproperty.Lookingfurtherinthememory,youfindthatthecreature’snameisstoredrightafteruniqueID,andaftersomededuction,youfigureoutthenameis16byteslong.ThenextvalueyouseeinmemoryturnsouttobethenameLength;it’sabitstrangethatanull-terminatedstringhasanassociatedlength,butyouignorethatoddityandcontinueanalyzingthedatainmemory.Afterfurtheranalysis,youdeterminewhattheremainingvaluesarefor,definethestructureshowninListing5-3,andwriteabotthatautomaticallyattackscreatureswithcertainnames.

AfterweeksoftestingyourbotwhilehuntingcreatureswithnameslikeDragon,Cyclops,Giant,andHound,youdecideit’stimetogiveyourbottoyourfriends.Fortheinauguraluse,yougathereveryonetogethertokillabossnamedSuperBossmanSupreme.TheentireteamsetsthebottoattackthebossfirstandtargetlessercreatureslikeaDemonorGrimReaperwhenthebossgoesoutofrange.

Onceyourteamarrivesattheboss’sdungeon...you’reallslowlyobliterated.

Whatwentwronginthisscenario?Yourgamemustbestoringcreaturenameswithstd::string,notjustasimplecharacterarray.ThenameandnameLengthfieldsincreatureInfoare,infact,partofastd::stringfield,andthenamecharacterarrayisaunionofdataAanddataPmembers.Super

BossmanSupremeislongerthan15characters,andbecausethebotwasnotawareofthestd::stringimplementation,itdidn’trecognizetheboss.Instead,itconstantlyretargetedsummonedDemoncreatures,effectivelykeepingyoufromtargetingthebosswhileheslowlydrainedyourhealthandsupplies.

DeterminingWhetherDataIsStoredinastd::stringWithoutknowinghowthestd::stringclassisstructured,you’dhavetroubletrackingdownbugslikethehypotheticaloneIjustdescribed.Butpairwhatyou’velearnedherewithexperience,andyoucanavoidthesekindsofbugsentirely.Whenyoufindastringlikenameinmemory,don’tjustassumeit’sstoredinasimplearray.Tofigureoutwhetherastringisinfactastd::string,askyourselfthesequestions:

•Whyisthestringlengthpresentforanull-terminatedstring?Ifyoucan’tthinkofagoodreason,thenyoumayhaveastd::stringonyourhands.

•Dosomecreatures(orothergameelements,dependingonwhatyou’relookingfor)havenameslongerthan16letters,butyoufindroomforonly16charactersinmemory?Ifso,thedataisalmostdefinitelystoredinastd::string.

•Isthenamestoredinplace,requiringthedevelopertousestrcpy()tomodifyit?It’sprobablyastd::string,becauseworkingwithrawCstringsinthiswayisconsideredbadpractice.

Finally,keepinmindthatthereisalsoaclasscalledstd::wstringthatisusedtostorewidestrings.Theimplementationisverysimilar,butwchar_tisusedinplaceofeverychar.

Thestd::vectorClassGamesmustkeeptrackofmanydynamicarraysofdata,butmanagingdynamicallysizedarrayscanbeverytricky.Forspeedandflexibility,gamedevelopersoftenstoresuchdatausingatemplatedSTLclasscalledstd::vectorinsteadofasimplearray.

ExaminingtheStructureofastd::vector

AdeclarationofthisclasslookssomethinglikeListing5-4.

template<typenameT>classvector{T*begin;T*end;T*reservationEnd;};

Listing5-4:Anabstractedstd::vectorobject

Thistemplateaddsanextralayerofabstraction,soI’llcontinuethisdescriptionusingastd::vectordeclaredwiththeDWORDtype.Here’showagamemightdeclarethatvector:

std::vector<DWORD>_vec;

Now,let’sdissectwhatastd::vectorofDWORDobjectswouldlooklikeinmemory.Ifyouhadtheaddressof_vecandsharedthesamememoryspace,youcouldre-createtheunderlyingstructureoftheclassandaccess_vecasshowninListing5-5.

classvector{DWORD*begin;DWORD*end;DWORD*tail;};//pointtoavectorinmemoryvector*_vec=(vector*)vectorAddress;

Listing5-5:ADWORDstd::vectorobject

Youcantreatthememberbeginlikearawarray,asitpointstothefirstelementinthestd::vectorobject.Thereisnoarraylengthmember,though,soyoumustcalculatethevector’slengthbasedonbeginandend,whichisanemptyobjectfollowingthefinalobjectinthearray.Thelengthcalculationcodelookslikethis:

intlength(){return((DWORD)_vec->end-(DWORD)_vec->begin)/sizeof(DWORD);}

Thisfunctionsimplysubtractstheaddressstoredinbeginfromtheaddressstoredinendtofindthenumberofbytesbetweenthem.Then,to

calculatethenumberofobjects,itdividesthenumberofbytesbythenumberofbytesperobject.

Usingbeginandthislength()function,youcansafelyaccesselementsin_vec.Thatcodewouldlooksomethinglikethis:

DWORDat(intindex){if(index>=_vec->length())thrownewstd::out_of_range();return_vec->begin[index];}

Givenanindex,thiscodewillfetchanitemfromthevector.Butiftheindexisgreaterthanthevector’slength,astd::out_of_rangeexceptionwillbethrown.Addingvaluestoastd::vectorwouldbeveryexpensiveiftheclasscouldn’treserveorreusememory,though.Toremedythis,theclassimplementsafunctioncalledreserve()thattellsthevectorhowmanyobjectstoleaveroomfor.

Theabsolutesizeofastd::vector(itscapacity)isdeterminedthroughanadditionalpointer,whichiscalledtailinthevectorclasswe’vere-created.Thecalculationforthecapacityresemblesthelengthcalculation:

intcapacity(){return((DWORD)_vec->tail-(DWORD)_vec->begin)/sizeof(DWORD);}

Tofindthecapacityofastd::vector,insteadofsubtractingthebeginaddressfromtheendaddress,asyouwouldtocalculatelength,thisfunctionsubtractsthebeginaddressfromtail.Additionally,youcanusethiscalculationathirdtimetodeterminethenumberoffreeelementsinthevectorbyusingtailandendinstead:

intfreeSpace(){return((DWORD)_vec->tail-(DWORD)_vec->end)/sizeof(DWORD);}

Givenpropermemoryreadingandwritingfunctions,youcanusethedeclarationinListing5-4andthecalculationsthatfollowtoaccessandmanipulatevectorsinthememoryofagame.Chapter6discussesreadingmemoryindetail,butfornow,let’slookatwaysyoucandeterminewhetherdatayou’reinterestedinisstoredinastd::vector.

DeterminingWhetherDataIsStoredinastd::vectorOnceyou’vefoundanarrayofdatainagame’smemory,thereareafewstepsyoucanfollowtodeterminewhetheritisstoredinastd::vector.First,youcanbesurethatthearrayisnotstoredinastd::vectorifithasastaticaddress,becausestd::vectorobjectsrequirepointerpathstoaccesstheunderlyingarray.Ifthearraydoesrequireapointerpath,havingafinaloffsetof0wouldindicateastd::vector.Toconfirm,youcanchangethefinaloffsetto4andcheckifitpointstothefinalobjectinthearrayinsteadofthefirstone.Ifso,you’realmostdefinitelylookingatavector,asyou’vejustconfirmedthebeginandendpointers.

Thestd::listClassSimilartostd::vector,std::listisaclassthatyoucanusetostoreacollectionofitemsinalinkedlist.Themaindifferencesarethatstd::listdoesn’trequireacontiguousstoragespaceforelements,cannotdirectlyaccesselementsbytheirindex,andcangrowinsizewithoutaffectinganypreviouselements.Duetotheoverheadrequiredtoaccessitems,itisraretoseethisclassusedingames,butitshowsupinsomespecialcases,whichI’lldiscussinthissection.

ExaminingtheStructureofastd::listThestd::listclasslookssomethinglikeListing5-6.

template<typenameT>classlistItem{listItem<T>*next;listItem<T>*prev;Tvalue;};

template<typenameT>classlist{listItem<T>*root;intsize;};

Listing5-6:Anabstractedstd::listobject

Therearetwoclasseshere:listItemandlist.Toavoidextraabstractionwhileexplaininghowstd::listworks,I’lldescribethisobjectasitwould

lookwhenthetypeisDWORD.Here’showagamewoulddeclareastd::listoftheDWORDtype:

std::list<DWORD>_lst;

Giventhatdeclaration,thestd::listisstructuredlikethecodeinListing5-7.

classlistItem{listItem*next;listItem*prev;DWORDvalue;};classlist{listItem*root;intsize;};//pointtoalistlist*_lst=(list*)listAddress;

Listing5-7:ADWORDstd::listobject

Theclasslistrepresentsthelistheader,whilelistItemrepresentsavaluestoredinthelist.Insteadofbeingstoredcontiguously,theitemsinthelistarestoredindependently.Eachitemcontainsapointertotheitemthatcomesafterit(next)andtheonethatcomesbeforeit(prev),andthesepointersareusedtolocateitemsinthelist.Therootitemactsasamarkerfortheendofthelist;thenextpointerofthelastitempointstoroot,asdoestheprevpointerofthefirstitem.Therootitem’snextandprevpointersalsopointtothefirstitemandthelastitem,respectively.Figure5-5showswhatthislookslike.

Giventhisstructure,youcanusethefollowingcodetoiterateoverastd::listobject:

Figure5-5:Astd::listflowchart

//iterateforwardlistItem*it=_lst->root->next;for(;it!=_lst->root;it=it->next)printf("Valueis%d\n",it->value);

//iteratebackwardlistItem*it=_lst->root->prev;for(;it!=_lst->root;it=it->prev)printf("Valueis%d\n",it->value);

Thefirstloopstartsatthefirstitem(root->next)anditeratesforward(it=it->next)untilithitstheendmarker(root).Thesecondloopstartsatthelastitem(root->pres)anditeratesbackward(it=it->prev)untilithitstheendmarker(root).Thisiterationreliesonnextandprevbecauseunlikeobjectsinanarray,objectsinastd::listarenotcontiguous.Sincethememoryofeachobjectinastd::listisnotcontiguous,there’snoquick-and-dirtywaytocalculatethesize.Instead,theclassjustdefinesasizemember.Additionally,theconceptofreservingspacefornewobjectsisirrelevantforlists,sothere’snovariableorcalculationtodeterminealist’scapacity.

DeterminingWhetherGameDataIsStoredinastd::listIdentifyingobjectsstoredinthestd::listclasscanbetricky,butthereareafewhintsyoucanwatchfor.First,itemsinastd::listcannothavestaticaddresses,soifthedatayouseekhasastaticaddress,thenyou’reintheclear.Itemsthatareobviouslypartofacollectionmay,however,bepartofastd::listifthey’renotcontiguousinmemory.

Alsoconsiderthatobjectsinastd::listcanhaveinfinitelylongpointerchains(thinkit->prev->next->prev->next->prev...),andpointerscanningfortheminCheatEnginewillshowmanymoreresultswhenNoLoopingPointersisturnedoff.

Youcanalsouseascripttodetectwhenavalueisstoredinalinkedlist.Listing5-8showsaCheatEnginescriptthatdoesjustthis.

function_verifyLinkedList(address)localnextItem=readInteger(address)or0localpreviousItem=readInteger(address+4)or0localnextItemBack=readInteger(nextItem+4)localpreviousItemForward=readInteger(previousItem)

return(address==nextItemBackandaddress==previousItemForward)end

functionisValueInLinkedList(valueAddress)foraddress=valueAddress-8,valueAddress-48,-4doif(_verifyLinkedList(address))thenreturnaddressendendreturn0end

localnode=isValueInLinkedList(addressOfSomeValue)if(node>0)thenprint(string.format("ValueinLL,topofnodeat0x0%x",node))end

Listing5-8:Determiningwhetherdataisinastd::listusingaCheatEngineLuascript

There’squiteabitofcodehere,butwhatit’sdoingisactuallyprettysimple.TheisValueInLinkedList()functiontakesanaddressofsomevalueandthenlooksbackwardforupto40bytes(10integerobjects,incasethevalueisinsomelargerstructure),starting8bytesabovetheaddress(twopointersmustbepresent,andtheyare4byteseach).Becauseofmemoryalignment,thisloopiteratesinstepsof4bytes.

Oneachiteration,theaddressispassedtothe_verifyLinkedList()function,whichiswherethemagichappens.Ifwelookatitintermsoflinkedliststructureasdefinedinthischapter,thefunctionsimplydoesthis:

return(node->next->prev==node&&node->prev->next==node)

Thatis,thefunctionbasicallyassumesthememoryaddressit’sgivenpointstoalinkedlist,anditmakessurethesupposednodehasvalidnextandpreviousnodes.Ifthenodesarevalid,theassumptionwascorrectandtheaddressisthatofalinkedlistnode.Ifthenodesdon’texistordon’tpointtotherightlocations,theassumptionwaswrongandtheaddressisnotpartofalinkedlist.

Keepinmindthatthisscriptwon’tgiveyoutheaddressofthelist’srootnodebutsimplytheaddressofthenodecontainingthevalueyou’vegivenit.Toproperlytraversealinkedlist,you’llneedtoscanforavalidpointerpathtotherootnode,soyou’llneeditsaddress.

Findingthataddresscanrequiresomesearchingofmemorydumps,alotoftrialanderror,andatonofheadscratching,butit’sdefinitelypossible.Thebestwaytostartistofollowthechainofprevandnextnodesuntilyoufindanodewithdatathatiseitherblank,nonsensical,orfilledwiththevalue0xBAADF00D(some,butnotall,standardlibraryimplementationsusethisvaluetomarkrootnodes).

Thisinvestigationcanalsobemadeeasierifyouknowexactlyhowmanynodesareinthelist.Evenwithoutthelistheader,youcandeterminetheamountofnodesbycontinuouslyfollowingthenextpointeruntilyouendupbackatyourstartingnode,asinListing5-9.

functioncountLinkedListNodes(nodeAddress)localcounter=0localnext=readInteger(nodeAddress)while(next~=nodeAddress)docounter=counter+1next=readInteger(next)endreturncounterend

Listing5-9:Determiningthesizeofanarbitrarystd::listusingaCheatEngineLuascript

First,thisfunctioncreatesacountertostorethenumberofnodesandavariabletostorethenextnode’saddress.Thewhilelooptheniteratesoverthenodesuntilitendsupbackattheinitialnode.Finally,itreturnsthecountervariable,whichwasincrementedoneveryiterationoftheloop.

FINDTHEROOTNODEWITHASCRIPTIt’sactuallypossibletowriteascriptthatcanfindtherootnode,butI’llleaveitasanoptionalexerciseforyou.Howdoesitwork?Well,therootnodemustbeinthechainofnodes,thelistheaderpointstotheroot,andthesizeofthelistwillimmediatelyfollowtherootinmemory.Giventhisinformation,youcanwriteascriptthatwillsearchforanymemorycontainingapointertooneofthelist’snodes,followedbythesizeofthelist.Moreoftenthannot,thispieceofmemoryisthelistheader,andthenodeitpointstoistherootnode.

Thestd::mapClassLikeastd::list,astd::mapuseslinksbetweenelementstoformitsstructure.Uniquetostd::map,however,isthefactthateachelementstorestwopiecesofdata(akeyandavalue),andsortingtheelementsisaninherentpropertyoftheunderlyingdatastructure:ared-blacktree.Thefollowingcodeshowsthestructuresthatcomposeastd::map.

template<typenamekeyT,typenamevalT>structmapItem{mapItem<keyT,valT>*left;mapItem<keyT,valT>*parent;mapItem<keyT,valT>*right;keyTkey;valTvalue;};

template<typenamekeyT,typenamevalT>structmap{DWORDirrelevant;mapItem<keyT,valT>*rootNode;intsize;}

Ared-blacktreeisaself-balancingbinarysearchtree,soastd::mapis,too.IntheSTL’sstd::mapimplementation,eachelement(ornode)inthetreehasthreepointers:left,parent,andright.Inadditiontothepointers,eachnodealsohasakeyandavalue.Thenodesarearrangedinthetreebasedonacomparisonbetweentheirkeys.Theleftpointerofanodepointstoanodewithasmallerkey,andtherightpointerpointstoanodewithalargerkey.Theparentpointstotheuppernode.ThefirstnodeinthetreeiscalledtherootNode,andnodesthatlackchildrenpointtoit.

Visualizingastd::mapFigure5-6showsastd::mapthathasthekeys1,6,8,11,13,15,17,22,25,and27.

Figure5-6:Ared-blacktree

Thetopnode(holdingthevalue13)ispointedtobytheparentofrootNode.Everythingtotheleftofithasasmallerkey,andeverythingtotherighthasagreaterkey.Thisistrueforanynodeinthetree,andthistruthenablesefficientkey-basedsearch.Whilenotrepresentedintheimage,theleftpointeroftherootnodewillpointtotheleftmostnode(1),andtherightpointerwillpointtotherightmostnode(27).

AccessingDatainastd::mapOnceagain,I’lluseastaticstd::mapdefinitionwhendiscussinghowtoextractdatafromthestructure.Sincethetemplatetakestwotypes,I’llalsousesomepseudotypestokeepthingsobvious.Here’sthedeclarationforthestd::mapobjectI’llreferencefortherestofthesection:

typedefintkeyInt;typedefintvalInt;std::map<keyInt,valInt>myMap;

Withthisdeclaration,thestructureofmyMapbecomes:

structmapItem{mapItem*left;mapItem*parent;mapItem*right;keyIntkey;valIntvalue;};structmap{DWORDirrelevant;mapItem*rootNode;intsize;}map*_map=(map*)mapAddress;

Therearesomeimportantalgorithmsthatyoumightneedtoaccessthedatainastd::mapstructureinagame.First,blindlyiteratingovereveryiteminthemapcanbeusefulifyoujustwanttoseeallofthedata.Todothissequentially,youcouldwriteaniterationfunctionlikethis:

voiditerateMap(mapItem*node){if(node==_map->rootNode)return;iterateMap(node->left);printNode(node);iterateMap(node->right);}

Afunctiontoiterateoveranentiremapwouldfirstreadthecurrentnodeandcheckwhetherit’stherootNode.Ifnot,itwouldrecurseleft,printthenode,andrecurseright.

Tocallthisfunction,you’dhavetopassapointertotherootNodeasfollows:

iterateMap(_map->rootNode->parent);

Thepurposeofastd::map,however,istostorekeyeddatainaquicklysearchableway.Whenyouneedtolocateanodegivenaspecifickey,mimickingtheinternalsearchalgorithmispreferabletoscanningtheentiretree.Thecodeforsearchingastd::maplookssomethinglikethis:

mapItem*findItem(keyIntkey,mapItem*node){if(node!=_map->rootNode){if(key==node->key)returnnode;elseif(key<node->key)returnfindItem(key,node->left);

elsereturnfindItem(key,node->right);}elsereturnNULL;}

Startingatthetopofthetree,yousimplyrecurseleftifthecurrentkeyisgreaterthanthesearchkeyandrecurserightifitissmaller.Ifthekeysareequal,youreturnthecurrentnode.Ifyoureachthebottomofthetreeanddon’tfindthekey,youreturnNULLbecausethekeyisn’tstoredinthemap.

Here’sonewayyoumightusethisfindItem()function:

mapItem*ret=findItem(someKey,_map->rootNode->parent);if(ret)printNode(ret);

AslongasfindItem()doesn’treturnNULL,thiscodeshouldprintanodefrom_map.

DeterminingWhetherGameDataIsStoredinastd::mapTypically,Idon’tevenconsiderwhetherdatacouldbeinastd::mapuntilIknowthecollectionisnotanarray,astd::vector,orastd::list.Ifyouruleoutallthreeoptions,thenaswithastd::list,youcanlookatthethreeintegervaluesbeforethevalueandcheckiftheypointtomemorythatcouldpossiblybeothermapnodes.

Onceagain,thiscanbedonewithaLuascriptinCheatEngine.ThescriptissimilartotheoneIshowedforlists,loopingbackwardovermemorytoseeifavalidnodestructureisfoundbeforethevalue.Unlikethelistcode,though,thefunctionthatverifiesanodeismuchtrickier.TakealookatthecodeinListing5-10,andthenI’lldissectit.

function_verifyMap(address)localparentItem=readInteger(address+4)or0

localparentLeftItem=readInteger(parentItem+0)or0localparentRightItem=readInteger(parentItem+8)or0

➊localvalidParent=parentLeftItem==addressorparentRightItem==addressif(notvalidParent)thenreturnfalseend

localtries=0

locallastChecked=parentItemlocalparentsParent=readInteger(parentItem+4)or0➋while(readInteger(parentsParent+4)~=lastCheckedandtries<200)dotries=tries+1lastChecked=parentsParentparentsParent=readInteger(parentsParent+4)or0end

returnreadInteger(parentsParent+4)==lastCheckedend

Listing5-10:Determiningwhetherdataisinastd::mapusingaCheatEngineLuascript

Givenaddress,thisfunctionchecksifaddressisinamapstructure.Itfirstchecksifthere’savalidparentnodeand,ifso,checkswhetherthatparentnodepointstoaddressoneitherside➊.Butthischeckisn’tenough.Ifthecheckpasses,thefunctionwillalsoclimbupthelineofparentnodesuntilitreachesanodethatistheparentofitsownparent➋,trying200timesbeforecallingitquits.Iftheclimbsucceedsinfindinganodethatisitsowngrandparent,thenaddressdefinitelypointstoamapnode.Thisworksbecause,asIoutlinedin“Visualizingastd::map”onpage114,atthetopofeverymapisarootnodewhoseparentpointstothefirstnodeinthetree,andthatnode’sparentpointsbacktotherootnode.

NOTE

Ibetyoudidn’texpecttorunintothegrandfatherparadoxfromtimetravelwhenreadingagame-hackingbook!

UsingthisfunctionandaslightlymodifiedbacktrackingloopfromListing5-8,youcanautomaticallydetectwhenavalueisinsideamap:

functionisValueInMap(valueAddress)foraddress=valueAddress-12,valueAddress-52,-4doif(_verifyMap(address))thenreturnaddressendendreturn0end

localnode=isValueInMap(addressOfSomeValue)if(node>0)then

print(string.format("Valueinmap,topofnodeat0x0%x",node))end

Asidefromfunctionnames,theonlychangeinthiscodefromListing5-8isthatitstartslooping12bytesbeforethevalueinsteadof8,becauseamaphasthreepointersinsteadofthetwoinalist.Onegoodconsequenceofamap’sstructureisthatit’seasytoobtaintherootnode.Whenthe_verifyMapfunctionreturnstrue,theparentsParentvariablewillcontaintheaddressoftherootnode.Withsomesimplemodifications,youcouldreturnthistothemaincallandhaveeverythingyouneedtoreadthedatafromastd::mapinoneplace.

ClosingThoughtsMemoryforensicsisthemosttime-consumingpartofhackinggames,anditsobstaclescanappearinallshapesandsizes.Usingpurpose,patterns,andadeepunderstandingofcomplexdatastructures,however,youcanquicklyovercometheseobstacles.Ifyou’restillabitconfusedaboutwhat’sgoingon,makesuretodownloadandplaywiththeexamplecodeprovided,asitcontainsproofsofconceptforallofthealgorithmscoveredinthischapter.

InChapter6,we’llstartdivingintothecodeyouneedtoreadfromandwritetoagame’smemoryfromyourownprogramssoyoucantakethefirststepinputtingtoworkallofthisinformationaboutmemorystructures,addresses,anddata.

6READINGFROMANDWRITINGTOGAME

MEMORY

EarlierchaptersdiscussedhowmemoryisstructuredaswellashowtoscanandmodifymemoryusingCheatEngineandOllyDbg.Workingwithmemorywillbeessentialwhenyoubegintowritebots,andyourcodewillneedtoknowhowtodoso.

Thischapterdigsintothecode-leveldetailsofmemorymanipulation.First,you’lllearnhowtousecodetolocateandobtainhandlestogameprocesses.Next,you’lllearnhowtousethosehandlestoreadfromandwritetomemorybothfromremoteprocessesandfrominjectedcode.Towrapup,you’lllearnbypassesforacertainmemoryprotectiontechnique,completewithasmallexampleofcodeinjection.You’llfindtheexamplecodeforthischapterintheGameHackingExamples/Chapter6_AccessingMemorydirectoryinthisbook’ssourcefiles.

NOTE

WhenItalkaboutAPIfunctionsinthischapter(andinlaterones),I’mreferringtotheWindowsAPIunlessotherwisespecified.IfIdon’tmentionaheaderfileforthelibrary,youcanassumeitisWindows.h.

ObtainingtheGame’sProcessIdentifierToreadfromorwritetoagame’smemory,youneeditsprocessidentifier(PID),anumberthatuniquelyidentifiesanactiveprocess.Ifthegamehasavisiblewindow,youcanobtainthePIDoftheprocessthatcreatedthatwindowbycallingGetWindowThreadProcessId().Thisfunctiontakesthewindow’shandleasthefirstparameterandoutputsthePIDtothesecondparameter.Youcanfindthewindow’shandlebypassingitstitle(thetextonthetaskbar)asthesecondparametertoFindWindow(),asshowninListing6-1.

HWNDmyWindow=FindWindow(NULL,"Titleofthegamewindowhere");DWORDPID;GetWindowThreadProcessId(myWindow,&PID);

Listing6-1:Fetchingawindow’shandletoobtainaPID

Withthewindowhandlesecured,allyouhavetodoiscreateaplacetostorethePIDandcallGetWindowThreadProcessId(),asshowninthisexample.

Ifagameisn’twindowedorthewindownameisn’tpredictable,youcanfindthegame’sPIDbyenumeratingallprocessesandlookingforthenameofthegamebinary.Listing6-2doesthisusingtheAPIfunctionsCreateToolhelp32Snapshot(),Process32First(),andProcess32Next()fromtlhelp32.h.

#include<tlhelp32.h>

PROCESSENTRY32entry;entry.dwSize=sizeof(PROCESSENTRY32);HANDLEsnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL);if(Process32First(snapshot,&entry)==TRUE){while(Process32Next(snapshot,&entry)==TRUE){wstringbinPath=entry.szExeFile;if(binPath.find(L"game.exe")!=wstring::npos){printf("gamepidis%d\n",entry.th32ProcessID);break;}}}CloseHandle(snapshot);

Listing6-2:Fetchingagame’sPIDwithoutthewindowname

Listing6-2mightlookabitmorecomplexthanListing6-1,butunderneathallthatcode,thefunctionisactuallylikeacanonicalfor(iterator;comparator;increment)loop.TheCreateToolhelp32Snapshot()functionobtainsalistofprocessesnamedsnapshot,andentryisaniteratoroverthatlist.ThevaluereturnedbyProcess32First()initializestheiterator,whileProcess32Next()incrementsit.Finally,theBooleanreturnvalueofProcess32Next()isthecomparator.Thiscodejustiteratesoverasnapshotofeveryrunningprocess,looksforonewhosebinarypathcontainsthetextgame.exe,andprintsitsPID.

ObtainingProcessHandlesOnceyouknowagame’sPID,youcanobtainahandletotheprocessitselfusinganAPIfunctioncalledOpenProcess().Thisfunctionallowsyoutofetchhandleswiththeaccesslevelsyouneedtoreadfromandwritetomemory.Thisiscrucialtogamehacking,asanyfunctionthatoperatesonaprocesswillrequireahandlewithproperaccess.

Let’stakealookattheprototypeofOpenProcess():

HANDLEOpenProcess(DWORDDesiredAccess,BOOLInheritHandle,DWORDProcessId);

Thefirstparameter,DesiredAccess,expectsoneoramixtureofprocessaccessflagstosetonthehandlethatOpenProcess()returns.Therearemanyflagsyoucanuse,butthesearethemostcommoningamehacking:

PROCESS_VM_OPERATIONThereturnedhandlecanbeusedwithVirtualAllocEx(),VirtualFreeEx(),andVirtualProtectEx()toallocate,free,andprotectchunksofmemory,respectively.

PROCESS_VM_READThereturnedhandlecanbeusedwithReadProcessMemory().

PROCESS_VM_WRITEThereturnedhandlecanbeusedwithWriteProcessMemory(),butitmustalsohavePROCESS_VM_OPERATIONrights.YoucansetbothflagsbypassingPROCESS_VM_OPERATION|PROCESS_VM_WRITEastheDesiredAccessparameter.

PROCESS_CREATE_THREADThereturnedhandlecanbeusedwithCreateRemoteThread().

PROCESS_ALL_ACCESSThereturnedhandlecanbeusedtodoanything.Avoidusingthisflag,asitcanonlybeusedbyprocesseswithdebugprivilegesenabledandhascompatibilityissueswitholderversionsofWindows.

Whenfetchingahandletoagame,youcantypicallyjustsettheOpenProcess()function’ssecondparameter,InheritHandle,tofalse.Thethirdparameter,ProcessId,expectsthePIDoftheprocesstobeopened.

WorkingwithOpenProcess()Nowlet’swalkthroughanexamplecalltoOpenProcess()thatusesahandlewithaccesspermissionsallowingittoreadfromandwritetomemory:

DWORDPID=getGamePID();HANDLEprocess=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE,

FALSE,PID);➊if(process==INVALID_HANDLE_VALUE){printf("FailedtoopenPID%d,errorcode%d",PID,GetLastError());}

First,thecalltogetGamePID()fetchesthePIDyou’relookingfor.(Thefunctionissomethingyou’llhavetowriteyourself,thoughitcouldjustbeoneofthesnippetsIshowedinListings6-1and6-2,fleshedoutintoafullblownfunction.)Then,thecodecallsOpenProcess()withthreeflags:thePROCESS_VM_OPERATIONflaggivesthishandlememoryaccesspermissions,andtheothertwocombinedgiveitreadandwritepermissions.Thisexamplealsocontainsanerror-handlingcase➊,butaslongasyouhavethecorrectPID,youhavevalidaccessflags,andyourcodeisrunningunderthesameorhigherpermissionsasthegame(forexample,ifyoustartyourbotusingRunAsAdmin),thecallshouldneverfail.

Onceyou’redoneusingahandle,cleanitupusingCloseHandle()asfollows:

CloseHandle(process);

CloseHandle(process);

Youcanreusehandlesasmuchasyouwant,soyoucanleaveoneopenuntilyou’recompletelydoneusingitoruntilyourbotisexited.

Nowthatyou’veseenhowtoopenaprocesshandleinpreparationformanipulatinggamememory,let’sdigintohowtoactuallyaccessthememoryofthatprocess.

AccessingMemoryTheWindowsAPIexposestwofunctionsthatarecrucialtomemoryaccess:ReadProcessMemory()andWriteProcessMemory().Youcanusethesefunctionstoexternallymanipulateagame’smemory.

WorkingwithReadProcessMemory()andWriteProcessMemory()Theprototypesforthesetwofunctions(showninListing6-3)resembleeachotherclosely,andyou’llfollowalmostexactlythesamestepstousethem.

BOOLReadProcessMemory(HANDLEProcess,LPVOIDAddress,LPVOIDBuffer,DWORDSize,DWORD*NumberOfBytesRead);BOOLWriteProcessMemory(HANDLEProcess,LPVOIDAddress,LPCVOIDBuffer,DWORDSize,

DWORD*NumberOfBytesWritten);

Listing6-3:ReadProcessMemory()andWriteProcessMemory()prototypes

BothfunctionsexpectProcesstobeaprocesshandleandAddresstobethetargetmemoryaddress.Whenthefunctionisreadingfrommemory,Bufferisexpectedtopointtoanobjectthatwillholdthereaddata.Whenthefunctioniswritingtomemory,Bufferisexpectedtopointtothedatatowrite.Inbothcases,SizedefinesthesizeofBuffer,inbytes.Thefinalparametertobothfunctionsisusedtooptionallyreturnthenumberofbytesthatwereaccessed;youcansafelysetittoNULL.Unlessthefunctionfails,the

valuereturnedinthefinalparametershouldbeequaltoSize.

AccessingaValueinMemorywithReadProcessMemory()andWriteProcessMemory()ThecodeinListing6-4showshowyoumightusethesefunctionstoaccessavalueinmemory.

DWORDval;ReadProcessMemory(proc,adr,&val,sizeof(DWORD),0);printf("Currentmemvalueis%d\n",val);

val++;

WriteProcessMemory(proc,adr,&val,sizeof(DWORD),0);ReadProcessMemory(proc,adr,&val,sizeof(DWORD),0);printf("Newmemvalueisconfirmedas%d\n",val);

Listing6-4:ReadingfromandwritingtoprocessmemoryusingtheWindowsAPI

Beforecodelikethisappearsinaprogram,youneedtofindthePID(proc)asdescribedin“ObtainingtheGame’sProcessIdentifier”onpage120,aswellasthememoryaddress(adr)youwanttoreadfromorwriteto.Withthosevaluesinplace,theReadProcessMemory()functionstoresafetchedvaluefrommemoryinval.Then,thecodeincrementsvalandreplacestheoriginalvaluebycallingWriteProcessMemory().Afterthewritetakesplace,ReadProcessMemory()iscalledonthesameaddresstoconfirmthenewmemoryvalue.Noticethatvalisn’tactuallyabuffer.Passing&valastheBufferparameterworksbecauseitcanbeapointertoanystaticmemorystructure,aslongasSizematches.

WritingTemplatedMemoryAccessFunctionsOfcourse,theexampleinListing6-4assumesyoualreadyknowwhattypeofmemoryyou’redealingwith,andithardcodesthetypeasDWORD.Tobeaversatilegamehacker,it’sbettertohavesomegenericcodeinyourtoolboxtoavoidduplicatingcodefordifferenttypes.GenericmemoryreadingandwritingfunctionsthatsupportdifferenttypesmightlooklikeListing6-5.

template<typenameT>

TreadMemory(HANDLEproc,LPVOIDadr){Tval;ReadProcessMemory(proc,adr,&val,sizeof(T),NULL);returnval;}

template<typenameT>voidwriteMemory(HANDLEproc,LPVOIDadr,Tval){WriteProcessMemory(proc,adr,&val,sizeof(T),NULL);}

Listing6-5:Genericmemoryfunctions

ThesefunctionsuseC++templatestoacceptarbitrarytypesasarguments.Theyallowyoutoaccessmemorywithwhatevertypesyoulikeinaverycleanway.Forexample,giventhesereadMemory()andwriteMemory()templatesIjustshowed,youcouldmakethecallsinListing6-6.

DWORDvalue=readMemory<DWORD>(proc,adr);//readwriteMemory<DWORD>(proc,adr,value++);//incrementandwrite

Listing6-6:Callingtemplatedmemoryaccessfunctions

ComparethistothecallstoWriteProcessMemory()andReadProcessMemory()inListing6-4.Thiscodestillreadsavalue,incrementsit,andwritesthenewvaluetomemory.Butsincethetemplatedfunctionsletyouspecifythetypewhenyoucallthem,youdon’tneedanewreadMemory()andwriteMemory()functionforeverydatatypeyoumightneedtoworkwith.That’smuchcleaner,sinceyou’lloftenwanttoworkwithallkindsofdata.

MemoryProtectionWhenmemoryisallocatedbyagame(oranyprogram),itisplacedinapage.Inx86Windows,pagesarechunksof4,096bytesthatstoredata.Becauseallmemorymustbewithinapage,theminimalallocationunitis4,096bytes.Theoperatingsystemcanplacememorychunkssmallerthan4,096bytesasasubsetofanexistingpagethathasenoughuncommittedspace,inanewlyallocatedpage,oracrosstwocontiguouspagesthathavethesameattributes.

Memorychunks4,096bytesorlargerspannpages,wherenis

Theoperatingsystemtypicallylooksforroominexistingpageswhenallocatingmemory,butitallocatesnewpagesondemandifnecessary.

NOTE

It’salsopossibleforlargechunkstospann+1pages,asthere’snoguaranteethatachunkbeginsatthestartofapage.

Theimportantthingtounderstandaboutmemorypagesisthateachpagehasasetofspecificattributes.Mostoftheseattributesaretransparentinusermode,butthere’soneyoushouldbeextraconsciousofwhenworkingwithmemory:protection.

Differentiatingx86WindowsMemoryProtectionAttributesThememory-readingtechniquesyou’velearnedsofarareverybasic.Theyassumethatthememoryyou’reaccessingisprotectedwiththePAGE_READWRITEattribute.Whilethisassumptioniscorrectforvariabledata,othertypesofdataexistonpageswithdifferenttypesofprotection.Table6-1describesthedifferenttypesofmemoryprotectioninx86Windows.

Table6-1:MemoryProtectionTypes

Protectiontype Value Readpermission?

Writepermission?

Executepermission?

Specialpermissions?

PAGE_NOACCESS 0x01 No No No PAGE_READONLY 0x02 Yes No No PAGE_READWRITE 0x04 Yes No No PAGE_WRITECOPY 0x08 Yes Yes No Yes,copyon

writePAGE_EXECUTE 0x10 No No Yes PAGE_EXECUTE_READ 0x20 Yes No Yes

PAGE_EXECUTE_READ 0x20 Yes No Yes PAGE_EXECUTE_READWRITE 0x40 Yes Yes Yes

PAGE_EXECUTE_WRITECOPY 0x80 Yes Yes Yes Yes,copyonwrite

PAGE_GUARD 0x100 No No No Yes,guardpage

IfaprotectiontypeinTable6-1hasaYesinanypermissioncolumn,itmeanstheactioninquestioncanbeperformedonthatpageofmemory.Forexample,ifapageisPAGE_READONLY,thenaprogramcanreadthememoryonthatpage,buttheprogramcannotwritetothatmemory.

Constantstrings,forexample,areusuallystoredwithPAGE_READONLYprotection.Otherconstantdata,suchasvirtualfunctiontablesandamodule’sentirePortableExecutable(PE)header(whichcontainsinformationaboutaprogram,suchasthekindofapplicationitis,libraryfunctionsituses,itssize,andsoon),arealsostoredonread-onlypages.Assemblycode,ontheotherhand,isstoredonpagesprotectedwithPAGE_EXECUTE_READ.

Mostprotectiontypesinvolveonlysomecombinationofread,write,andexecuteprotection.Fornow,youcansafelyignorespecialprotectiontypes;Icoverthemin“SpecialProtectionTypes”onpage126ifyou’recurious,butonlyveryadvancedhackswilleverrequireknowledgeofthem.Thebasicprotectiontypes,though,willbeprevalentinyourgame-hackingadventures.

SPECIALPROTECTIONTYPESTwoprotectiontypesinTable6-1includecopy-on-writeprotection.Whenmultipleprocesseshavepagesofmemorythatareidentical(suchaspageswithmappedsystemDLLs),copy-on-writeprotectionisusedtoconservememory.Theactualdataisstoredinonlyonephysicalplace,andtheoperatingsystemvirtuallymapsallmemorypagescontainingthatdatatothephysicallocation.Ifaprocesssharingthememorymakesachangetoit,acopyofthedatawillbemadeinphysicalmemory,thechangewillbeapplied,andthememorypage(s)forthatprocesswillberemappedtothenewphysical

memory.Whenacopyonwritehappens,theprotectionforallaffectedpageschangesaccordingly;PAGE_WRITECOPYwillbecomePAGE_READWRITE,andPAGE_EXECUTE_WRITECOPYwillbecomePAGE_EXECUTE_READWRITE.I’vefoundnogamehacking–specificusesforcopy-on-writepages,butit’susefultounderstandthem.

Pagescanalsobecreatedwithguardprotection.Guardedpagesmusthaveasecondaryprotection,definedlikePAGE_GUARD|PAGE_READONLY.Whentheprogramtriestoaccessaguardedpage,theoperatingsystemwillthrowaSTATUS_GUARD_PAGE_VIOLATIONexception.Oncetheexceptionishandled,theguardprotectionisremovedfromthepage,leavingonlythesecondaryprotection.Onewayinwhichtheoperatingsystemusesthistypeofprotectionistodynamicallyexpandthecallstackbyplacingaguardedpageatthetopandallocatingmorememorywhenthatguardedpageishit.Somememoryanalysistoolsplaceguardedpagesafterheapmemorytodetectheapcorruptionbugs.Inthecontextofgamehacking,aguardedpagecanbeusedasatripwirethattellsyouwhenagamemightbeattemptingtodetectyourcodewithinitsmemory.

ChangingMemoryProtectionWhenyouwanttohackagame,you’llsometimesneedtoaccessmemoryinawaythatisforbiddenbythememorypage’sprotection,makingitimportanttobeabletochangememoryprotectionatwill.Luckily,theWindowsAPIprovidestheVirtualProtectEx()functionforthispurpose.Thisisthefunction’sprototype:

BOOLVirtualProtectEx(HANDLEProcess,LPVOIDAddress,DWORDSize,DWORDNewProtect,PDWORDOldProtect);

TheparametersProcess,Address,andSizetakethesameinputastheydointheReadProcessMemory()andWriteProcessMemory()functions.NewProtectshouldspecifythenewprotectionflagsforthememory,andOldProtectcanoptionallypointtoaDWORDwheretheoldprotectionflagswillbestored.

Themostgranularscaleformemoryprotectionisperpage,whichmeansVirtualProtectEx()willsetthenewprotectiontoeverypagethatisonorbetweenAddressandAddress+Size-1.

NOTE

TheVirtualProtectEx()functionhasasistercalledVirtualProtect().Theyworkthesameway,butVirtualProtect()operatesonlyontheprocesscallingitand,thus,doesnothaveaprocesshandleparameter.

Whenyou’rewritingyourowncodetochangememoryprotections,Isuggestmakingitflexiblebycreatingatemplate.AgenericwrappedfunctionforVirtualProtectEx()shouldlooksomethinglikeListing6-7.

template<typenameT>DWORDprotectMemory(HANDLEproc,LPVOIDadr,DWORDprot){DWORDoldProt;VirtualProtectEx(proc,adr,sizeof(T),prot,&oldProt);returnoldProt;}

Listing6-7:Agenericfunctiontochangememoryprotection

Withthistemplateinplace,ifyouwantedto,say,writeaDWORDtoamemorypagewithoutwritepermission,youmightdosomethinglikethis:

protectMemory<DWORD>(process,address,PAGE_READWRITE)writeMemory<DWORD>(process,address,newValue)

First,thissetstheprotectiononthememorytochangetoPAGE_READWRITE.Withwritepermissiongranted,thedoorisopentocallwriteMemory()andchangethedataataddress.

Whenyou’rechangingmemoryprotection,it’sbestpracticetoletthechangepersistonlyaslongasneededandrestoretheoriginalprotectionassoonaspossible.Thisislessefficient,butitensuresthatagamedoesn’tdetectyourbot(forexample,bynoticingthatsomeofitsassemblycodepageshavebecomewritable).

Atypicalwriteoperationonread-onlymemoryshouldlooklikethis:

DWORDoldProt=protectMemory<DWORD>(process,address,PAGE_READWRITE);

writeMemory<DWORD>(process,address,newValue);protectMemory<DWORD>(process,address,oldProt);

ThiscodecallstheprotectMemory()functionfromListing6-7tochangetheprotectiontoPAGE_READWRITE.ItthenwritesnewValuetothememorybeforechangingtheprotectionbacktooldProt,whichwassettothepage’soriginalprotectionbytheinitialcalltoprotectMemory().ThewriteMemory()functionusedhereisthesameonedefinedinListing6-5.

Afinalimportantpointisthatwhenyou’remanipulatingagame’smemory,it’sentirelypossiblethatthegamewillaccessthememoryatthesametimeasyou.Ifthenewprotectionthatyousetisnotcompatiblewiththeoriginalprotection,thegameprocesswillgetanACCESS_VIOLATIONexceptionandcrash.Forinstance,ifyouchangememoryprotectionfromPAGE_EXECUTEtoPAGE_READWRITE,thegamemighttrytoexecutethecodeonthepage(s)whenthememoryisnotmarkedasexecutable.Inthiscase,you’dwanttoinsteadsetthememoryprotectiontoPAGE_EXECUTE_READWRITEtoensurethatyoucanoperateonthememorywhilestillallowingthegametoexecuteit.

AddressSpaceLayoutRandomizationSofar,I’vedescribedmemoryaddressesasstaticintegersthatchangeonlyasthebinarychanges.ThismodeliscorrectonWindowsXPandearlier.OnlaterWindowssystems,however,memoryaddressesareonlystaticrelativetothebaseaddressofthegamebinary,becausethesesystemsenableafeaturecalledaddressspacelayoutrandomization(ASLR)forsupportedbinaries.WhenabinaryiscompiledwithASLRsupport(enabledbydefaultonMSVC++2010andmanyothercompilers),itsbaseaddresscanbedifferenteverytimeitisrun.Conversely,non-ASLRbinarieswillalwayshaveabaseaddressof0x400000.

NOTE

SinceASLRdoesn’tworkonXP,I’llcall0x400000theXP-base.

DisablingASLRtoSimplifyBotDevelopmentTokeepdevelopmentsimple,youcandisableASLRanduseaddresseswith

Tokeepdevelopmentsimple,youcandisableASLRanduseaddresseswiththetransparentXP-base.Todoso,enterasinglecommandintheVisualStudioCommandPrompt:

>editbin/DYNAMICBASE:NO"C:\path\to\game.exe"

Tore-enableit,enter:

>editbin/DYNAMICBASE"C:\path\to\game.exe"

BypassingASLRinProductionDisablingASLRissuitableforbotdevelopment,butitisano-noforproduction;enduserscannotbeexpectedtoturnoffALSR.Instead,youcanwriteafunctiontodynamicallyrebaseaddressesatruntime.IfyouuseaddresseswiththeXP-base,thecodetodoarebasewouldlooklikethis:

DWORDrebase(DWORDaddress,DWORDnewBase){DWORDdiff=address-0x400000;returndiff+newBase;}

Whenyouknowthebaseaddressofthegame(newBase),thisfunctionallowsyoutoessentiallyignoreASLRbyrebasingaddress.

TofindnewBase,however,youneedtousetheGetModuleHandle()function.WhentheparametertoGetModuleHandle()isNULL,italwaysreturnsahandletothemainbinaryinaprocess.Thefunction’sreturnedtypeisHMODULE,butthevalueisactuallyjusttheaddresswherethebinaryismapped.Thisisthebaseaddress,soyoucandirectlycastittoaDWORDtogetnewBase.Sinceyou’relookingforthebaseaddressinanotherprocess,though,youneedawaytoexecutethefunctioninthecontextofthatprocess.

Todothis,callGetModuleHandle()usingtheCreateRemoteThread()APIfunction,whichcanbeusedtospawnthreadsandexecutecodeinaremoteprocess.IthastheprototypeshowninListing6-8.

HANDLECreateRemoteThread(HANDLEProcess,LPSECURITY_ATTRIBUTESThreadAttributes,DWORDStackSize,LPTHREAD_START_ROUTINEStartAddress,LPVOIDParam,

DWORDCreationFlags,LPDWORDThreadId);

Listing6-8:Afunctionthatspawnsathread

ThespawnedthreadwillstartexecutiononStartAddress,treatingitasasingle-parameterfunctionwithParamasinputandsettingthevaluereturnedasthethreadexitcode.Thisisideal,asthethreadcanbestartedwithStartAddresspointingtotheaddressofGetModuleHandle()andParamsettoNULL.YoucanthenusetheAPIfunctionWaitForSingleObject()towaituntilthethreadisdoneexecutingandgetthereturnedbaseaddressusingtheAPIfunctionGetExitCodeThread().

Onceallofthesethingsaretiedtogether,thecodetogetnewBasefromanexternalbotshouldlooklikeListing6-9.

DWORDnewBase;

//gettheaddressofkernel32.dllHMODULEk32=GetModuleHandle("kernel32.dll");

//gettheaddressofGetModuleHandle()LPVOIDfuncAdr=GetProcAddress(k32,"GetModuleHandleA");if(!funcAdr)funcAdr=GetProcAddress(k32,"GetModuleHandleW");

//createthethreadHANDLEthread=CreateRemoteThread(process,NULL,NULL,(LPTHREAD_START_ROUTINE)funcAdr,NULL,NULL,NULL);

//letthethreadfinishWaitForSingleObject(thread,INFINITE);

//gettheexitcodeGetExitCodeThread(thread,&newBase);

//cleanupthethreadhandleCloseHandle(thread);

Listing6-9:FindingthebaseaddressofagamewithAPIfunctions

TheGetModuleHandle()functionispartofkernel32.dll,whichhasthesamebaseaddressineveryprocess,sofirstthiscodegetstheaddressforkernel32.dll.Sincethebaseaddressofkernel32.dllisthesameinevery

process,theaddressofGetModuleHandle()willbethesameinthegameasitisintheexternalbot.Giventhebaseaddressofkernel32.dll,thiscodefindstheaddressofGetModuleHandle()easilywiththeAPIfunctionGetProcAddress().Fromthere,itcallstheCreateRemoteThread()functionfromListing6-8,letsthethreaddoitsjob,andfetchestheexitcodetoobtainnewBase.

ClosingThoughtsNowthatyou’veseenhowtomanipulatememoryfromyourowncode,I’llshowyouhowtoapplytheskillsfromPartsIandIItogames.Theseskillsareparamounttotheconceptsyou’llexploreinthecomingchapters,somakesureyouhaveafirmgrasponwhat’shappening.Ifyou’rehavingtrouble,playwiththeexamplecodeasyoureviewconcepts,asitprovidesasafesandboxfortestingandtweakinghowthemethodsinthisandearlierchaptersbehave.

ThewayListing6-9tricksthegameintoexecutingGetModuleHandle()isaformofcodeinjection.Butthat’sjustaglimpseintowhatinjectioncando.Ifyou’reexcitedtolearnmoreaboutit,diveintoChapter7,whichexploresthistopicindetail.

PART3PROCESSPUPPETEERING

7CODEINJECTION

Imaginebeingabletowalkintoagamecompany’soffice,sitdown,andstartaddingcodetotheirgameclient.Imaginethatyoucandothisforanygameyouwant,wheneveryouwant,andforanyfunctionalityyouwant.Almostanygameryoutalktowillhaveideasonhowtoimproveagame,but,asfarastheyknow,it’sjustapipedream.Butyouknowthatdreamsaremeanttobefulfilled,andnowthatyou’velearnedabitabouthowmemoryworks,you’rereadytostartthrowingtherulesoutthewindow.Usingcodeinjection,youcan,forallintentsandpurposes,becomeaspowerfulasanygame’sdevelopers.

Codeinjectionisameansofforcinganyprocesstoexecuteforeigncodewithinitsownmemoryspaceandexecutioncontext.Itouchedonthistopicpreviouslyin“BypassingASLRinProduction”onpage128,whereIshowedyouhowtoremotelysubvertASLRusingCreateRemoteThread(),butthatexampleonlyscratchedthesurface.Inthefirstpartofthischapter,you’lllearnhowtocreatecodecaves,injectnewthreads,andhijackthreadexecutiontoforcegamestoexecutesmallsnippetsofassemblycode.Inthesecondpart,you’lllearnhowtoinjectforeignbinariesdirectlyintogames,forcingthosegamestoexecuteentireprogramsthatyou’vecreated.

InjectingCodeCaveswithThreadInjection

Thefirststeptoinjectingcodeintoanotherprocessiswritingposition-agnosticassemblycode,knownasshellcode,intheformofabytearray.Youcanwriteshellcodetoremoteprocessestoformcodecaves,whichactastheentrypointforanewthreadthatyouwantagametoexecute.Onceacodecaveiscreated,youcanexecuteitusingeitherthreadinjectionorthreadhijacking.I’llshowyouanexampleofthreadinjectioninthissection,andI’llillustratethreadhijackingin“HijackingaGame’sMainThreadtoExecuteCodeCaves”onpage138.

You’llfindexamplecodeforthischapterinthisbook’sresourcefilesinthedirectoryGameHackingExamples/Chapter7_CodeInjection.Openmain-codeInjection.cpptofollowalongasIexplainhowtobuildasimplifiedversionofthefunctioninjectCodeUsingThreadInjection()fromthatfile.

CreatinganAssemblyCodeCaveIn“BypassingASLRinProduction”onpage128,IusedthreadinjectiontocallthefunctionGetModuleHandle()bywayofCreateRemoteThread()andobtainaprocesshandle.Inthatcase,GetModuleHandle()actedasthecodecave;ithadthepropercodestructuretoactastheentrypointforanewthread.Threadinjectionisn’talwaysthateasy,though.

Forexample,sayyouwantyourexternalbottoremotelycallafunctionwithinagame,andthefunctionhasthisprototype:

DWORD__cdeclsomeFunction(inttimes,constchar*string);

Afewthingsmakeremotelycallingthisfunctiontricky.First,ithastwoparameters,meaningyouneedtocreateacodecavethatwillbothsetupthestackandproperlymakethecall.CreateRemoteThread()allowsyoutopassoneargumenttothecodecave,andyoucanaccessthatargumentrelativetoESP,buttheotheronewouldstillneedtobehardcodedintothecodecave.Hardcodingthefirstargument,times,iseasiest.Additionally,you’dneedtomakesurethatthecaveproperlycleansthestack.

NOTE

RecallthatwhenbypassingASLRinChapter6,IusedCreateRemoteThread()tostartnewthreadsbyexecutinganyarbitrarycodeatagivenaddressandpassingthatcodeasingleparameter.That’swhytheseexamplescanpassone

parameterusingthestack.

Ultimately,thecodecavetoinjectthatcalltosomeFunctionintoarunninggameprocesswouldlooksomethinglikethispseudocode:

PUSHDWORDPTR:[ESP+0x4]//getsecondargfromstackPUSHtimesCALLsomeFunctionADDESP,0x8RETN

Thiscodecaveisalmostperfect,butitcouldbelesscomplex.TheCALLoperationexpectsoneoftwooperands:eitheraregisterwithanabsolutefunctionaddressoranimmediateintegerthatholdsanoffsettoafunction,relativetothereturnaddress.Thismeansyou’dhavetodoabunchofoffsetcalculations,whichcanbetedious.

Tokeepthecavepositionagnostic,modifyittousearegisterinstead,asinListing7-1.

PUSHDWORDPTR:[ESP+0x4]//getsecondargfromstackPUSHtimesMOVEAX,someFunctionCALLEAXADDESP,0x8RETN

Listing7-1:AcodecavetocallsomeFunction

SinceacallerknowsthatafunctionitcallswilloverwriteEAXwithitsreturnvalue,thecallershouldensurethatEAXdoesn’tholdanycriticaldata.Knowingthis,youcanuseEAXtoholdtheabsoluteaddressofsomeFunction.

TranslatingtheAssemblytoShellcodeBecausecodecavesneedtobewrittentoanotherprocess’smemory,theycannotbewrittendirectlyinassembly.Instead,you’llneedtowritethembytebybyte.There’snostandardwaytodeterminewhichbytesrepresentwhichassemblycode,butthereareafewhackyapproaches.MypersonalfavoriteistocompileanemptyC++applicationwiththeassemblycodeinafunctionanduseOllyDbgtoinspectthatfunction.Alternatively,youcouldopenOllyDbgonanyarbitraryprocessandscanthroughthedisassemblyuntilyoufindthebytesforalloftheoperationsyouneed.Thismethodis

untilyoufindthebytesforalloftheoperationsyouneed.Thismethodisactuallyreallygood,asyourcodecavesshouldbewrittenassimplyaspossible,meaningalloftheoperationsshouldbeverycommon.Youcanalsofindchartsofassemblyopcodesonline,butIfindthatthey’reallprettyhardtoread;themethodsIjustdescribedareeasieroverall.

Whenyouknowwhatyourbytesshouldbe,youcanuseC++toeasilygeneratethepropershellcode.Listing7-2showsthefinishedshellcodeskeletonfortheassemblyinListing7-1.

BYTEcodeCave[20]={0xFF,0x74,0x24,0x04,//PUSHDWORDPTR:[ESP+0x4]0x68,0x00,0x00,0x00,0x00,//PUSH00xB8,0x00,0x00,0x00,0x00,//MOVEAX,0x00xFF,0xD0,//CALLEAX0x83,0xC4,0x08,//ADDESP,0x080xC3//RETN};

Listing7-2:Shellcodeskeleton

ThisexamplecreatesaBYTEarraycontainingtheneededbytesofshellcode.Butthetimesargumentneedstobedynamic,andit’simpossibletoknowtheaddressofsomeFunctionatcompiletime,whichiswhythisshellcodeiswrittenasaskeleton.Thetwogroupsoffoursequential0x00bytesareplaceholdersfortimesandtheaddressofsomeFunction,andyoucaninserttherealvaluesintoyourcodecaveatruntimebycallingmemcpy(),asinthesnippetinListing7-3.

memcpy(&codeCave[5],&times,4);memcpy(&codeCave[10],&addressOfSomeFunc,4);

Listing7-3:InsertingtimesandthelocationofsomeFunctionintothecodecave

BothtimesandtheaddressofsomeFunctionare4byteseach(recallthattimesisanintandaddressesare32-bitvalues),andtheybelongatcodeCave[5-8]andcodeCave[10-13],respectively.Thetwocallstomemcpy()passthisinformationasparameterstofilltheblanksinthecodeCavearray.

WritingtheCodeCavetoMemoryWiththepropershellcodecreated,youcanplaceitinsidethetargetprocess

usingVirtualAllocEx()andWriteProcessMemory().Listing7-4showsonewaytodothis.

intstringlen=strlen(string)+1;//+1toincludenullterminatorintcavelen=sizeof(codeCave);➊intfulllen=stringlen+cavelen;autoremoteString=//allocatethememorywithEXECUTErights➋VirtualAllocEx(process,0,fulllen,MEM_COMMIT,PAGE_EXECUTE);

autoremoteCave=//keepanoteofwherethecodecavewillgo➌(LPVOID)((DWORD)remoteString+stringlen);

//writethestringfirst➍WriteProcessMemory(process,remoteString,string,stringlen,NULL);

//writethecodecavenext➎WriteProcessMemory(process,remoteCave,codeCave,cavelen,NULL);

Listing7-4:Writingthefinalshellcodetoacodecavememory

First,thiscodedeterminesexactlyhowmanybytesofmemoryitwillneedtowritethestringargumentandthecodecaveintothegame’smemory,anditstoresthatvalueinfulllen➊.Then,itcallstheAPIfunctionVirtualAllocEx()toallocatefulllenbytesinsideofprocesswithPAGE_EXECUTEprotection(youcanalwaysuse0andMEM_COMMIT,respectively,forthesecondandfourthparameters),anditstorestheaddressofthememoryinremoteString➋.ItalsoincrementsremoteStringbystringlenbytesandstorestheresultinremoteCave➌,astheshellcodeshouldbewrittendirectlytothememoryfollowingthestringargument.Finally,itusesWriteProcessMemory()tofilltheallocatedbufferwithstring➍andtheassemblybytes➎storedincodeCave.

Table7-1showshowamemorydumpofthecodecavemightlook,assumingthatitisallocatedat0x030000,someFunctionisat0xDEADBEEF,timesissetto5,andstringispointingtotheinjected!text.

Table7-1:CodeCaveMemoryDump

Address Coderepresentation

Rawdata Datameaning

0x030000 remoteString[0-4]

0x690x6E0x6A0x650x63

injec

4] 0x63

0x030005 remoteString[5-9] 0x740x650x640x0A0x00

ted!\0

0x03000A remoteCave[0-3] 0xFF0x740x240x04 PUSHDWORDPTR[ESP+0x4]

0x03000E remoteCave[4-8] 0x680x050x000x000x00

PUSH0x05

0x030013 remoteCave[9-13] 0xB80xEF0xBE0xAD0xDE

MOVEAX,0xDEADBEEF

0x030018 remoteCave[14-15] 0xFF0xD0 CALLEAX

0x03001A remoteCave[16-18]

0x830xC40x08 ADDESP,0x08

0x03001D remoteCave[19] 0xC3 RETN

TheAddresscolumnshowswhereeachpieceofthecaveislocatedinmemory;theCoderepresentationcolumntellsyouwhichindexesofremoteStringandremoteCavecorrespondtothebytesintheRawdatacolumn;andtheDatameaningcolumnshowswhatthebytesrepresent,inhuman-readableformat.Youcanseetheinjected!stringat0x030000,thevalueoftimesat0x03000E,andtheaddressofsomeFunctionat0x030014.

UsingThreadInjectiontoExecutetheCodeCaveWithacompletecodecavewrittentomemory,theonlythinglefttodoisexecuteit.Inthisexample,youcouldexecutethecaveusingthefollowingcode:

HANDLEthread=CreateRemoteThread(process,NULL,NULL,(LPTHREAD_START_ROUTINE)remoteCave,remoteString,NULL,NULL);

WaitForSingleObject(thread,INFINITE);CloseHandle(thread);VirtualFreeEx(process,remoteString,fulllen,MEM_RELEASE)

ThecallstoCreateRemoteThread(),WaitForSingleObject(),andCloseHandle()worktoinjectandexecutethecodecave,andVirtalFreeEx()

coversthebot’stracksbyfreeingthememoryallocatedincodelikeListing7-4.Inthesimplestform,that’sallthereistoexecutingacodecaveinjectedintoagame.Inpractice,youshouldalsocheckreturnvaluesaftercallingVirtualAllocEx(),WriteProcessMemory(),andCreateRemoteThread()tomakesurethateverythingwassuccessful.

Forinstance,ifVirtualAllocEx()returns0x00000000,itmeansthatthememoryallocationfailed.Ifyoudon’thandlethefailure,WriteProcessMemory()willalsofailandCreateRemoteThread()willbeginexecutingwithanentrypointof0x00000000,ultimatelycrashingthegame.ThesameistrueforthereturnvaluesofWriteProcessMemory()andCreateRemoteThread().Typically,thesefunctionswillonlyfailwhentheprocesshandleisopenedwithouttherequiredaccessflags.

HijackingaGame’sMainThreadtoExecuteCodeCavesInsomecases,injectedcodecavesneedtobeinsyncwiththemainthreadofthegameprocess.Solvingthisproblemcanbeverytrickybecauseitmeansthatyoumustcontroltheexistingthreadsinanexternalprocess.

Youcouldsimplysuspendthemainthreaduntilthecodecavefinishesexecuting,whichmightwork,butthatwouldproveveryslow.Theoverheadrequiredtowaitforacodecaveandthenresumeathreadisprettyheavy.Afasteralternativeistoforcethethreadtoexecutethecodeforyou,aprocesscalledthreadhijacking.

NOTE

Openthemain-codeInjection.cppfileinthisbook’ssourcecodefilestofollowalongwithbuildingthisthread-hijackingexample,whichisasimplifiedversionofinjectCodeUsingThreadHijacking().

BuildingtheAssemblyCodeCaveAswiththreadinjection,thefirststeptothreadhijackingisknowingwhatyouwanttohappeninyourcodecave.Thistime,however,youdon’tknow

whatthethreadwillbeexecutingwhenyouhijackit,soyou’llneedtomakesuretosavethethread’sstatewhenthecodecavestartsandrestorethestatewhenyou’redonehijackingit.Thismeansyourshellcodeneedstobewrappedinsomeassembly,asinListing7-5.

PUSHAD//pushgeneralregisterstothestackPUSHFD//pushEFLAGStothestack

//shellcodeshouldbehere

POPFD//popEFLAGSfromthestackPOPAD//popgeneralregisterstothestack

//resumethethreadwithoutusingregistershere

Listing7-5:Aframeworkforthethread-hijackingcodecave

IfyouweretocallthesamesomeFunctionthatyoudidwiththreadinjection,youcoulduseshellcodesimilartothatinListing7-2.Theonlydifferenceisthatyoucouldn’tpassthesecondparametertoyourbotusingthestackbecauseyouwouldn’tbeusingCreateRemoteThread().Butthat’snoproblem;youcouldjustpushitthesamewayyou’dpushthefirstparameter.ThepartofthecodecavethatexecutesthefunctionyouwanttocallwouldneedtolooklikeListing7-6.

PUSHstringPUSHtimesMOVEAX,someFunctionCALLEAXADDESP,0x8

Listing7-6:AssemblyskeletonforcallingsomeFunction

Allthat’schangedherefromListing7-1isthatthisexamplepushesstringexplicitlyandthere’snoRETN.Youdon’tcallRETNinthiscasebecauseyouwantthegamethreadtogobacktowhateveritwasdoingbeforeyouhijackedit.

Toresumetheexecutionofthethreadnormally,thecaveneedstojumpbacktothethread’soriginalEIPwithoutusingregisters.Fortunately,youcanusetheGetThreadContext()functiontofetchEIP,fillingtheshellcodeskeletoninC++.Thenyoucanpushittothestackinsideyourcodecaveanddoareturn.Listing7-7showshowyourcodecavewouldneedtoend.

PUSHoriginalEIP

PUSHoriginalEIPRETN

Listing7-7:JumpingtoEIPindirectly

Areturnjumpstothevalueonthetopofthestack,sodoingthisimmediatelyafterpushingEIPwilldothetrick.Youshouldusethismethodinsteadofajump,becausejumpsrequireoffsetcalculationandmaketheshellcodeabitmorecomplextogenerate.IfyoutieListings7-5through7-7together,youcomeupwiththefollowingcodecave:

//savestatePUSHAD//pushgeneralregisterstothestackPUSHFD//pushEFLAGStothestack

//doworkwithshellcodePUSHstringPUSHtimesMOVEAX,someFunctionCALLEAXADDESP,0x8

//restorestatePOPFD//popEFLAGSfromthestackPOPAD//popgeneralregisterstothestack

//un-hijack:resumethethreadwithoutusingregistersPUSHoriginalEIPRETN

Next,followtheinstructionsin“TranslatingtheAssemblytoShellcode”onpage135andplugthosebytesintoanarrayrepresentingyourcodecave.

GeneratingSkeletonShellcodeandAllocatingMemoryUsingthesamemethodshowninListing7-2,youcouldgeneratetheshellcodeforthiscave,asshowninListing7-8.

BYTEcodeCave[31]={0x60,//PUSHAD0x9C,//PUSHFD0x68,0x00,0x00,0x00,0x00,//PUSH00x68,0x00,0x00,0x00,0x00,//PUSH00xB8,0x00,0x00,0x00,0x00,//MOVEAX,0x00xFF,0xD0,//CALLEAX0x83,0xC4,0x08,//ADDESP,0x080x9D,//POPFD

0x61,//POPAD0x68,0x00,0x00,0x00,0x00,//PUSH00xC3//RETN};

//we'llneedtoaddsomecodeheretoplace//thethread'sEIPintothreadContext.Eip

memcpy(&codeCave[3],&remoteString,4);memcpy(&codeCave[8],&times,4);memcpy(&codeCave[13],&func,4);memcpy(&codeCave[25],&threadContext.Eip,4);

Listing7-8:Creatingthethread-hijackingshellcodearray

AsinListing7-3,memcpy()isusedtoputthevariablesintotheskeleton.Unlikeinthatlisting,though,therearetwovariablesthatcannotbecopiedrightaway;timesandfuncareknownimmediately,butremoteStringisaresultofallocationandthreadContext.Eipwillbeknownonlyoncethethreadisfrozen.Italsomakessensetoallocatememorybeforefreezingthethread,becauseyoudon’twantthethreadtobefrozenanylongerthanithastobe.Here’showthismightlook:

intstringlen=strlen(string)+1;intcavelen=sizeof(codeCave);intfulllen=stringlen+cavelen;

autoremoteString=VirtualAllocEx(process,0,fulllen,MEM_COMMIT,PAGE_EXECUTE);autoremoteCave=(LPVOID)((DWORD)remoteString+stringlen);

Theallocationcodeisthesameasitwasforthreadinjection,soyoucanreusethesamesnippet.

FindingandFreezingtheMainThreadThecodetofreezethemainthreadisabittrickier.First,yougetthethread’suniqueidentifier.ThisworksmuchlikegettingaPID,andyoucandoitusingCreateToolhelp32Snapshot(),Thread32First(),andThread32Next()fromTlHelp32.h.Asdiscussedin“ObtainingtheGame’sProcessIdentifier”onpage120,thesefunctionsareusedtoessentiallyiterateoveralist.Aprocesscanhavemanythreads,butthefollowingexampleassumesthatthefirstthreadthegameprocesscreatedistheonethatneedstobehijacked:

DWORDGetProcessThreadID(HANDLEProcess){THREADENTRY32entry;entry.dwSize=sizeof(THREADENTRY32);HANDLEsnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0);

if(Thread32First(snapshot,&entry)==TRUE){DWORDPID=GetProcessId(Process);while(Thread32Next(snapshot,&entry)==TRUE){if(entry.th32OwnerProcessID==PID){CloseHandle(snapshot);returnentry.th32ThreadID;}}}CloseHandle(snapshot);returnNULL;}

Thiscodesimplyiteratesoverthelistofallthreadsonthesystemandfindsthefirstonethatmatchesthegame’sPID.Thenitgetsthethreadidentifierfromthesnapshotentry.Onceyouknowthethreadidentifier,fetchthethread’scurrentregisterstatelikethis:

HANDLEthread=OpenThread((THREAD_GET_CONTEXT|THREAD_SUSPEND_RESUME|THREAD_SET_CONTEXT),false,threadID);SuspendThread(thread);

CONTEXTthreadContext;threadContext.ContextFlags=CONTEXT_CONTROL;GetThreadContext(thread,&threadContext);

ThiscodeusesOpenThread()togetathreadhandle.ItthensuspendsthethreadusingSuspendThread()andobtainsthevaluesofitsregistersusingGetThreadContext().Afterthis,thememcpy()codeinListing7-8shouldhaveallofthevariablesitneedstofinishgeneratingtheshellcode.

Withtheshellcodegenerated,thecodecavecanbewrittentotheallocatedmemoryinthesamefashionasinListing7-4:

WriteProcessMemory(process,remoteString,string,stringlen,NULL);WriteProcessMemory(process,remoteCave,codeCave,cavelen,NULL);

Oncethecaveisreadyandwaitinginmemory,allyouneedtodoissetthethread’sEIPtotheaddressofthecodecaveandletthethreadresumeexecution,asfollows:

threadContext.Eip=(DWORD)remoteCave;threadContext.ContextFlags=CONTEXT_CONTROL;SetThreadContext(thread,&threadContext);ResumeThread(thread);

Thiscodecausesthethreadtoresumeexecutionattheaddressofthecodecave.Becauseofthewaythecodecaveiswritten,thethreadhasnocluethatanythinghaschanged.Thecavestoresthethread’soriginalstate,executesthepayload,restoresthethread’soriginalstate,andthenreturnstotheoriginalcodewitheverythingintact.

Whenyou’reusinganyformofcodeinjection,itisalsoimportanttounderstandwhatdatayourcodecavestouch.Forexample,ifyouweretocreateacodecavethatcallsagame’sinternalfunctionstocreateandsendanetworkpacket,you’dneedtomakesurethatanyglobalvariablesthatthefunctionstouch(likeapacketbuffer,packetpositionmarker,andsoon)aresafelyrestoredonceyou’redone.Youneverknowwhatthegameisdoingwhenyourcodecaveisexecuted—itcouldbecallingthesamefunctionasyou!

InjectingDLLsforFullControlCodecavesareverypowerful(youcanmakeagamedoanythingusingassemblyshellcode),buthandcraftingshellcodeisn’tpractical.ItwouldbemuchmoreconvenienttoinjectC++code,wouldn’tit?That’spossible,buttheprocessisfarmorecomplex:thecodemustbecompiledtoassembly,packagedinaposition-agnosticformat,madeawareofanyexternaldependencies,entirelymappedintomemory,andthenexecutedonsomeentrypoint.

Luckily,allofthesethingsarealreadytakencareofinWindows.BychangingaC++projecttocompileasadynamiclibrary,youcancreateaself-contained,position-agnosticbinarycalledadynamiclinklibrary(DLL).ThenyoucanuseamixofthreadinjectionorhijackingandtheLoadLibrary()APIfunctiontomapyourDLLfileintoagame’smemory.

Openmain-codeInjection.cppintheGameHackingExamples/Chapter7_CodeInjectiondirectoryanddllmain.cppunderGameHackingExamples/Chapter7_CodeInjection_DLLtofollowalongwithsomeexamplecodeasyoureadthissection.Inmain-codeInjection.cpp,lookat

theLoadDLL()functionspecifically.

TrickingaProcessintoLoadingYourDLLUsingacodecave,youcantrickaremoteprocessintoinvokingLoadLibrary()onaDLL,effectivelyloadingforeigncodeintoitsmemoryspace.BecauseLoadLibrary()takesonlyasingleparameter,youcouldcreateacodecavetocallitasfollows:

//writethedllnametomemorywchar_t*dllName="c:\\something.dll";intnamelen=wcslen(dllName)+1;LPVOIDremoteString=VirtualAllocEx(process,NULL,namelen*2,MEM_COMMIT,PAGE_EXECUTE);WriteProcessMemory(process,remoteString,dllName,namelen*2,NULL);

//gettheaddressofLoadLibraryW()HMODULEk32=GetModuleHandleA("kernel32.dll");LPVOIDfuncAdr=GetProcAddress(k32,"LoadLibraryW");

//createathreadtocallLoadLibraryW(dllName)HANDLEthread=CreateRemoteThread(process,NULL,NULL,(LPTHREAD_START_ROUTINE)funcAdr,remoteString,NULL,NULL);

//letthethreadfinishandcleanupWaitForSingleObject(thread,INFINITE);CloseHandle(thread);

Thiscodeissomewhatamixofthethreadinjectioncodefrom“BypassingASLRinProduction”onpage128andthecodecavecreatedtocallsomeFunctioninListings7-2and7-3.Liketheformer,thisexampleusesthebodyofasingle-parameterAPIfunction,namelyLoadLibrary,asthebodyofthecodecave.Likethelatter,though,ithastoinjectastringintomemory,sinceLoadLibraryexpectsastringpointerasitsfirstargument.Oncethethreadisinjected,itforcesLoadLibrarytoloadtheDLLwhosenamewasinjectedintomemory,effectivelyputtingforeigncodeintoagame.

NOTE

GiveanyDLLyouplantoinjectauniquename,like

MySuperBotV2Hook.dll.Simplernames,suchasHook.dllorInjected.dll,aredangerouslygeneric.IfthenameconflictswithaDLLthatisalreadyloaded,LoadLibrary()willassumethatitisthesameDLLandnotloadit!

OncetheLoadLibrary()codecaveloadsyourDLLintoagame,theDLL’sentrypoint—knownasDllMain()—willbeexecutedwithDLL_PROCESS_ATTACHasthereason.WhentheprocessiskilledorFreeLibrary()iscalledontheDLL,itsentrypointwillbecalledwiththeDLL_PROCESS_DETACHreason.Handlingtheseeventsfromtheentrypointmightlooklikethis:

BOOLAPIENTRYDllMain(HMODULEhModule,DWORDul_reason_for_call,LPVOIDlpReserved){switch(ul_reason_for_call){caseDLL_PROCESS_ATTACH:printf("DLLattached!\n");break;caseDLL_PROCESS_DETACH:printf("DLLdetached!\n");break;}returnTRUE;}

ThisexamplefunctionstartsbycheckingwhyDllMain()wascalled.ItthenoutputstextindicatingwhetheritwascalledbecausetheDLLwasattachedordetached,returningTRUEeitherway.

KeepinmindthattheentrypointofaDLLisexecutedinsidealoaderlock,whichisaglobalsynchronizationlockusedbyallfunctionsthatreadormodifythelistofmodulesloadedinaprocess.ThisloaderlockgetsusedbyfunctionslikeGetModuleHandle(),GetModuleFileName(),Module32First(),andModule32Next(),whichmeansthatrunningnontrivialcodefromaDLLentrypointcanleadtodeadlocksandshouldbeavoided.

IfyouneedtoruncodefromaDLLentrypoint,dosofromanewthread,asfollows:

DWORDWINAPIrunBot(LPVOIDlpParam){//runyourbotreturn1;}

//dothisfromDllMain()forcaseDLL_PROCESS_ATTACHautothread=CreateThread(NULL,0,&runBot,NULL,0,NULL);CloseHandle(thread);

FromDllMain(),thiscodecreatesanewthreadstartingonthefunctionrunBot().Itthenimmediatelyclosesitshandletothethread,asdoinganyfurtheroperationsfromDllMain()canleadtoseriousproblems.FrominsidethisrunBot(),youcanbeginexecutingyourbot’scode.Thecoderunsinsidethegame,meaningyoucandirectlymanipulatememoryusingthetype-castingmethods.Youcanalsodoalotmore,asyou’llseeinChapter8.

WheninjectingDLLs,makesureyouhavenodependencyissues.IfyourDLLreliesonsomenonstandardDLLs,forexample,youhavetoeitherinjectthoseDLLsintothegamefirstorputtheminafolderthatLoadLibrary()willsearch,suchasanyfolderinthePATHenvironmentvariable.TheformerwillworkonlyiftheDLLshavenodependenciesoftheirown,whereasthelatterisabittrickytoimplementandsubjecttonamecollisions.ThebestoptionistolinkallexternallibrariesstaticallysothattheyarecompileddirectlyintoyourDLL.

AccessingMemoryinanInjectedDLLWhenyou’retryingtoaccessagame’smemoryfromaninjectedDLL,processhandlesandAPIfunctionsareahindrance.Becauseagamesharesthesamememoryspaceasallcodeinjectedintoit,youcanaccessagame’smemorydirectlyfrominjectedcode.Forexample,toaccessaDWORDvaluefrominjectedcode,youcouldwritethefollowing:

DWORDvalue=*((DWORD*)adr);//readaDWORDfromadr*((DWORD*)adr)=1234;//write1234toDWORDadr

ThissimplytypecaststhememoryaddressadrtoaDWORD*anddereferencesthatpointertoaDWORD.Doingtypecastsinplacelikethatisfine,butyourmemoryaccesscodewilllookcleanerifthefunctionsareabstractedandmadegeneric,justliketheWindowsAPIwrappers.

Thegenericfunctionsforaccessingmemoryfrominsideinjectedcodelooksomethinglikethis:

template<typenameT>TreadMemory(LPVOIDadr){return*((T*)adr);

}

template<typenameT>voidwriteMemory(LPVOIDadr,Tval){*((T*)adr)=val;}

Usingthesetemplatesisjustlikeusingthefunctionsunder“WritingTemplatedMemoryAccessFunctions”onpage123.Here’sanexample:

DWORDvalue=readMemory<DWORD>(adr);//readwriteMemory<DWORD>(adr,value++);//incrementandwrite

ThesecallsarenearlyidenticaltothecallsinListing6-6onpage124;theyjustdon’tneedtotaketheprocesshandleasanargumentbecausethey’rebeingcalledfrominsidetheprocessitself.YoucanmakethismethodevenmoreflexiblebycreatingathirdtemplatedfunctioncalledpointMemory(),asfollows:

template<typenameT>T*pointMemory(LPVOIDadr){return((T*)adr);}

Thisfunctionskipsthedereferencingstepofamemoryreadandsimplygivesyouthepointertothedata.Fromthere,you’refreetobothreadfromandwritetothememorybydereferencingthatpointeryourself,likethis:

DWORD*pValue=pointMemory<DWORD>(adr);//pointDWORDvalue=*pValue;//'read'(*pValue)++;//incrementand'write'

WithafunctionlikepointMemory()inplace,youcouldeliminatethecallstoreadMemory()andwriteMemory().You’dstillneedtofindadraheadoftime,butfromthere,thecodetoreadavalue,changeit,andwriteitbackwouldbemuchsimplertofollow.

BypassingASLRinanInjectedDLLSimilarly,sincethecodeisinjected,there’snoneedtoinjectathreadintothegametogetthebaseaddress.Instead,youcanjustcallGetModuleHandle()directly,likeso:

DWORDnewBase=(DWORD)GetModuleHandle(NULL);

DWORDnewBase=(DWORD)GetModuleHandle(NULL);

Afasterwaytogetthebaseaddressistoutilizethegame’sFSmemorysegment,whichisanothersuperpoweryougetfrominjectedcode.Thismemorysegmentpointstoastructurecalledthethreadenvironmentblock(TEB),and0x30bytesintotheTEBisapointertotheprocessenvironmentblock(PEB)structure.Thesestructuresareusedbytheoperatingsystemandcontainatonofdataaboutthecurrentthreadandthecurrentprocess,butwe’reinterestedonlyinthebaseaddressofthemainmodule,whichisstored0x8bytesintothePEB.Usinginlineassembly,youcantraversethesestructurestogetnewBase,likethis:

DWORDnewBase;__asm{MOVEAX,DWORDPTRFS:[0x30]MOVEAX,DWORDPTRDS:[EAX+0x8]MOVnewBase,EAX}

ThefirstcommandstoresthePEBaddressinEAX,andthesecondcommandreadsthemainmodule’sbaseaddressandstoresitinEAX.ThefinalcommandthencopiesEAXtonewBase.

ClosingThoughtsInChapter6,IshowedyouhowtoreadfrommemoryremotelyandhowaninjectedDLLcandirectlyaccessagame’smemoryusingpointers.Thischapterdemonstratedhowtoinjectalltypesofcode,frompureassemblybytecodetoentireC++binaries.Inthenextchapter,you’lllearnjusthowmuchpowerbeinginagame’smemoryspaceactuallygivesyou.Ifyouthoughtassemblycodeinjectionwascool,you’lllovewhatyoucandowhenyoumixinjectedC++withcontrolflowmanipulation.

Theexamplecodeforthischaptercontainsproofsofconceptforeverythingwe’vediscussed.Ifyou’restillunclearaboutanyofthetopics,youcanpokeatthecodetolearnexactlywhat’sgoingonandseeallofthetricksinaction.

8MANIPULATINGCONTROLFLOWINA

GAME

Forcingagametoexecuteforeigncodeisdefinitelypowerful,butwhatifyoucouldalterthewayagameexecutesitsowncode?Whatifyoucouldforcethegametobypassthecodethatdrawsthefogofwar,trickitintomakingenemiesvisiblethroughwalls,ormanipulatetheargumentsitpassestofunctions?Controlflowmanipulationletsyoudoexactlythat,allowingyoutochangewhataprocessdoesbyinterceptingcodeexecutionandmonitoring,modifying,orpreventingit.

Therearemanywaystomanipulatethecontrolflowofaprocess,butalmostallrequireyoutomodifytheprocess’sassemblycode.Dependingonyourgoals,you’llneedtoeithercompletelyremovecodefromtheprocess(calledNOPing)orforcetheprocesstoredirectexecutiontoinjectedfunctions(calledhooking).Inthebeginningofthischapter,you’lllearnaboutNOPing,severaltypesofhooking,andothercontrolflowmanipulationtechniques.OnceI’veexplainedthebasics,I’llshowyouhowI’veappliedtheseprinciplestocommongamelibrarieslikeAdobeAIRandDirect3D.

OpenthedirectoryGameHackingExamples/Chapter8_ControlFlowinthisbook’sresourcefilestoseethecompletesamplecodeforthenextsectionand“HookingtoRedirectGameExecution”onpage153.

NOPingtoRemoveUnwantedCodeChapter7describedhowtoinjectnewcodeintoagame,buttheopposite—removingcodefromagame—canalsobeuseful.Somehacksrequireyoutostopsomeofagame’soriginalcodefrombeingexecuted,andtodothat,you’llhavetogetridofit.OnewaytoeliminatecodefromagameprocessisNOPing,whichinvolvesoverwritingtheoriginalx86assemblycodewithNOPinstructions.

WhentoNOPConsideragamethatwon’tshowthehealthbarsofcloakedenemies.It’sprettyhardtoseecloakedenemiescoming,andyou’dhaveahugeadvantageincombatifyoucouldatleastseetheirhealthbars.ThecodetodrawhealthbarsoftenlookslikeListing8-1.

for(inti=0;i<creatures.size();i++){autoc=creatures[i];if(c.isEnemy&&c.isCloaked)continue;drawHealthBar(c.healthBar);}

Listing8-1:TheloopfromthedrawCreatureHealthBarExample()function

Whendrawinghealthbars,agamewithcloakedcreaturesmightuseaforlooptocheckwhetherthecreatureswithinthescreen’sboundsarecloaked.Ifanenemyisn’tcloaked,theloopcallssomefunction(drawHealthBar()inthisexample)todisplaytheenemy’shealthbar.

Giventhesourcecode,youcouldforcethegametodrawevencloakedenemies’healthbarsbysimplyremovingif(c.isEnemy&&c.isCloaked)continue;fromthecode.Butasagamehacker,youhaveonlytheassemblycode,notthesourcecode.Whensimplified,theassemblythatListing8-1translatesintolookssomethinglikethispseudocode:

startOfLoop:;forMOVi,0;inti=0JMPcondition;firstloop,skipincrementincrement:ADDi,1;i++condition:CMPi,creatures.Size();i<creatures.size()JNBendOfLoop;exitloopifi>=

creatures.size()body:MOVc,creatures[i];autoc=creatures[i]TESTc.isEnemy,c.isEnemy;ifc.isEnemyJZdrawHealthBar;drawbarifc.isEnemy==falseTESTc.isCloaked,c.isCloaked;&&c.isCloakedJZdrawHealthBar;drawbarifc.isCloaked==false➊JMPincrement;continuedrawHealthBar:CALLdrawHealthBar(c.healthBar);drawHealthBar(c.healthBar)JMPincrement;continueendOfLoop:

Totrickthegameintodrawingallenemyhealthbars,regardlessofcloaking,you’dneedtoremovetheJMPincrementcommand➊thatexecuteswhenc.isEnemy&&c.isCloakedevaluatestotrue.Inassembly,though,replacingunwantedcodewithinstructionsthatdonothingiseasierthandeletingcode.That’swheretheNOPcommandcomesin.SinceNOPisasinglebyte(0x90),youcanoverwritethe2-byteJMPincrementcommandwithtwoNOPcommands.WhentheprocessorreachesthoseNOPcommands,itrollsoverthemandfallsintodrawHealthBar()evenwhenc.isEnemy&&c.isCloakedevaluatestotrue.

HowtoNOPThefirststeptoNOPingachunkofassemblycodeismakingthememorychunkwherethecodeliveswritable.It’spossibleforthecodeonthesamememorypagetobeexecutedwhileyou’rewritingtheNOPcommands,though,soyoualsowanttomakesurethememoryisstillexecutable.Youcanaccomplishbothofthesetasksbysettingthememory’sprotectiontoPAGE_EXECUTE_READWRITE.Oncethememoryisproperlyprotected,youcanwritetheNOPcommandsandbedone.Ittechnicallydoesn’thurttoleavethememorywritable,butit’sgoodpracticetoalsorestoretheoriginalprotectionwhenyou’refinished.

Providedyouhavefacilitiesinplaceforwritingandprotectingmemory(asdescribedinChapter6),youcanwriteafunctionliketheoneshowninListing8-2towriteNOPcommandstogamememory.(Followalongbyopeningtheproject’sNOPExample.cppfile.)

template<intSIZE>

voidwriteNop(DWORDaddress){autooldProtection=protectMemory<BYTE[SIZE]>(address,PAGE_EXECUTE_READWRITE);

for(inti=0;i<SIZE;i++)writeMemory<BYTE>(address+i,0x90);

protectMemory<BYTE[SIZE]>(address,oldProtection);}

Listing8-2:ProperNOPing,completewithmemoryprotection

Inthisexample,thewriteNop()functionsetstheappropriatememoryprotection,writesanumberofNOPcommandsequaltoSIZE,andreappliestheoriginalmemoryprotectionlevel.

ThewriteNop()functiontakesthenumberofNOPinstructionstoplaceasatemplateparameter,sincethememoryfunctionsrequireacorrectlysizedtypeatcompiletime.PassinganintegerSIZEtellsthememoryfunctionstooperateonatypeofBYTE[SIZE]atcompiletime.Tospecifyadynamicsizeatruntime,simplydroptheloopandinsteadcallprotectMemory<BYTE>andpassaddressandaddress+SIZEasarguments.Aslongasthesizeisn’tlargerthanapage(andreally,youshouldn’tbeNOPingafullpage),thiswillensurethatthememorygetsproperlyprotectedevenifit’sonapageboundary.

CallthisfunctionwiththeaddresswhereyouwanttoplaceyourNOPsandthenumberofNOPcommandstoplace:

writeNop<2>(0xDEADBEEF);

KeepinmindthatthenumberofNOPcommandsshouldmatchthesizeinbytesofthecommandbeingremoved.ThiscalltowriteNop()writestwoNOPcommandstotheaddress0xDEADBEEF.

PRACTICENOPINGIfyouhaven’talready,openNOPExample.cppinthischapter’sexamplecodenowandplayaroundwithitforabit.You’llfindaworkingimplementationofthewriteNop()functionandaninterestingfunctioncalledgetAddressforNOP()thatscanstheexampleprogram’s

memorytofindwheretheNOPcommandshouldbeplaced.ToseetheNOPcommandinaction,runthecompiled

NOPapplicationinVisualStudio’sdebuggerwithbreakpointsatthestartandendofthewriteNop()function.Whenthefirstbreakpointishit,pressALT-8toopenthedisassemblywindow,enteraddressintheinputbox,andpressENTER.ThisbringsyoutotheNOP’stargetaddress,whereyou’llseetheassemblycodefullyintact.PressF5tocontinueexecution,whichtriggersthesecondbreakpointafterallowingtheapplicationtoplacetheNOPs.Finally,jumpbacktoaddressinthedisassemblytabtoseethatthecodewasreplacedbyNOPs.

Youcanreworkthiscodetodoothercoolstuff.Forexample,youmighttryplacingNOPsonthecomparisonsinsteadofthejumporevenmodifyingthejump’stypeordestination.

Theseandotheralternativeapproachesmaywork,butnotethattheyintroducemoreroomforerrorthanoverwritingthesingleJMPwithNOPcommands.Whenmodifyingforeigncode,makeasfewchangesaspossibletominimizethepotentialforerrors.

HookingtoRedirectGameExecutionSofar,I’veshownyouhowtomanipulategamesbyaddingcodetothem,hijackingtheirthreads,creatingnewthreads,andevenremovingexistingcodefromtheirexecutionflow.Thesemethodsareverypowerfulontheirown,butwhencombined,theyformanevenmorepotentmethodofmanipulationcalledhooking.Hookingallowsyoutointerceptprecisebranchesofexecutionandredirectthemtoinjectedcodethatyou’vewrittentodictatewhatthegameshoulddonext,anditcomesinavarietyofflavors.Inthissection,I’llteachyouaboutfourofthemostpowerfulhookingmethodsforgamehacking:callhooking,virtualfunctiontablehooking,importaddresstablehooking,andjumphooking.

CallHookingAcallhookdirectlymodifiesthetargetofaCALLoperationtopointtoanew

pieceofcode.ThereareafewvariationsoftheCALLoperationinx86assembly,butcallhooksaregenerallyusedononlyone:thenearcall,whichtakesanimmediateaddressasanoperand.

WorkingwithNearCallsinMemoryInanassemblyprogram,anearcalllookslikethis:

CALL0x0BADF00D

Thisnearcallisrepresentedbythebyte0xE8,soyoumightassumeitisstoredinmemorylikethis:

0xE80x0BADF00D

Or,whensplitintosinglebytesandswappedforendianness,likethis:

0xE80x0D0xF00xAD0x0B

Buttheanatomyofanearcallinmemoryisnotthatsimple.Insteadofstoringthecallee’sabsoluteaddress,anearcallstoresanoffsettothecalleerelativetotheaddressimmediatelyafterthecall.Sinceanearcallis5bytes,theaddressimmediatelyafterthecallis5byteslaterinmemory.Giventhat,theaddressstoredcanbecomputedasfollows:

calleeAddress–(callAddress+5)

IfCALL0x0BADF00Dlivesat0xDEADBEEFinmemory,thenthevalueafter0xE8isthis:

0x0BADF00D–(0xDEADBEEF+5)=0x2D003119

Inmemory,then,thatCALLinstructionlookslikethis:

0xE80x190x310x000x2D

Tohookanearcall,youfirstneedtochangetheoffsetfollowing0xE8(thatis,thelittle-endian0x190x310x000x2D)topointtoyournewcallee.

HookingaNearCall

FollowingthesamememoryprotectionrulesshowninListing8-2,youhookanearcalllikeso(followalongbyopeningCallHookExample.cpp):

DWORDcallHook(DWORDhookAt,DWORDnewFunc){DWORDnewOffset=newFunc-hookAt-5;

autooldProtection=protectMemory<DWORD>(hookAt+1,PAGE_EXECUTE_READWRITE);

DWORDoriginalOffset=readMemory<DWORD>(➊hookAt+1);writeMemory<DWORD>(hookAt+1,newOffset);protectMemory<DWORD>(hookAt+1,oldProtection);

➋returnoriginalOffset+hookAt+5;}

ThisfunctiontakesasargumentstheaddressoftheCALLtohook(hookAt)andtheaddresstoredirectexecutionto(newFunc),anditusesthemtocalculatetheoffsetrequiredtocallthecodeattheaddressnewFunccontains.Afteryouapplythecorrectmemoryprotections,thecallHook()functionwritesthenewoffsettothememoryathookAt+1➊,appliestheoldmemoryprotections,calculatestheaddressoftheoriginalcall➋,andreturnsthatvaluetothecaller.

Here’showyoumightactuallyuseafunctionlikethisinagamehack:

DWORDorigFunc=callHook(0xDEADBEEF,(DWORD)&someNewFunction);

Thishooksthenearcallto0x0BADF00Dat0xDEADBEEFandredirectsittotheaddressofsomeNewFunction,whichisthecodeyourhackwillexecute.Afterthisiscalled,theorigFuncvaluewillhold0x0BADF00D.

CleaningUptheStackThenewcalleemustalsoproperlyhandlethestack,preserveregisters,andpassproperreturnvalues.Attheleast,thismeansyourreplacementfunctionmustmatchthegame’soriginalfunctioninbothcallingconventionandargumentcount.

Let’ssaythisistheoriginalfullfunctioncall,inassembly:

PUSH1PUSH456PUSH321

PUSH321CALL0x0BADF00DADDESP,0x0C

YoucantellthefunctionhastheC++__cdeclconventionbecausethestackisbeingresetbythecaller.Additionally,the0x0Cbytesbeingcleanedfromthestackshowthattherearethreearguments,whichyoucancalculateasfollows:

Ofcourse,youcanalsoobtainthenumberofargumentsbycheckinghowmanythingsarepushedtothestack:therearethreePUSHcommands,oneforeachargument.

WritingaCallHookInanycase,thenewcallee,someNewFunction,mustfollowthe__cdeclconventionandhavethreearguments.Here’sanexampleskeletonforthenewcallee:

DWORD__cdeclsomeNewFunction(DWORDarg1,DWORDarg2,DWORDarg3){

}

InVisualStudio,C++programsusethe__cdeclconventionbydefault,sotechnicallyyoucouldomititfromyourfunctiondefinition;however,I’vefoundit’sbettertobeverbosesoyougetintothehabitofbeingspecific.Alsokeepinmindthatifthecallerexpectsavaluetobereturned,thereturntypeofyourfunctionshouldmatchaswell.ThisexampleassumesthereturntypeisalwaysaDWORDorsmaller.SincereturntypesinthissizerangewillallbepassedbackonEAX,furtherexampleswillalsouseareturntypeofDWORD.

Inmostcases,ahookfinishesbycallingtheoriginalfunctionandpassingitsreturnvaluebacktothecaller.Here’showallofthatmightfittogether:

typedefDWORD(__cdecl_origFunc)(DWORDarg1,DWORDarg2,DWORDarg3);

_origFunc*originalFunction=(_origFunc*)hookCall(0xDEADBEEF,(DWORD)&someNewFunction);

DWORD__cdeclsomeNewFunction(DWORDarg1,DWORDarg2,DWORDarg3){

returnoriginalFunction(arg1,arg2,arg3);}

Thisexampleusestypedeftodeclareatyperepresentingtheoriginalfunction’sprototypeandcreatesapointerwiththistypetotheoriginalfunction.ThensomeNewFunction()usesthispointertocalltheoriginalfunctionwiththeoriginalargumentsandpassthereturnedvaluebacktothecaller.

Rightnow,allsomeNewFunction()doesisreturntotheoriginalfunction.ButyoucandowhateveryouwantfrominsidethesomeNewFunction()callfromhere.Youcanmodifytheparametersbeingpassedtotheoriginalfunctionorinterceptandstoreinterestingparametersforlateruse.Ifyouknowthecallerisn’texpectingareturnvalue(orifyouknowhowtospoofthereturnvalue),youcanevenforgetabouttheoriginalfunctionandcompletelyreplace,replicate,orimproveitsfunctionalityinsidethenewcallee.Onceyou’veperfectedthisskill,youcanaddyourownnativeCorC++codetoanypartofagamethatyouwish.

VFTableHookingUnlikecallhooks,virtualfunction(VF)tablehooksdon’tmodifyassemblycode.Instead,theymodifythefunctionaddressesstoredintheVFtablesofclasses.(IfyouneedarefresheronVFtables,see“AClasswithVirtualFunctions”onpage75.)AllinstancesofthesameclasstypeshareastaticVFtable,soVFtablehookswillinterceptallcallsmadetoamemberfunction,regardlessofwhichclassinstancethegameiscallingthefunctionfrom.Thiscanbebothpowerfulandtricky.

THETRUTHABOUTVFTABLESTosimplifytheexplanation,IliedalittlewhenIsaidthatVFtablehookscouldinterceptallcallsmadetoafunction.Inreality,theVFtableistraversedonlywhenavirtualfunctioniscalledinawaythatleavesthecompilerwithsomeplausibletypeambiguity.Forexample,aVFtablewillbetraversedwhenafunctioniscalledthroughtheinst->function()callformat.AVFtablewon’tbetraversedwhena

virtualfunctionisinvokedinsuchawaythatthecompilerissureaboutthetype,asininst.function()orsimilarcalls,sincethecompilerwillknowthefunction’saddress.Conversely,callinginst.function()fromascopewhereinstispassedinasareferencewouldtriggeraVFtabletraversal.BeforeyoutrytodeployVFtablehooking,makesurethefunctioncallsyouwanttohookhavetypeambiguity.

WritingaVFTableHookBeforewegoanydeeperintohowtoplaceaVFtablehook,weneedtotalkaboutthosepeskycallingconventionsagain.VFtablesareusedbyclassinstancestocallvirtualmemberfunctions,andallmemberfunctionswillhavethe__thiscallconvention.Thename__thiscallisderivedfromthethispointerthatmemberfunctionsusetoreferencetheactiveclassinstance.Thus,memberfunctionsaregiventhisasapseudoparameteronECX.

It’spossibletomatchtheprototypeofa__thiscallbydeclaringaclassthatactsasacontainerforall__thiscallhookcallbacks,butIdon’tpreferthismethod.Instead,Ifinditeasiertocontrolthedatausinginlineassembly.Let’sexplorehowyoucontrolthedatawhenplacingaVFhookonaclassthatlookslikethis:

classsomeBaseClass{public:virtualDWORDsomeFunction(DWORDarg1){}};classsomeClass:publicsomeBaseClass{public:virtualDWORDsomeFunction(DWORDarg1){}};

ThesomeBaseClassclassjusthasonemember(apublicvirtualfunction),andthesomeClassclassinheritsfromsomeBaseClassandoverridesthesomeBaseClass::someFunctionmember.TohooksomeClass::someFunction,youreplicatetheprototypeinyourVFtablehook,asshowninListing8-3(followalongintheVFHookExample.cppfileoftheproject).

DWORD__stdcallsomeNewVFFunction(DWORDarg1){➊staticDWORD_this;

__asmMOV_this,ECX}

Listing8-3:ThestartofaVFtablehook

Thisfunctionworksasahookbecause__thiscallonlydiffersfrom__stdcallinthattheformerisgiventhisonECX.Toreconcilethissmalldifference,thecallbackfunctionusesinlineassembly(denotedby__asm)tocopythisfromECXtoastaticvariable➊.Sincethestaticvariableisactuallyinitializedasaglobal,theonlycodethatexecutesbeforeMOV_this,ECXisthecodethatsetsupthestackframe—andthatcodenevertouchesECX.ThatensuresthatthepropervalueisinECXwhentheassemblyisexecuted.

NOTE

IfmultiplethreadsstartcallingthesameVFfunction,thesomeNewVFFunction()hookwillbreakbecause_thismightbemodifiedbyonecallwhilestillbeingusedbyanothercall.I’veneverpersonallyrunintothisproblem,asgamesdon’ttypicallythrowaroundmultipleinstancesofcriticalclassesbetweenthreads,butanefficientremedywouldbetostore_thisinthreadlocalstorage,ensuringeachthreadwouldhaveitsowncopy.

Beforereturning,aVFtablecallbackmustalsorestoreECX,tokeepwiththe__thiscallconvention.Here’showthatprocesslooks:

DWORD__stdcallsomeNewVFFunction(DWORDarg1){staticDWORD_this;__asmMOV_this,ECX

//dogamemodifyingstuffhere

__asm➊MOVECX,_this}

Afterexecutingsomegame-hackingcode,thisversionofthefunctionsomeNewVFFunction()restoresECX➊withareversedversionofthefirstMOVinstructionfromListing8-3.

Unlikewith__cdeclfunctions,however,youshouldn’tcallfunctionsthat

usethe__thiscallconventionfrompureC++usingonlyafunctionpointerandtypedef(asyouwouldforacallhook).WhencallingtheoriginalfunctionfromaVFtablehook,youmustuseinlineassembly—that’stheonlywaytobesureyou’repassingdata(specifically_this)aroundproperly.Forexample,thisishowyoucontinuetobuildthesomeNewVFFunction()hook:

DWORD__stdcallsomeNewVFFunction(DWORDarg1){staticDWORD_this,_ret;__asmMOV_this,ECX

//dopre-callstuffhere

__asm{PUSHarg1MOVECX,_this➊CALL[originalVFFunction]➋MOV_ret,EAX}

//dopost-callstuffhere

➌__asmMOVECX,_thisreturn_ret;}

Now,someNewVFFunction()storesthisinthe_thisvariable,allowssomecodetoexecute,callstheoriginalgamefunction➊that’sbeinghooked,storesthatfunction’sreturnvaluein_ret➋,allowssomemorecodetoexecute,restoresthistoECX➌,andreturnsthevaluestoredin_ret.Thecalleecleansthestackfor__thiscallcalls,sounlikeacallhook,thepushedargumentdoesn’tneedtoberemoved.

NOTE

Ifyouwanttoremoveasinglepushedargumentatanypoint,usetheassemblyinstructionADDESP,0x4becauseasingleargumentis4bytes.

UsingaVFTableHookWiththecallingconventionestablishedandaskeletoncallbackinplace,it’stimetomoveontothefunpart:actuallyusingaVFtablehook.Apointerto

timetomoveontothefunpart:actuallyusingaVFtablehook.Apointertoaclass’sVFtableisthefirstmemberofeveryclassinstance,soplacingaVFtablehookrequiresonlyaclassinstanceaddressandtheindexofthefunctiontobehooked.Usingthesetwopiecesofinformation,youneedonlyamodestamountofcodetoplaceahook.Here’sanexample:

DWORDhookVF(DWORDclassInst,DWORDfuncIndex,DWORDnewFunc){DWORDVFTable=➊readMemory<DWORD>(classInst);DWORDhookAt=VFTable+funcIndex*sizeof(DWORD);

autooldProtection=protectMemory<DWORD>(hookAt,PAGE_READWRITE);DWORDoriginalFunc=readMemory<DWORD>(hookAt);writeMemory<DWORD>(hookAt,newFunc);protectMemory<DWORD>(hookAt,oldProtection);

returnoriginalFunc;}

ThehookVF()functionfindstheVFtablebyreadingthefirstmemberoftheclassinstance➊andstoringitinVFTable.SincetheVFtableisjustanarrayofDWORD-sizedaddresses,thiscodefindsthefunctionaddressbymultiplyingthefunction’sindexintheVFtable(funcIndexinthisexample)bythesizeofaDWORD,whichis4,andaddingtheresulttotheVFtable’saddress.Fromthere,hookVF()actssimilartoacallhook:itmakessurethememoryisproperlyaccessiblebysettingappropriateprotections,storestheoriginalfunctionaddressforlater,writesthenewfunctionaddress,andfinally,restorestheoriginalmemoryprotection.

You’lltypicallyhooktheVFtableofaclassinstantiatedbythegame,andcallingafunctionlikehookVF()foraVFtablehooklookslikethis:

DWORDorigVFFunction=hookVF(classInstAddr,0,(DWORD)&someNewVFFunction);

Asusual,youneedtofindclassInstAddrandthefuncIndexargumentaheadoftime.

TherearesomeverynichecasesinwhichVFtablehooksareuseful,anditcanbereallyhardtofindtherightclasspointersandfunctions.Giventhat,insteadofshowingcontrivedusecases,I’llcomebacktoVFtablehooksin“ApplyingJumpHooksandVFHookstoDirect3D”onpage175,onceI’vediscussedothertypesofhooking.

IfyouwanttoplaywithVFhooksbeforereadingmore,addnewvirtualfunctionstotheexampleclassesinthisbook’sresourcefilesandpracticehookingthem.YoumightevencreateasecondclassthatderivesfromsomeBaseClassandplaceahookonitsvirtualtabletodemonstratehowyoucanhavetwocompletelyseparateVFhooksontwoclassesthatinheritthesamebaseclass.

IATHookingIAThooksactuallyreplacefunctionaddressesinaspecifictypeofVFtable,calledtheimportaddresstable(IAT).EachloadedmoduleinaprocesscontainsanIATinitsPEheader.Amodule’sIATholdsalistofalltheothermodulesonwhichthemoduledepends,aswellasalistoffunctionsthatthemoduleusesfromeachdependency.ThinkofanIATasalookuptableforAPIstocalloneanother.

Whenamoduleisloaded,itsdependenciesarealsoloaded.Dependencyloadingisarecursiveprocessthatcontinuesuntilalldependenciesforallmodulesareloaded.Aseachdependencyisloaded,theoperatingsystemfindsallfunctionsusedbythedependentmoduleandfillsanyblankspacesinitsIATwiththefunctionaddresses.Then,whenamodulecallsafunctionfromadependency,itmakesthatcallbyresolvingthefunction’saddressfromtheIAT.

PayingforPortabilityFunctionaddressesarealwaysresolvedfromtheIATinrealtime,sohookingtheIATissimilartohookingVFtables.SincefunctionpointersarestoredintheIATbesidetheiractualnames,there’snoneedtodoanyreverseengineeringormemoryscanning;aslongasyouknowthenameoftheAPIyouwanttohook,youcanhookit!Moreover,IAThookingletsyoueasilyhookWindowsAPIcallsonamodule-specificbasis,allowingyourhookstointerceptonlyAPIcallsfromagame’smainmodule.

Thisportabilityhasacost,though;thecodetoplaceanIAThookismuchmorecomplexthanwhatyou’veseensofar.First,youneedtolocatethePEheaderofthegame’smainmodule.SincethePEheaderisthefirststructureinanybinary,youcanfinditatthebaseaddressofeachmodule,asshowninListing8-4(followalongintheIATHookExample.cppfileofthe

project).

DWORDbaseAddr=(DWORD)GetModuleHandle(NULL);

Listing8-4:Fetchingthemodule’sbaseaddress

Onceyou’vefoundthebaseaddress,youmustverifythatthePEheaderisvalid.Thisvalidationcanbeveryimportant,assomegamestrytopreventthesetypesofhooksbyscramblingnonessentialpartsoftheirPEheaderaftertheyload.AvalidPEheaderisprefixedbyaDOSheader,whichindicatesthefileisaDOSMZexecutable;theDOSheaderisidentifiedbythemagicvalue0x5A4D.AmemberoftheDOSheadercallede_lfanewthenpointstotheoptionalheader,whichcontainsvalueslikethesizeofthecode,aversionnumber,andsoonandisidentifiedbythemagicvalue0x10B.

TheWindowsAPIhasPEstructurescalledIMAGE_DOS_HEADERandIMAGE_OPTIONAL_HEADERthatcorrespondtotheDOSheaderandoptionalheader,respectively.YoucanusethemtovalidatethePEheaderwithcodelikeListing8-5.

autodosHeader=pointMemory<IMAGE_DOS_HEADER>(baseAddr);if(dosHeader->e_magic!=0x5A4D)return0;

autooptHeader=pointMemory<IMAGE_OPTIONAL_HEADER>(baseAddr+dosHeader->e_lfanew+24);if(optHeader->Magic!=0x10B)return0;

Listing8-5:ConfirmingtheDOSandoptionalheadersarevalid

ThecallstopointMemory()createpointerstothetwoheadersthatneedtobechecked.Ifeitherif()statementreturns0,thenthecorrespondingheaderhasthewrongmagicnumber,meaningthePEheaderisn’tvalid.

ReferencestotheIATfromassemblyarehardcoded,meaningassemblyreferencesdon’ttraversethePEheadertolocatetheIAT.Instead,eachfunctioncallhasastaticlocationindicatingwheretofindthefunctionaddress.ThatmeansoverwritingthePEheadertosaythattherearenoimportsisaviablewaytoprotectagainstIAThooks,andsomegameshavethisprotection.

Toaccountforthat,youalsoneedtomakesurethegame’sIATstill

exists.Listing8-6showshowtoaddsuchachecktothecodeinListing8-5.

autoIAT=optHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];if(IAT.Size==0||IAT.VirtualAddress==0)return0;

Listing8-6:CheckingthattheIATactuallyexists

ThePEheadercontainsmanysectionsthatstoreinformationabouttheapplication’scode,embeddedresources,relocations,andsoon.ThepieceofcodeinListing8-6isparticularlyinterestedinthedatasection,which—asyoumightguess—storesmanydifferenttypesofdata.Eachtypeofdataisstoredinitsowndirectory,andtheDataDirectorymemberofIMAGE_OPTIONAL_HEADERisanarrayofdirectoryheadersthatdescribesthesizeandvirtualaddressofeachdirectoryinthedatasection.TheWindowsAPIdefinesaconstantcalledIMAGE_DIRECTORY_ENTRY_IMPORT,whichhappenstobetheindexoftheIATheaderwithintheDataDirectoryarray.

Thus,thiscodeusesoptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]toresolvetheheaderoftheIATandcheckthattheheader’sSizeandVirtualAddressarenonzero,essentiallyconfirmingitsexistence.

TraversingtheIATOnceyouknowtheIATisstillintact,youcanstarttraversingit,andthisiswhereIAThookingstartstogetugly.TheIATisanarrayofstructurescalledimportdescriptors.Thereisoneimportdescriptorforeachdependency,eachimportdescriptorpointstoanarrayofstructurescalledthunks,andeachthunkrepresentsafunctionimportedfromthedependency.

Luckily,theWindowsAPIexposesboththeimportdescriptorsandthunksthroughtheIMAGE_IMPORT_DESCRIPTORandIMAGE_THUNK_DATAstructures,respectively.Havingthestructurespredefinedsavesyoufromcreatingyourown,butitdoesn’tmakethecodetotraversetheIATanyprettier.ToseewhatImean,lookatListing8-7,whichbuildsonListings8-4through8-6.

autoimpDesc=pointMemory<IMAGE_IMPORT_DESCRIPTOR>(➊baseAddr+IAT.VirtualAddress);

➋while(impDesc->FirstThunk){➌autothunkData=pointMemory<IMAGE_THUNK_DATA>(baseAddr+impDesc->OriginalFirstThunk);intn=0;➍while(thunkData->u1.Function){//thehookhappensinheren++;thunkData++;}impDesc++;}

Listing8-7:IteratingovertheIATtofindafunction

KeepinginmindthattheimportdescriptorsarestoredrelativetothestartofthePEheader,thiscodeaddsthemodule’sbaseaddresstothevirtualaddressfoundintheIAT’sdirectoryheader➊,creatingapointer,impDesc,thatpointstothemodule’sfirstimportdescriptor.

Importdescriptorsarestoredinasequentialarray,andadescriptorwithaFirstThunkmembersettoNULLsignifiestheendofthearray.Knowingthis,thecodeusesawhileloop➋thatcontinuesuntilimpDesc->FirstThunkisNULL,incrementingthedescriptorbyexecutingimpDesc++eachiteration.

Foreachimportdescriptor,thecodecreatesapointercalledthunkData➌thatpointstothefirstthunkinsidethedescriptor.Usingafamiliarloop,thecodeiteratesoverthunks➍untiloneisfoundwithaFunctionmembersettoNULL.Theloopalsousesaninteger,n,tokeeptrackofthecurrentthunkindex,astheindexisimportantwhenplacingthehook.

PlacingtheIATHookFromhere,placingthehookisjustamatteroffindingtheproperfunctionnameandreplacingthefunctionaddress.Youcanfindthenameinsidethenestedwhileloop,asshowninListing8-8.

char*importFunctionName=pointMemory<char>(baseAddr+(DWORD)thunkData->u1.AddressOfData+2);

Listing8-8:Findingthefunctionname

ThefunctionnameforeachthunkisstoredatthunkData-

>u1.AddressOfData+2bytesintothemodule,soyoucanaddthatvaluetothemodule’sbaseaddresstolocatethefunctionnameinmemory.

Afterobtainingapointertothefunctionname,usestrcmp()tocheckwhetherit’sthetargetfunction,likeso:

if(strcmp(importFuncName,funcName)==0){//thefinalstephappensinhere}

Onceyou’velocatedthetargetfunctionusingitsname,yousimplyneedtooverwritethefunctionaddresswiththeaddressofyourownfunction.Unlikefunctionnames,functionaddressesarestoredinanarrayatthestartofeachimportdescriptor.Usingnfromthethunkloop,youcanfinallysetthehook,asshowninListing8-9.

autovfTable=pointMemory<DWORD>(baseAddr+impDesc->FirstThunk);DWORDoriginal=vfTable[n];

➊autooldProtection=protectMemory<DWORD>((DWORD)&vfTable[n],PAGE_READWRITE);➋vfTable[n]=newFunc;protectMemory<DWORD>((DWORD)&vfTable[n],oldProtection);

Listing8-9:Findingthefunctionaddress

ThiscodelocatestheVFtableforthecurrentdescriptorbyaddingtheaddressofthefirstthunktothemodulebaseaddress.TheVFtableisanarrayoffunctionaddresses,sothecodeusesthenvariableasanindextolocatethetargetfunctionaddress.

Oncetheaddressisfound,thecodeinListing8-9worksjustlikeatypicalVFhook:itstorestheoriginalfunctionaddress,setstheprotectionofindexnintheVFtabletoPAGE_READWRITE➊,insertsthenewfunctionaddressintotheVFtable➋,andfinallyrestorestheoldprotection.

IfyoustitchtogetherthecodefromListings8-4through8-9,thefinalIAThookingfunctionlookslikeListing8-10.

DWORDhookIAT(constchar*funcName,DWORDnewFunc){DWORDbaseAddr=(DWORD)GetModuleHandle(NULL);autodosHeader=pointMemory<IMAGE_DOS_HEADER>(baseAddr);if(dosHeader->e_magic!=0x5A4D)return0;

autooptHeader=pointMemory<IMAGE_OPTIONAL_HEADER>(baseAddr+dosHeader->e_lfanew+24);if(optHeader->Magic!=0x10B)return0;

autoIAT=optHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];if(IAT.Size==0||IAT.VirtualAddress==0)return0;

autoimpDesc=pointMemory<IMAGE_IMPORT_DESCRIPTOR>(baseAddr+IAT.VirtualAddress);

while(impDesc->FirstThunk){autothunkData=pointMemory<IMAGE_THUNK_DATA>(baseAddr+impDesc->OriginalFirstThunk);intn=0;while(thunkData->u1.Function){char*importFuncName=pointMemory<char>(baseAddr+(DWORD)thunkData->u1.AddressOfData+2);if(strcmp(importFuncName,funcName)==0){autovfTable=pointMemory<DWORD>(baseAddr+impDesc->FirstThunk);DWORDoriginal=vfTable[n];autooldProtection=protectMemory<DWORD>((DWORD)&vfTable[n],PAGE_READWRITE);vfTable[n]=newFunc;protectMemory<DWORD>((DWORD)&vfTable[n],oldProtection);returnoriginal;}n++;thunkData++;}impDesc++;}}

Listing8-10:ThecompleteIAThookingfunction

Thisisthemostcomplexcodethatwe’veputtogethersofar,andit’sprettyhardtoreadwhensquishedtofitonapage.Ifyouhaven’tyetwrappedyourheadaroundwhatit’sdoing,youmightwanttostudytheexamplecodefromthisbook’sresourcefilesbeforecontinuing.

UsinganIATHooktoSyncwithaGameThreadWiththecodeinListing8-10,hookinganyWindowsAPIfunctionisassimpleasknowingthefunctionnameandtheproperprototype.TheSleep()APIisacommonAPItohookwhengamehacking,asbotscanuseaSleep()hooktothread-syncwithagame’smainloop.

GETTINGINSYNCWITHTHREADSYNCYourinjectedcodewillinevitablyhavetosyncwithagame’smainloop,oritwon’twork.Whenyou’rereadingorwritingdatalargerthan4bytes,forexample,beingoutofsyncallowsthegametoreadorwritethatdataatthesametimeasyou.You’llbesteppingonthegame’stoes,andviceversa,leadingtoallsortsofraceconditionsanddatacorruptionissues.Similarly,ifyoutrytocallagame’sfunctionfromyourownthread,youruntheriskofcrashingthegameifthefunctionisnotthreadsafe.

SinceIAThooksarethread-safemodificationstothePEheader,theycanbeplacedfromanythread.Byplacingoneonafunctionthat’scalledbeforeorafterthegame’smainloop,youcaneffectivelysyncwiththegame’smainthread.Allyouneedtodoisplacethehookandexecuteanythread-sensitivecodefromyourhookcallback.

Here’sonewaytousehookIAT()tohooktheSleep()API:

VOIDWINAPInewSleepFunction(DWORDms){//dothread-sensitivethingsoriginalSleep(ms);}

typedefVOID(WINAPI_origSleep)(DWORDms);_origSleep*originalSleep=(_origSleep*)hookIAT("Sleep",(DWORD)&newSleepFunction);

Here’swhythisworks.Attheendofagame’smainloop,itmightcallSleep()torestuntilit’sreadytodrawthenextframe.Sinceit’ssleeping,it’ssafeforyoutodoanythingyouwantwithoutworryingabout

synchronizationissues.Somegamesmightnotdothis,ortheymightcallSleep()frommultiplethreads,andthosegameswillrequireadifferentmethod.

AmoreportablealternativeistohookthePeekMessageA()APIfunction,becausegamesoftencallthatfunctionfromthemainloopwhilewaitingforinput.Then,yourbotcandothread-sensitiveoperationsfromwithinthePeekMessageA()hook,ensuringthatthey’redonefromthegame’smainthread.Youmayalsowantyourbottousethismethodtohookthesend()andrecv()APIfunctions,asinterceptingtheseallowsyoutocreateapacketsnifferrelativelysimply.

JumpHookingJumphookingallowsyoutohookcodeinplaceswherethereisnobranchingcodetomanipulate.Ajumphookreplacesthecodebeinghookedwithanunconditionaljumptoatrampolinefunction.Whenthejumpishit,thetrampolinefunctionstoresallcurrentregisterandflagvalues,callsacallbackfunctionofyourchoice,restorestheregisters,restorestheflags,executesthecodethatwasreplacedbythehook,andfinallyjumpsbacktothecodejustbelowthehook.ThisprocessisshowninFigure8-1.

Figure8-1:Ajumphook

Theoriginalcodeshowsanexampleofsomeunmodifiedassemblyyoumightfindinagame,andthehookedcodeshowshowthatassemblymightlookafterbeinghookedbyajumphook.Thetrampolineboxshowsanexampletrampolinefunction,inassembly,andthecallbackrepresentsthe

codeyou’retryingtoexecutethroughhooking.Intheoriginalcode,theassemblyexecutesfromtoptobottom.Inthehookedcode,togetfromtheSUBEAX,1instructiontotheRETNinstruction,executionmustfollowthepathshownbythedashedarrows.

NOTE

Ifyourcallbackcodeissimple,itcanbeintegratedintothetrampolineinstead.It’salsonotalwaysnecessarytostoreandrestoretheregistersandflags,butdoingsoisgoodpractice.

PlacingaJumpThebytecodeofanunconditionaljumpresemblesthatofanearcall,butthefirstbyteis0xE9insteadof0xE8.(See“WorkingwithNearCallsinMemory”onpage153forarefresher.)InFigure8-1,theunconditionaljumpJMPtrampolinereplacesthefollowingfouroperations:

POPEAXMOVAL,1POPEDIPOPESI

Inthiscase,youneedtoreplacemultiplesequentialoperationstoaccommodatethe5-bytesizeoftheunconditionaljump.Youmaycomeacrosscaseswherethesizeoftheoperation(oroperations)beingreplacedislargerthan5bytes.Whenthishappens,replacetheremainingbyteswithNOPinstructions.

Now,let’slookathowtoreplacethoseoperations.Listing8-11showsthecodetoplaceajumphook.

DWORDhookWithJump(DWORDhookAt,DWORDnewFunc,intsize){if(size>12)//shouldn'teverhavetoreplace12+bytesreturn0;➊DWORDnewOffset=newFunc-hookAt-5;

autooldProtection=protectMemory<DWORD[3]>(hookAt+1,PAGE_EXECUTE_READWRITE);➋writeMemory<BYTE>(hookAt,0xE9);➌writeMemory<DWORD>(hookAt+1,newOffset);

for(unsignedinti=5;i<size;i++)writeMemory<BYTE>(hookAt+i,0x90);protectMemory<DWORD[3]>(hookAt+1,oldProtection);

returnhookAt+5;}

Listing8-11:Howtoplaceajumphook

Thisfunctiontakestheaddresstohookat,theaddressofthecallbackfunction,andthesizeofthememorytooverwrite(inbytes)asarguments.First,itcalculatestheoffsetbetweenthehooksiteandthetrampolineandstorestheresultinnewOffset➊.Next,PAGE_EXECUTE_READWRITEpermissionsareappliedtothememorytobechanged.Theunconditionaljump(0xE9)➋andtheaddressofthecallbackfunction➌arethenwrittentomemory,andaforloopwritesNOPinstructions(0x90)toanyabandonedbytes.Aftertheoldprotectionsarereapplied,hookWithJump()returnstotheoriginaladdress.

NoticethatthehookWithJump()functionensuresthatsizeisnotabove12beforeplacingthejump.Thischeckisimportantbecauseajumptakesup5bytes,meaningitcanreplaceuptofivecommandsifthefirstfourareeachasinglebyte.Ifthefirstfourcommandsareeachasinglebyte,thefifthcommandwouldneedtobemorethan8bytestotriggertheif(size>12)clause.Because9-byteoperationsarevery,veryrare,12isasafebutflexiblelimit.Havingthislimitcanstopallsortsofbugsfromhappening,especiallyifyourbotisdynamicallydetectingthesizeparameter.Ifthebotmessesupandpassesasizeof500,000,000,forinstance,thecheckwillstopyoufromNOPingthewholeuniverse.

WritingtheTrampolineFunctionUsingthefunctioninListing8-11,youcanreplicatethehookshowninFigure8-1,butfirstyou’llhavetocreatethetrampolinefunctionasfollows:

DWORDrestoreJumpHook=0;void__declspec(naked)myTrampoline(){__asm{➊PUSHFD➋PUSHAD➌CALLjumpHookCallback➍POPAD

➎POPFD➏POPEAXMOVAL,1POPEDI➐POPESI➑JMP[restoreJumpHook]}}

JustlikethetrampolinedescribedalongsideFigure8-1,thistrampolinestoresallcurrentflag➊andregistervalues➋,callsacallbackfunction➌,restorestheregisters➍,restorestheflags➎,executesthecodethatwasreplacedbythehookat➏and➐,andfinallyjumpsbacktotheoriginalcodejustbelowthejumpandNOPs➑.

NOTE

Toensurethatthecompilerdoesn’tautogenerateanyextracodewithinthetrampoline,alwaysdeclarethetrampolineusingthe__declspec(naked)convention.

FinishingtheJumpHookOnceyoucreatethetrampoline,definethecallbackandsetthehooklikeso:

voidjumpHookCallback(){//dostuff}restoreJumpHook=hookWithJump(0xDEADBEEF,&myTrampoline,5);

Finally,insidethejumpHookCallback()function,executethecodethatreliesonthehook.Ifyourcodeneedstoreadorwritethevaluesoftheregistersastheywerewhenthehookexecuted,you’reinluck.ThePUSHADcommandpushesthemtothestackintheorderEAX,ECX,EDX,EBX,originalESP,EBP,ESI,andEDI.ThetrampolinecallsPUSHADdirectlybeforethejumpHookCallback()call,soyoucanreferencetheregistervaluesasarguments,likethis:

voidjumpHookCallback(DWORDEDI,DWORDESI,DWORDEBP,DWORDESP,DWORDEBX,DWORDEDX,DWORDECX,DWORDEAX){//dostuff

}restoreJumpHook=hookWithJump(0xDEADBEEF,&myTrampoline,5);

SincethetrampolineusesPOPADtodirectlyrestoretheregistersfromthesevaluesonthestack,anymodificationsyoumaketotheparameterswillbeappliedtotheactualregisterswhentheyarerestoredfromthestack.

LikeVFtablehooks,jumphooksarerarelyneeded,andtheycanbetrickytosimulatewithasimpleexample.Tohelpyouwrapyourheadaroundthem,I’llexploreareal-world,practicalusecasein“ApplyingJumpHooksandVFHookstoDirect3D”onpage175.

PROFESSIONALAPIHOOKINGLIBRARIESThereareprewrittenhookinglibraries,likeMicrosoft’sDetoursandMadCHook,thatuseonlyjumphooks.Theselibrariescanautomaticallydetectandfollowotherhooks,theyknowhowmanyinstructionstoreplace,andtheygeneratetrampolinefunctionsforyou.Thelibrariesareabletodothisbecausetheyunderstandhowtodisassembleandwalkthroughassemblyinstructionstodeterminelengths,jumpdestinations,andsoon.Ifyouneedtousehookswiththatmuchpower,itisarguablybettertouseoneofthoselibrariesthantocreateyourown.

ApplyingCallHookstoAdobeAIRAdobeAIRisadevelopmentframeworkthatcanbeusedtomakecross-platformgamesinanenvironmentsimilartoAbodeFlash.AIRisacommonframeworkforonlinegames,asitallowsdeveloperstowritecross-platformcodeinaversatile,high-levellanguagecalledActionScript.ActionScriptisaninterpretedlanguage,andAIRrunsthecodeinsideavirtualmachine,whichmakesitinfeasibletohookgame-specificcodewithAIR.Instead,itiseasiertohookAIRitself.

TheexamplecodeforthissectioncanbefoundinGameHackingExamples/Chapter8_AdobeAirHookinthisbook’ssourcefiles.

Thecodecomesfromanoldprojectofmine,anditworksonanygamerunningAdobeAIR.dllversion3.7.0.1530.I’vegottenitworkingonotherversionsaswell,butIcan’tguaranteeitwillworkwithmuchnewerormucholderversions,sotreatthisasacasestudy.

AccessingtheRTMPGoldmineTheRealTimeMessagingProtocol(RTMP)isatext-basednetworkprotocolthatActionScriptusestoserializeandsendentireobjectsoverthenetwork.RTMPsitsontopoftheHyperTextTransferProtocol(HTTP),andasecureversion,RTMPS,sitsontopofHTTPSecure(HTTPS).RTMPSallowsgamedeveloperstoeasilysendandreceiveentireobjectinstancesoverasecureconnectionwithlittlecomplication,makingitthenetworkprotocolofchoiceforanygamesrunningonAIR.

NOTE

DatasentoverRTMP/RTMPSisserializedthroughActionMessageFormat(AMF),andparsingAMFpacketsisbeyondthescopeofthisbook.Searchonlinefor“AMF3Parser,”andyou’llfindalotofcodethatdoesit.

DatasentoverRTMPandRTMPSisveryrich.Thepacketscontaininformationaboutobjecttypes,names,andvalues.Thisisagoldmine.Ifyoucaninterceptthisdatainrealtime,youcaninstantaneouslyrespondtochangesingamestate,seeawealthofcriticalinformationwithouteverreadingitfrommemory,andfindusefulpiecesofdatathatyoumightnotevenknowexist.

Awhileback,Iwasworkingonatoolthatrequiredatonofinsightintothestateofagame.Obtainingsuchalargeamountofdatadirectlyfrommemorywouldhavebeenextremelyhard,ifnotimpossible.Aftersomeresearch,IrealizedthatthegamewasusingRTMPStocommunicatewiththeserver,andthatpromptedmetostartdiggingintothisgoldmine.

SinceRTMPSisencrypted,IknewIhadtosomehowhookthecryptographicfunctionsusedbyAIRbeforeIcouldgetanyusabledata.Aftersearchingonline,Ifoundsourcecodeforasmalltoolcalledairlog,createdbyanothergamehackerwho,likeme,wastryingtologpacketssentoverRTMPS.AlthoughthetoolhookedtheexactfunctionsIneeded,thecodewasoutdated,messy,and,worstofall,didn’tworkontheversionof

codewasoutdated,messy,and,worstofall,didn’tworkontheversionofAIRIwastryingtohook.

Butthatdidn’tmeanitwasuseless.NotonlydidairloghookthetwofunctionsIneeded,butitalsolocatedthembyscanningforcertainbytepatternswithintheAdobeAIRlibrary.Thesebytepatternswerethreeyearsold,though,sotheyweren’tworkinganymore.ThenewerversionsofAdobeAIRhadchangedenoughthattheassemblybyteswerenolongerthesame.Thedifferenceinbyteswasaproblemforthecodeinairlog,butnotforme.

Insideaninlineassemblyblock,youcanspecifyrawbyteswiththefollowingfunctioncall:

_emitBYTE

IfyoureplaceBYTEwith,say,0x03,thecodewillbecompiledinawaythattreats0x03asabyteintheassemblycode,regardlessofwhetherthatmakessense.Usingthistrick,Icompiledthebytearraysbacktoassemblycode.Thecodedidn’tdoanything,anditwasn’tmeantto;usingthistricksimplyallowedmetoattachtomydummyapplicationwithOllyDBGandinspectbytes,whichwereconvenientlypresentedasacleandisassembly.

SincethesebytesrepresentedthecodesurroundingthefunctionsIneeded,so,too,didtheirdisassembly.Thecodewasprettystandardanddidn’tseemlikelytochange,soIturnedmyattentiontotheconstants.Thecodehadafewimmediatevaluespassedasoffsetsincommands.Knowinghowcommonlythesecanchange,Irewiredairlog’spattern-matchingalgorithmtosupportwildcards,updatedthepatternstotreatanyconstantsaswildcards,andthenranthematch.Aftersometweakstothepatternsandabitofdiggingthroughduplicatesearchresults,ItrackeddownthefunctionsIwantedtohook.Iappropriatelynamedthemencode()anddecode()andbeganworkingonatoolsimilartoairlog—butbetter.

HookingtheRTMPSencode()FunctionIdiscoveredthattheencode()function,whichisusedtoencryptthedataforoutgoingpackets,isanonvirtual__thiscall,meaningit’scalledbyanearcall.Moreover,thecallhappensinsidealoop.TheentirelooplookslikeListing8-12,takendirectlyfromtheOllyDBGdisassemblypane.

loop:

MOVEAX,[ESI+3C58]SUBEAX,EDIPUSHEAX➊LEAEAX,[ESI+EDI+1C58]PUSHEAXMOVECX,ESI➋CALLencodeCMPEAX,-1➌JESHORTendLoopADDEDI,EAX➍CMPEDI,[ESI+3C58]JLloopendLoop:

Listing8-12:Theencode()loop

Withabitofanalysisandsomeguidancefromairlog,Ideterminedthattheencode()functioncalledat➊takesabytearrayandbufferlength(let’scallthembufferandsize,respectively)asparameters.Thefunctionreturns-1whenitfailsandreturnssizeotherwise.Thefunctionoperatesonchunksof4,096bytes,whichiswhythishappensinaloop.

Turnedintomorereadablepseudocode,theloopcallingencode()lookslikethis(thenumbersrefertotherelevantassemblyinstructionsinListing8-12):

for(EDI=0;EDI<➍[ESI+3C58];){EAX=➋encode(➊&[ESI+EDI+1C58],[ESI+3C58]-EDI);if(EAX==-1)➌break;EDI+=EAX;}

Iwasn’tinterestedinwhatencode()did,butIneededtheentirebufferitwasloopingover,andhookingencode()wasmymeansofgettingthatbuffer.LookingattherealloopinListing8-12,Iknewthatthecallingobjectinstance’sfullbufferwasstoredatESI+0x1C58,thatthefullsizewasstoredatESI+0x3C58,andthatEDIcontainedtheloopcounter.Idevisedthehookwiththesethingsinmind,ultimatelycreatingatwo-parthook.

ThefirstpartofmyhookwasareportEncode()functionthatlogstheentirebufferonthefirstloopiteration.Here’sthereportEncode()functioninfull:

DWORD__stdcallreportEncode(

constunsignedchar*buffer,unsignedintsize,unsignedintloopCounter){if(loopCounter==0)printBuffer(buffer,size);returnorigEncodeFunc;}

Thisfunctiontakesbuffer,size,andloopCounterasparametersandreturnstheaddressofthefunctionIdubbedencode().Beforefetchingthataddress,however,thesecondpartofmyhook,amyEncode()function,doesallofthedirtyworktoobtainbuffer,size,andloopCounter,asfollows:

void__declspec(naked)myEncode(){__asm{MOVEAX,DWORDPTRSS:[ESP+0x4]//getbufferMOVEDX,DWORDPTRDS:[ESI+0x3C58]//getfullsizePUSHECX//storeecxPUSHEDI//pushcurrentpos(loopcounter)PUSHEDX//pushsizePUSHEAX//pushbufferCALLreportEncode//reporttheencodecallPOPECX//restoreecxJMPEAX//jumptoencode}}

ThemyEncode()functionisapureassemblyfunctionthatreplacestheoriginalencode()functioncallusinganearcallhook.AfterstoringECXonthestack,myEncode()obtainsbuffer,size,andloopCounterandpassesthemtothereportEncode()function.AftercallingthereportEncode()function,themyEncode()functionrestoresECXandjumpsdirectlyintoencode(),causingtheoriginalfunctiontoexecuteandreturngracefullytotheloop.

SincemyEncode()cleanseverythingitusesfromthestack,thestackstillcontainstheoriginalparametersandreturnaddressinthecorrectspotaftermyEncode()runs.That’swhymyEncode()jumpsdirectlyintoencode()insteadofusingafunctioncall:thatstackisalreadysetupwiththeproperreturnaddressandparameters,sotheencode()functionwillthinkeverythinghappenedasnormal.

HookingtheRTMPSdecode()Function

ThefunctionInameddecode(),whichisusedtodecryptincomingdata,wasalsoa__thiscallthatwascalledinaloop.Itworkedonchunksof4,096bytesandtookabufferandsizeasparameters.Theloopwasquiteabitmorecomplex,containingmultiplefunctioncalls,nestedloops,andloopescapes,buthookingworkedmuchthesameashookingtheso-calledencode()function.Thereasonfortheaddedcomplexityisnotrelevanttohookingthefunction,butitmakesthecodedifficulttosummarize,soIwon’tshowtheoriginalfunctionhere.Thebottomlineisthis:onceallthecomplexitywasrubbedaway,thedecode()loopwastheencode()loopinreverse.

Onceagain,Idevisedatwo-partnearcallhook.Thefirstpart,reportDecode(),isshownhere:

void__stdcallreportDecode(constunsignedchar*buffer,unsignedintsize){printBuffer(buffer,size);}

Thefunctionlogseachpacketthatcomesthrough.Ididn’thavealoopindexatthetime,soIdecidedthatitwasokaytologeverysinglepartialpacket.

Thesecondpartofthehook,themyDecode()function,actsasthenewcalleeanddoesallofthedirtywork,asfollows:

void__declspec(naked)myDecode(){__asm{MOVEAX,DWORDPTRSS:[ESP+0x4]//getbufferMOVEDX,DWORDPTRSS:[ESP+0x8]//getsizePUSHEDX//pushsizePUSHEAX//pushbuffer➊CALL[origDecodeFunc]

MOVEDX,DWORDPTRSS:[ESP+0x4]//getthebuffer

PUSHEAX//storeeax(returnvalue)PUSHECX//storeecxPUSHEAX//pushsizePUSHEDX//pushbufferCALLreportDecode//reporttheresultsnowPOPECX//restoreecx➋POPEAX//restoreeax(returnvalue)

➌RETN8//returnandcleanstack}}

Iknewthebufferwasdecryptedinplace,meaningtheencryptedchunkwouldbeoverwrittenwiththedecryptedoneoncethecalltodecode()wascomplete.ThismeantthatmyDecode()hadtocalltheoriginaldecode()function➊beforecallingthereportDecode()function,whichwouldgivetheresultsofthedecoding.Ultimately,myDecode()alsoneededtoreturnwiththesamevaluethattheoriginaldecode()functionwouldandcleanupthestack,andthefinalPOP➋andRETN➌instructionstookcareofthat.

PlacingtheHooksThenextproblemIranintowasthatthehookswereforcodeinsidethemoduleAdobeAIR.dll,whichwasnotthemainmoduleofthegame.Becauseofthecode’slocation,Ineededtofindthebaseaddressesforthehooksabitdifferently.Additionally,sinceIneededthesehookstoworkacrossafewdifferentversionsofAdobeAIR,Ialsohadtofindtherightaddressesforeachversion.InsteadoftryingtogetmyhandsonallofthedifferentversionsofAdobeAIR,Itookanotherpageoutofairlog’splaybookanddecidedtoprogrammaticallylocatetheaddressesbywritingasmallmemoryscanner.BeforeIcouldwritethememoryscanner,IneededboththebaseaddressandsizeofAdobeAIR.dllsoIcouldlimitmymemorysearchtoonlythatarea.

IfoundthesevaluesusingModule32First()andModule32Next()asfollows:

MODULEENTRY32entry;entry.dwSize=sizeof(MODULEENTRY32);HANDLEsnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,NULL);

DWORDbase,size;if(Module32First(snapshot,&entry)==TRUE){➊while(Module32Next(snapshot,&entry)==TRUE){std::wstringbinaryPath=entry.szModule;➋if(binaryPath.find("AdobeAIR.dll")!=std::wstring::npos){size=(DWORD)entry.modBaseSize;base=(DWORD)entry.modBaseAddr;break;}

}}

CloseHandle(snapshot);

ThiscodeloopsthroughallmodulesintheprocessuntilitfindsAdobeAIR.dll➊.Whenitfindsthecorrectmoduleentry➋,itfetchesthemodBaseSizeandmodBaseAddrpropertiesfromitandbreaksoutimmediately.

ThenextstepwasfindingasequenceofbytesIcouldusetoidentifythefunctions.Idecidedtousethebytecodesurroundingeachcall.Ialsohadtomakesurethateachsequencewasuniquewhileavoidingtheuseofanyconstantsinthepatternstoensurethecode’sportability.Listing8-13showsthebytesequencesIendedupwith.

constcharencodeSeq[16]={0x8B,0xCE,//MOVECX,ESI0xE8,0xA6,0xFF,0xFF,0xFF,//CALLencode0x83,0xF8,0xFF,//CMPEAX,-10x74,0x16,//JESHORTendLoop0x03,0xF8,//ADDEDI,EAX0x3B,0xBE};//partofCMPEDI,[ESI+0x3C58]constchardecodeSeq[12]={0x8B,0xCE,//MOVECX,ESI0xE8,0x7F,0xF7,0xFF,0xFF,//CALLdecode0x83,0xF8,0xFF,//CMPEAX,-10x89,0x86};//partofMOV[ESI+0x1C54],EAX

Listing8-13:Theencode()anddecode()bytesequences

NoticetheCALLinstructionineachpattern;thesearethecallstotheAdobeAIRfunctionsInamedencode()anddecode().Iscannedforthesesequenceswiththefollowingfunction:

DWORDfindSequence(DWORDbase,DWORDsize,constchar*sequence,unsignedintseqLen){for(DWORDadr=base;adr<=base+size–seqLen;adr++){if(memcmp((LPVOID)sequence,(LPVOID)adr,seqLen)==0)returnadr;}return0;}

TreatingthememoryofAdobeAIR.dllasabytearray,thefindSequence()functionlooksforasequenceofbytesasasubsetofthatbytearrayand

returnstheaddressofthefirstmatchitfinds.WiththefindSequence()functionwritten,findingtheaddressesIneededtohookencode()anddecode()wassimple.Here’showthosecallslooked:

DWORDencodeHookAt=findSequence(base,size,encodeSeq,16)+2;DWORDdecodeHookAt=findSequence(base,size,decodeSeq,12)+2;

Sinceeachtargetcallwas2bytesintoitsreceptivesearchsequence,allIhadtodowaslocateeachsequenceandadd2.Afterthat,thefinalstepwastoplacethehooksusingthemethoddescribedin“CallHooking”onpage153.

Withmyhookfinished,Icouldseeeverysinglepieceofdatathatwentbetweenthegame’sclientandserver.Moreover,sincetheRTMPSprotocolsendsserializedActionScriptobjects,thedatawasbasicallyself-documenting.Everysinglepieceofinformationwasaccompaniedbyavariablename.Everyvariableexistedasamemberofawell-describedobject.Everyobjecthadaconsistentname.LikeIsaid—itwasagoldmine.

ApplyingJumpHooksandVFHookstoDirect3DUnliketheAdobeAIRhookIjustdescribed,hooksforDirect3D(the3DgraphicscomponentofMicrosoft’sDirectXAPI)areverycommonandhighlydocumented.Direct3Disubiquitousintheworldofgaming:amajorityofPCgamesusethelibrary,whichmeansthathookingitgivesyouaverypowerfulmethodforinterceptingdataandmanipulatingthegraphicslayersofmanydifferentgames.YoucanuseaDirect3Dhookforanumberoftasks,suchasdetectinglocationsofhiddenenemyplayers,increasingthelightingofdarkin-gameenvironments,andseamlesslydisplayingadditionalgraphicalinformation.MakingeffectiveuseofaDirect3DhookrequiresyoutolearnabouttheAPI,butthere’smorethanenoughinformationinthisbooktogetyoustarted.

Inthissection,I’llgiveyouahigh-levelintroductiontoagameloopthatusesDirect3DbeforedivingrightintotheimplementationofaDirect3Dhook.RatherthandetailingtheinternalsandgivingyoutheanalyticalbackstoryasIdidwiththeAdobeAIRhook,I’llgooverthemostpopularDirect3Dhookmethod,asitiswelldocumentedandusedbythemajorityof

Direct3Dhookmethod,asitiswelldocumentedandusedbythemajorityofgamehackers.

Theonlineresourcesforthisbookincludetwopiecesofexamplecodeforthissection;findthosefilesnowifyouwanttofollowalong.Thefirstpart,anexampleDirect3D9applicationforyoutohackon,canbefoundunderGameHackingExamples/Chapter8_Direct3DApplication.Thesecondpart,theactualhook,isunderChapter8_Direct3DHook.

TherearemultipleversionsofDirect3Dinuseatanygiventime,andtherearewaystohookeachone.Forthisbook,I’llfocusonhookingDirect3D9,becauseitistheonlycommonlyusedversionthatissupportedbyWindowsXP.

NOTE

EventhoughXPhasreachedendoflife,manypeopleinlessdevelopedcountriesstilluseitasaprimarygamingsystem.Direct3D9worksonallversionsofWindowsandisnearlyaspowerfulasitssuccessors,somanygamecompaniesstillprefertouseitovernewerversionsthatdon’thaveasmuchbackwardcompatibility.

TheDrawingLoopLet’sjumprightinwithacrashcourseonhowDirect3Dworks.InsideaDirect3Dgame’ssourcecode,you’llfindaninfiniteloopthatprocessesinputandrendersgraphics.Eachiterationinthisdrawingloopiscalledaframe.Ifwecutoutalltheextraneouscodeandfocussimplyonabareskeleton,wecanvisualizeagame’smainloopwiththefollowingcode:

intWINAPIWinMain(args){/*SomecodeherewouldbecalledtosetupDirect3Dandinitializethegame.Leavingitoutforbrevity.*/MSGmsg;while(TRUE){/*Somecodewouldbeheretohandleincomingmouseandkeyboardmessages.*/drawFrame();//thisisthefunctionwecareabout}/*Somecodeherewouldbecalledtocleanupeverythingbeforeexiting.*/}

Thisfunctionistheentrypointofthegame.Simplyput,itinitializesthegameandthenentersthegame’smainloop.Insidethemainloop,itexecutescoderesponsibleforprocessinguserinputbeforecallingdrawFrame()toredrawthescreenusingDirect3D.(CheckoutthecodeinGameHackingExamples/Chapter8_Direct3DApplicationtoseeafullyfunctionalgameloop.)

Eachtimeitiscalled,thedrawFrame()functionredrawstheentirescreen.Thecodelookssomethinglikethis:

voiddrawFrame(){➊device->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);device->BeginScene();//drawingwillhappenheredevice->EndScene();device->Present(NULL,NULL,NULL,NULL);}

Afterclearingthescreenwithdevice->Clear➊,thedrawFrame()functioncallsdevice->BeginScene()tounlockthescenefordrawing.Itthenexecutessomedrawingcode(whatthatdrawingcodeactuallydoesisn’timportantrightnow)andlocksthescenewithadevice->EndScene()call.Tofinishup,itrendersthescenetothescreenbycallingthedevice->Present()function.

Noticethatallofthesefunctionsarecalledasmembersofsomeinstancecalleddevice.ThisissimplyanobjectinstancerepresentingtheDirect3Ddevice,whichisusedtoinvokeallsortsofdrawingcalls.Also,noticethatthisfunctionisdevoidofanyactualdrawingcode,butthat’sokay.It’sreallyonlyimportantforyoutounderstandthehigh-levelconceptsofdrawingloops,frames,andtheDirect3Ddevice.Torecap,gameshaveamainloopwithtworesponsibilities:

•Handlingincomingmessages

•Drawingthegametothescreen

Eachiterationinthisloopiscalledaframe,andeachframeisdrawnbyadevice.Takingcontrolofthedevicegivesyouaccesstothemostsensitiveanddescriptivedetailsofthegame’sstate;thatis,you’llbeabletopeekintothegame’sstateafterthedatahasbeenparsed,processed,andrenderedtothescreen.Moreover,you’llbeabletomodifytheoutputofthisstate.These

thescreen.Moreover,you’llbeabletomodifytheoutputofthisstate.Thesetwosuperpowersenableyoutopulloffallkindsofawesomehacks.

FindingtheDirect3DDeviceTotakecontrolofaDirect3Ddevice,youhookthememberfunctionsinthedevice’sVFtable.Unfortunately,however,usingtheDirect3DAPItoinstantiateyourowninstanceofthesamedeviceclassfrominjectedcodedoesn’tmeanyou’llshareaVFtablewiththegame’sinstance.Direct3DdevicesuseacustomizedruntimeimplementationofVFtables,andeachdevicegetsitsownuniqueVFtable.Additionally,devicessometimesrewritetheirownVFtables,removinganyhooksandrestoringtheoriginalfunctionaddresses.

BothoftheseDirect3Dquirksleaveyouwithoneinevitableoption:youmustfindtheaddressofthegame’sdeviceandmodifyitsVFtabledirectly.Here’show:

1. CreateaDirect3DdeviceandtraverseitsVFtabletolocatethetrueaddressofEndScene().

2. PlaceatemporaryjumphookonEndScene().

3. Whenthejumphookcallbackisexecuted,storetheaddressofthedevicethatwasusedtocallthefunction,removethehook,andrestoreexecutionnormally.

4. Fromthere,useVFhookstohookanymemberfunctionoftheDirect3Ddevice.

JumpHookingEndScene()SinceeverydevicewillcallEndScene()attheendofeachframe,youcanhookEndScene()usingajumphookandinterceptthegame’sdevicefromyourhookcallback.UniquedevicesmayhavetheirownuniqueVFtables,butthedifferenttablesstillpointtothesamefunctions,soyoucanfindtheaddressofEndScene()intheVFtableofanyarbitrarydevice.UsingstandardDirect3DAPIcalls,youcancreateyourowndevicelikethis:

LPDIRECT3D9pD3D=Direct3DCreate9(D3D_SDK_VERSION);if(!pD3D)return0;

D3DPRESENT_PARAMETERSd3dpp;ZeroMemory(&d3dpp,sizeof(d3dpp));d3dpp.Windowed=TRUE;d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;d3dpp.hDeviceWindow=hWnd;

LPDIRECT3DDEVICE9device;HRESULTres=pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,&device);if(FAILED(res))return0;

ExplaininghoweverythinginDirect3Dworksisoutsidethescopeofthisbook,sojustknowthatyoucancopythiscodetocreateaDirect3DdevicethatcontainstheEndScene()functionasamember.TheEndScene()addressisatindex42intheVFtableofdevice(see“TheMeaningofDevice,Direct3D,andVFHooks”boxtolearnhowtofindthatindex),andyoucanreaditusingasubsetoftheVFtablehookingcodefrom“UsingaVFTableHook”onpage159,asfollows:

DWORDgetVF(DWORDclassInst,DWORDfuncIndex){DWORDVFTable=readMemory<DWORD>(classInst);DWORDhookAddress=VFTable+funcIndex*sizeof(DWORD);returnreadMemory<DWORD>(hookAddress);}DWORDEndSceneAddress=getVF((DWORD)device,42);

Onceyou’veobtainedtheaddress,yourdiscoverydevicehasserveditspurpose,anditcanbedestroyedwithacalltotheRelease()function:

pD3D->Release();device->Release();

WiththeaddressofEndScene()inhand,you’dbereadytostartthinkingabouthowtoplaceyourhookinmemory.Butsinceyoujusthaveafunctionaddress,youronlyoptionistoplaceajumphookatthetopofthefunction.

THEMEANINGOFDEVICE,DIRECT3D,ANDVF

HOOKSIfyou’rewonderinghowIknowthattheindexoftheEndScene()functionis42,you’vecometotherightbox.SinceDirect3D9isafreelyavailablelibrary,youcanactuallyseequiteabitofwhatgoesonunderthehood.Themainheaderfileforthelibraryisd3d9.h.Ifyouopenthisfileinyoureditorandsearchfor“EndScene,”you’llendupinthemiddleofalargeclassdefinitionthatspecifiesseveralfunctionsusingCmacros.ThisisthebaseclassforallDirect3D9deviceimplementations,anditdefinesthevirtualfunctionsusedbytheclass.

TheVFtableisconstructedinthesameorderasthefunctionsaredefinedincode,soyoucandeterminetheindexofanymemberfunctionbysimplycountingthelines.Youcanscrolltothetopoftheclassdefinition(atline426inmyversionofthelibrary,andprobablyyourstoo),notethelinewherethefirstfunctionisdeclared(line429),andthenscrolltotheEndScene()definitionandnotethatline(line473).Finally,countthenumberofblankorcommentedlines(twoforme)anddosomemath:473–429–2=42.

Presto!TheEndScene()functionisthe43rdfunctiondeclared,soitsitsatthe42ndspotintheVFtable.Anotheradvantagetohavingthisheaderisthatyoucanseethename,argumenttypes,argumentnames,andreturntypeofeverysinglefunctioninthedeviceclass.Sowhenyou’rewritingyourownhooksinthefuture,you’llknowexactlywheretolook.

PlacingandRemovingtheJumpHookSinceyou’rejustusingthehooktofindthedevice,youneedtocallitonlyonce.Afterobtainingthedevice,you’llremovethejumphookandrestoreexecutionbacktothestartofEndScene()sothatthedrawingloopcancarryonitswork.Believeitornot,thismakesyourlifemucheasier.Sincethecodewillberestoredimmediately,there’snoneedforyourtrampolinetoexecutethecommandsthatarereplacedbythejump,andthere’snoneedtopadthejumpwithNOPs.Allyouneedtodoisstoretheoriginalbytesandplacethehook.Todoso,youuseaslightlytweakedversionofthejump-hookingcodefromListing8-11:

unsignedchar*hookWithJump(DWORDhookAt,DWORDnewFunc){DWORDnewOffset=newFunc-hookAt-5;➊autooldProtection=protectMemory<BYTE[5]>(hookAt,PAGE_EXECUTE_READWRITE);unsignedchar*originals=newunsignedchar[5];for(inti=0;i<5;i++)➋originals[i]=readMemory<unsignedchar>(hookAt+i);➌writeMemory<BYTE>(hookAt,0xE9);writeMemory<DWORD>(hookAt+1,newOffset);protectMemory<BYTE[5]>(hookAt,oldProtection);returnoriginals;}

LikethefunctioninListing8-11,thisfunctionmakesthememorywritable➊,placesthehook➌,andrestoresthememoryprotection.Beforeplacingthehook,itallocatesa5-bytebuffercalledoriginals➋andfillsitwiththeoriginalbytes.Afterthehookisplaced,itreturnsoriginalstothecallingfunction.

Whenit’stimetoremovethehook,passoriginalstothefollowingfunction:

voidunhookWithJump(DWORDhookAt,unsignedchar*originals){autooldProtection=protectMemory<BYTE[5]>(hookAt,PAGE_EXECUTE_READWRITE);for(inti=0;i<5;i++)writeMemory<BYTE>(hookAt+i,originals[i]);protectMemory<BYTE[5]>(hookAt,oldProtection);delete[]originals;}

Thiscodesimplyiteratesoveroriginalsandquietlyplacesthose5bytesbackwheretheywerefoundsothateverythingisasexpectedwhenexecutionreturnstotheEndScene()function.Whenthetimecomes,youcanplaceandremoveyouractualhookusingtwolinesofcode,likethis:

autooriginals=hookWithJump(EndSceneAddress,(DWORD)&endSceneTrampoline);unhookWithJump(EndSceneAddress,originals);

OnceyouhavethehookWithJump()andunhookWithJump()functions,it’stimetopreparethecallbackandfindthedevice.

WritingtheCallbackandTrampolineEventhoughyoucanobtaintheEndScene()addressfromaVFtable,theEndScene()functiondoesn’tactuallyfollowthe__thiscallconvention.Direct3DclassesaresimplewrappersaroundaCAPI,andallofthememberfunctioncallsareforwardedto__stdcallfunctionsthattakeaclassinstanceasafirstparameter.Thismeansthatyourtrampolineonlyneedstograbthedevicefromthestack,passittothecallback,andthenjumpbacktoEndScene().Thecallbackonlyhastoremovethejumphookbeforereturningtothetrampoline.

Thefinalcodeforthecallbackandtrampolinetothisjumphooklookssomethinglikethis:

LPDIRECT3DDEVICE9discoveredDevice;DWORD__stdcallreportInitEndScene(LPDIRECT3DDEVICE9device){discoveredDevice=device;unhookWithJump(EndSceneAddress,originals);returnEndSceneAddress;}__declspec(naked)voidendSceneTrampoline(){__asm{MOVEAX,DWORDPTRSS:[ESP+0x4]PUSHEAX//givethedevicetothecallback➊CALLreportInitEndSceneJMPEAX//jumptothestartofEndScene}}

UsingthehookWithJump()function,youcanplaceajumphookonEndScene()thatcallstheendSceneTrampoline()function.Whenthegame’sdevicecallstheEndScene()function,thetrampolinefunctioncallsthereportInitEndScene()function➊.ThereportInitEndScene()functionstoresthecaptureddevicepointertoaglobalvariablecalleddiscoveredDevice,removesthehookbycallingunhookWithJump(),andreturnstheaddressofEndScene()tothetrampoline.Tofinishup,thetrampolinejumpsdirectlytoEAX,whichwillbeholdingtheaddressthatwasreturnedfromthereportingfunction.

NOTE

YoucanusejumphookstocompletelyskiptheVFtablehookingthatI’llshowyou,butit’sveryunreliabletouse“dumb”jumphooksoncommonlyhookedAPIfunctions.Consistentlyobtaininggoodresultswithonlyjumphooksrequiresprofessionalhookinglibraries,andI’dratherteachyouhowtodoitcompletelyonyourown.

Atthispoint,allthat’slefttodoishooktheVFtableofdiscoveredDevicetohackthegame.ThenexttwosectionswillwalkyouthroughhooksontheEndScene()andReset()functions,whicharerequiredifyouwantastablehook.

WritingaHookforEndScene()AhookonEndScene()isusefulbecauseitallowsyoutointerceptacompletedframejustbeforeitisrendered;youcaneffectivelyexecuteyourownrenderingcodeinsidethegameloop.Asyousawwhenlocatingthisfunction’saddressin“JumpHookingEndScene()”onpage178,thisfunctionisatindex42intheVFtable.YoucanhookEndScene()usingaVFhookasfollows:

typedefHRESULT(WINAPI*_endScene)(LPDIRECT3DDEVICE9pDevice);_endSceneorigEndScene=(_endScene)hookVF((DWORD)discoveredDevice,42,(DWORD)&myEndScene);HRESULTWINAPImyEndScene(LPDIRECT3DDEVICE9pDevice){//drawyourownstuffherereturnorigEndScene(pDevice);}

ThiscodeusesthehookVF()functionfrom“UsingaVFTableHook”onpage159tohookEndScene()atindex42ofdiscoveredDevice,usingmyEndScene()asthecallbackfunction.AdirectDirect3DdevicewilloccasionallyrepatchitsownVFtableandrestoretheoriginalfunctionaddresses.ThistypicallyhappensfromwithintheEndScene()function,meaningyoualsohavetorepatchtheVFtableaftercallingtheoriginalEndScene()function.Thereareafewchangesyoucanmaketothishooktohandlethat,asshowninListing8-14.

_endSceneorigEndScene=NULL;voidplaceHooks()

{autoret=hookVF((DWORD)discoveredDevice,42,(DWORD)&myEndScene);if(ret!=(DWORD)&myEndScene)//don'tpointtoyourhookorigEndScene=(_endScene)ret;}placeHooks();

HRESULTWINAPImyEndScene(LPDIRECT3DDEVICE9pDevice){//drawyourownstuffhereautoret=origEndScene(pDevice);placeHooks();//updatehooksreturnret;}

Listing8-14:FinalcodetohookEndScene()

ThecodetoplacethehookhasbeenmovedintoafunctioncalledplaceHooks()soitcanbecalledmultipletimeswithease.Thecallbackfunctionstillforwardsthecalltotheoriginalfunction,butitmakessuretocallplaceHooks()beforereturning.Thisensuresthatthehookisalwaysactive,eveniftheoriginalEndScene()functionremovesit.

AnotherpointtonoticeisthatplaceHooks()updatestheaddressoforigEndScene()everytimethehookisreplaced,aslongastheaddressreturnedfromhookVF()isn’ttheaddressofthemyEndScene()function.Thisdoestwodistinctthings.First,itallowsotherapplicationstohookEndScene()withoutsteppingontheirtoes,sinceitwillupdateorigEndScene()towhateverisseenintheVFtable.Second,itmakessurethatthevalueoforigEndScene()canneverbetheaddressofourcallback,preventingapotentialinfiniteloop.Aninfiniteloopispossibleotherwise,becauseorigEndScene()doesn’talwaysfixthedevice’sVFtable,meaningplaceHooks()canbecalledwhentheVFtablestillcontainsthemyEndScene()function.

WritingaHookforReset()Whenyou’reusingaDirect3Dhookinproduction,you’llbedoingallkindsoftaskslikedrawingcustomtext,displayingimagesrelatedtoyourbot,andinteractingwithfunctioncallsfromthegame.ThesetaskswillrequireyoutocreateyourownDirect3Dobjectsthataretiedtothegame’sdevice,andthatcanbeaproblem.Fromtimetotime,thegamemaycompletelyresetitsdevicethroughaReset()function.Whenadeviceisreset,you’llneedto

updateanyobjects(mostcommonlyfontsandsprites)thatyou’vecreatedforthedevice,usingtheirOnLostDevice()memberfunctions.

SinceReset()iscalledfromtheVFtableofthedevice,youcanuseahookonittotellyouwhenthedevicehasbeenreset.Reset()takestwoparametersandisatindex16intheVFtable.YoucanaddthiscodetoplaceHooks()inListing8-14tohooktheReset()function:

autoret=hookVF((DWORD)discoveredDevice,16,(DWORD)&myReset);if(ret!=(DWORD)&myReset)origReset=(_reset)ret;

AndthisisthedeclarationtousefororigReset:

typedefHRESULT(WINAPI*_reset)(LPDIRECT3DDEVICE9pDevice,D3DPRESENT_PARAMETERS*pPresentationParameters);_resetorigReset=NULL;

Whenaresetissuccessful,theoriginalfunctionreturnsD3D_OK.YourhookfunctionrecognizesthisandcallsOnLostDevice()accordingly:

HRESULTWINAPImyReset(LPDIRECT3DDEVICE9pDevice,D3DPRESENT_PARAMETERS*pPresentationParameters){autoresult=origReset(pDevice,pPresentationParameters);if(result==D3D_OK){//callonLostDevice()forallofyourobjects}returnresult;}

Onceyoufillinthecontentsoftheif()statement,allofyourobjectsarereadytouseagain.

What’sNext?NowthatI’veshownyouhowtotakecontrolofagame’sDirect3Ddevice,you’reprobablywonderingwhatyoucandowithit.Unliketheotherexamplesinthebook,thecodeinthissectionandtheexamplecodedon’thaveaone-to-onecorrelation,butthefunctionalityisstillthesame.Here’sahigh-levelviewofthecorrelationbetweenthischapterandthecodeintheChapter8_Direct3DHookexampleproject.

ThefileDirectXHookCallbacks.hcontainsthecallbacksfortheEndScene()andReset()functions,twocallbacksforothercommonfunctions,andthetrampolineandreporterfunctionsforthetemporaryjumphook.Thesefunctionsareallprettymuchasdescribedinthischapter,excepttheycallintoasingletonclassdefinedinDirectXHook.handDirectXHook.cpp.Thissingletonclassisresponsibleforforwardingthecallstotheoriginalfunctions.

Theclassisalsoresponsibleforalloftheheavylifting,anditcontainsthecodetocreatethediscoverydevice,placethehooks,drawtext,handledeviceresets,anddisplayimages.Furthermore,itallowsexternalcodetoaddcustomcallbacksforeachhook,asyoucanseeinmain.cpp.Here,you’llseeanumberofdifferentcallbacksthataredrawingcustomtext,addingnewimagestothescreen,andchangingthetexturesofmodelsthataredrawnbythegame.Irecommendpokingaroundinthecodetogetabetterunderstandingofwhat’sgoingon,butdon’tgettoocarriedaway.We’lldiveintothiscodeinChapter9totalkaboutallthecoolhacksitcando.

OPTIONALFIXESFORSTABILITYTheReset()andEndScene()hooksdescribedinthischaptershouldworkwellforanygamerunningDirect3D9,butitisslightlyunstable.IfthegametriestoexecuteEndScene()whenthejumphookisplaced,itwillcrashbecausethebytesarebeingmodified.Therearetwowaystofixthis.First,youcanplacethejumphookfromwithinanIAThookonPeekMessage().ThiswillworkbecauseplacinganIAThookisathread-safeoperation,butitassumesthatPeekMessage()iscalledonlyfromthesamethreadthatdoestheDirect3Ddrawing.

Asafer,butmorecomplex,alternativeistoiterateovereverythreadinthegame(similartohowitworkedforthreadhijacking)anduseSuspendThread()topauseallthreadsinthegame(exceptfortheoneplacingthehook,ofcourse).Beforepausingathread,youmustmakesureitsEIPisnotexecutingthefirst5bytesofEndScene().Afterthehookisplaced,youmustuseResumeThread()torestoreexecutionwithyourhookinplace.

ClosingThoughtsControlflowmanipulationisaveryimportantskillingamehacking,andalotofthehacksinthisbookrelyonit.Throughoutthenexttwochaptersyou’lllearnhowtocreatecommonhacksusingtheDirect3Dhook,andyou’llgetabetterideaofthegeneralusecasesofhooking.Evenifyoufeelalittleshaky,continuetoChapter9.ThecodeexamplestherecenterontheDirect3Dhookandwillgetyouevenmorefamiliarwithhookingtechniques.

PART4CREATINGBOTS

9USINGEXTRASENSORYPERCEPTIONTO

WARDOFFFOGOFWAR

Fogofwar(oftenshortenedtojustfog)isamechanismthatgamedeveloperscommonlyusetolimitaplayer’ssituationalawarenessandhideinformationaboutthegameenvironment.Fogisoftenaliterallackofsightinmassiveonlinebattlearena(MOBA)games,buttheconceptalsoincludesanylackorobscurityofpertinentgameplayinformation.Cloakedfigures,darkrooms,andenemieshidingbehindwallsareallformsoffog.

Gamehackerscanreduceorevencompletelyremovefogusinganextrasensoryperception(ESP)hack.AnESPhackuseshooking,memorymanipulation,orbothtoforceagametodisplayhiddeninformation.Thesehackstakeadvantageofthefactthatsometypesoffogareoftenimplementedontheclientside,asopposedtotheserverside,meaningthatthegameclientsstillcontaininformation(partialorcomplete)aboutwhatisbeinghidden.

Inthischapter,youwilllearnhowtoimplementdifferenttypesofESPhacks.First,you’lllearntolightupdarkenvironments.Next,you’llusex-rayvisiontoseethroughwalls.Finally,you’lllearnaboutzoomhacking,tweakingheads-updisplays,andothersimpleESPhacksthatcanrevealallsortsofuseful(butotherwisehidden)informationaboutthegameyou’replaying.

BackgroundKnowledgeThischapterstartsthetransitionfromhacking,puppeteering,andreverseengineeringtocoding.Fromhereonout,you’llbelearninghowtoactuallycodeyourownhacks.Tokeepontopic,everythingI’vetalkedaboutthusfarwillbetreatedasbackgroundknowledge.Ifyouseeatechniqueusedthatyoudon’tquiteremember,suchasmemoryscanning,settingmemorybreakpoints,hooking,orwritingmemory,flipbacktotherelevantchaptersandstudythemabitmorebeforecontinuing.Throughoutthetext,you’llfindnotestoremindyouwhereyoucanbrushuponcertaintopics.

Specifically,thischapterwilltalkalotaboutDirect3D.In“ApplyingJumpHooksandVFHookstoDirect3D”onpage175,Iexplainedhowtohookintoagame’sDirect3Ddrawingloop.TheexamplecodeforthatchapterincludesafullyfeaturedDirect3DhookingengineinGameHackingExamples/Chapter8_Direct3DHook.Alotofthehacksinthischapterbuildonthathook,andtheirexamplecodecanbefoundinthemain.cppfileoftheDirect3Dhookcode.YoucanrunthecompiledapplicationfromGameHackingExamples/Chapter8_Direct3DApplicationtoseethehacksinactiononatestapplication.

RevealingHiddenDetailswithLighthacksLighthacksincreaselightingindarkenvironments,allowingyoutoclearlyseeenemies,treasurechests,pathways,andanythingelsethatisnormallyobscuredbydarkness.Lightingisoftenacosmeticchangethat’saddedatagame’sgraphicallayer,anditcanusuallybedirectlymodifiedwithahookonthegraphicslayer.

Optimallightingdependsoncameraorientation,environmentlayout,andevenspecifictraitsofagame’sengine,andyoucanmanipulateanyofthesefactorstocreatelighthacks.Buttheeasiestwayissimplytoaddmorelighttoaroom.

AddingaCentralAmbientLightSourceTheonlineresourcesforthisbookincludetwosmalllighthackexamples.ThefirstistheenableLightHackDirectional()functioninmain.cpp,whichis

showninListing9-1.

voidenableLightHackDirectional(LPDIRECT3DDEVICE9pDevice){D3DLIGHT9light;ZeroMemory(&light,sizeof(light));light.Type=D3DLIGHT_DIRECTIONAL;light.Diffuse=D3DXCOLOR(0.5f,0.5f,0.5f,1.0f);light.Direction=D3DXVECTOR3(-1.0f,-0.5f,-1.0f);

pDevice->SetLight(0,&light);pDevice->LightEnable(0,TRUE);}Whenyouknowhowmuchexperienceyou

Listing9-1:Adirectionallighthack

ThiscodeiscalledfromtheEndScene()hook,anditaddslighttothescenebycreatingalightsourcecalledlight.Thecodesetslight.Typetodirectional,whichmeansthelightsourcewillactlikeaspotlightandprojectlightinaspecificdirection.Thecodethensetsthered,green,andbluevaluesoflight.Diffuseto0.5,0.5,and0.5,givingthelightanoff-whiteshinewhenreflectedfromasurface.Next,itsetslight.Directiontoanarbitrarypointinthethree-dimensionalspace.Finally,thecodeusesthegame’sDirect3Ddevicetosetupthelightatindex0andenablelightingeffects.

NOTE

Intheexampleapplication,thelightshinesupandtotherightfromthebottomleftofthescene.Youmayneedtochangethislocationdependingonhowyourtargetgameisrendered.

Notethatinsertingthelightatindex0worksforthisproofofconcept,butitwon’talwayswork.Gamestypicallyhavemultiplelightsourcesdefined,andsettingyourlightatanindexthegameusesmightoverridecriticallightingeffects.Inpractice,youmighttrysettingtheindextoanarbitrarilyhighnumber.There’sanissuewiththistypeoflighthack,though:directionallightswillbeblockedbyobjectssuchaswalls,creatures,andterrain,meaningshadowscanstillbecast.Directionallightsworkgreatforwide-openspaces,butnotsowellfortightlywoundcorridorsorundergroundcaves.

IncreasingtheAbsoluteAmbientLightTheotherlighthackmethod,seenintheenableLightHackAmbient()function,isfarmoreaggressivethantheoneinListing9-1.Itaffectsthelightlevelglobally,ratherthanaddinganextralightsource.Here’swhatthecodelookslike:

voidenableLightHackAmbient(LPDIRECT3DDEVICE9pDevice){pDevice->SetRenderState(D3DRS_AMBIENT,D3DCOLOR_XRGB(100,100,100));}

Thislighthacksetstheabsoluteambientlight(whichyouindicatebypassingD3DRS_AMBIENTtotheSetRenderState()function)toamedium-strengthwhite.TheD3DCOLOR_XRGBmacrosetsthatstrength,taking100asitsparametersforthered,green,andbluelevels.Thislightsupobjectsusinganomnidirectionalwhitelight,effectivelyrevealingeverythingatthecostofshadowsandotherlighting-baseddetails.

CreatingOtherTypesofLighthacksTherearemanyotherwaystocreatelighthacks,buttheydifferfromgametogame.OnecreativewaytoaffectthelightinagameistoNOPthecodethatthegameusestocallthedevice->SetRenderState()function.Sincethisfunctionisusedtosetuptheglobalambientlightstrength,disablingcallstoitleavesDirect3Datthedefaultlightsettingsandmakeseverythingvisible.Thisisperhapsthemostpowerfultypeoflighthack,butitrequiresyourbottoknowtheaddressofthelightingcodetoNOP.

Therearealsomemory-basedlighthacks.Insomegames,playersandcreaturesemitlightofdifferentcolorsandstrengths,oftendependingonattributesliketheirequipment,mount,oractivespells.Ifyouunderstandthestructureofthegame’screaturelist,youcandirectlymodifythevaluesthatdetermineacreature’slightlevel.

Forinstance,imagineagameinwhichcharactersemitabluishballoflightwhenunderahealingorstrengtheningspell.Somewhereinthegame’smemoryarevaluesassociatedwitheachcreaturethattellthegamethecolorandintensityoflightthecreatureshouldemit.Ifyoucanlocatethesevaluesinmemory,youcanchangethemsothatthecreatureseffectivelyemitorbsoflight.Thistypeoflighthackiscommonlyusedingameswitha2Dtop-

oflight.Thistypeoflighthackiscommonlyusedingameswitha2Dtop-downstyle,sincetheorbsaroundindividualcreaturesproduceacoolartisticeffectwhilesheddinglightonimportantpartsofthescreen.In3Dgames,however,thissortofhackjustturnscreaturesintoblobsoflightthatrunaround.

YoucanalsohooktheSetLight()memberfunctionatindex51intheVFtableofthegame’sDirect3Ddevice.Then,wheneveryourhookcallbackisinvoked,youcanmodifythepropertiesoftheinterceptedD3DLIGHT9lightstructurebeforepassingittotheoriginalfunction.Youmight,forinstance,changealllightstotheD3DLIGHT_POINTtype,causinganyexistinglightsourcesinthegametoradiatelightineverydirectionlikealightbulb.Thistypeoflighthackisverypowerfulandaccurate,butitcanproducesomedisturbingvisuals.Italsotendstobreakinanyenvironmentthathasnolighting,andopaqueobstaclesstillblockpointlightsources.

Lighthacksareverypowerful,buttheydon’trevealanything.Ifinformationishiddenbehindanobstacle,ratherthanbydarkness,you’llneedawallhacktorevealit.

RevealingSneakyEnemieswithWallhacksYoucanusewallhackstoshowenemiesthatarehiddenbywalls,floors,andotherobstacles.Thereareafewwaystocreatethesehacks,butthemostcommonmethodtakesadvantageofatypeofrenderingknownasz-buffering.

RenderingwithZ-BufferingMostgraphicsengines,includingDirect3D,supportz-buffering,whichisawaytomakesurethatwhenthereareoverlappingobjectsinascene,onlythetopobjectisdrawn.Z-bufferingworksby“drawing”thescenetoatwo-dimensionalarraythatdescribeshowclosetheobjectateachpixelonthescreenistotheviewer.Thinkofthearray’sindicesasaxes:theycorrespondtothex-axis(rightandleft)andy-axis(upanddown)foreachpixelonthescreen.Eachvaluestoredinthearrayisthez-axisvalueforapixel.

Whenanewobjectappears,whetheritisactuallydrawnonthescreenisdecidedbythez-bufferarray.Ifthespotattheobject’sx-andy-positionisalreadyfilledinthearray,thatmeansthere’sanotherobjectatthatpixelon

alreadyfilledinthearray,thatmeansthere’sanotherobjectatthatpixelonthescreen.Thenewobjectwillappearonlyifithasalowerz-axisvalue(thatis,ifit’sclosertotheviewer)thanthepixelalreadythere.Whenthesceneisfinishedbeingdrawntothearray,itisflushedtothescreen.

Toillustratethis,imagineathree-dimensionalspacethatneedstobedrawntoatwo-dimensionalcanvasbysomegamewith4×4-pixelviewport.Thez-bufferforthisscenariowouldlooklikeFigure9-1.

Figure9-1:Anemptyz-buffer

Tostart,thegamedrawsabluebackgroundthatcompletelyfillstheviewportandislocatedasfarawayonthez-axisaspossible;let’ssaythehighestz-valueis100.Next,thegamedrawsa2×2-pixelredrectangleatposition(0,0)withaz-positionof5.Finally,thegamedrawsa2×2-pixelgreenrectangleatposition(1,1)withaz-positionof3.Thez-bufferwouldnowlooklikeFigure9-2.

Figure9-2:Afilledz-buffer

Thez-bufferneatlyhandledoverlappingobjectsbasedontheirz-positions.Thegreensquarethat’sclosestonthez-axisoverlapstheredsquarethat’sabitfartheraway,andbothsquaresoverlapthebluebackground,whichisveryfaraway.

Thisbehaviorallowsagametodrawitsmap,players,creatures,details,andparticleswithoutworryingaboutwhatisactuallyvisibletotheplayer.Thisisahugeoptimizationforgamedevelopers,butitexposesalargeareaofattack.Sinceallgamemodelsarealwaysgiventothegraphicsengine,youcanusehookstodetectobjectsthattheplayercan’tactuallysee.

CreatingaDirect3DWallhackYoucancreatewallhacksthatmanipulatez-bufferinginDirect3DusingahookontheDrawIndexedPrimitive()function,whichiscalledwhenagamedrawsa3Dmodeltothescreen.Whenanenemyplayermodelisdrawn,a

wallhackofthistypedisablesz-buffering,callstheoriginalfunctiontodrawthemodel,andthenreenablesz-buffering.Thiscausestheenemymodeltobedrawnontopofeverythingelseinthescene,regardlessofwhat’sinfrontofit.Somewallhackscanalsorenderspecificmodelsinasolidcolor,suchasredforenemiesandgreenforallies.

TogglingZ-BufferingTheDirect3Dhookinmain.cppfromGameHackingExamples/Chapter8_Direct3DHookhasthisexamplewallhackintheonDrawIndexedPrimitive()function:

voidonDrawIndexedPrimitive(DirectXHook*hook,LPDIRECT3DDEVICE9device,D3DPRIMITIVETYPEprimType,INTbaseVertexIndex,UINTminVertexIndex,UINTnumVertices,UINTstartIndex,UINTprimCount){if(numVertices==24&&primCount==12){//it'sanenemy,dothewallhack}}

ThisfunctionisusedasacallbackforahookonDrawIndexedPrimitive()atVFindex82ofthegame’sDirect3Ddevice.Everymodelthegamedrawspassesthroughthisfunction,accompaniedbysomemodel-specificproperties.Byinspectingasubsetoftheproperties,namelythenumVerticesandprimCountvalues,thehookdetectswhenanenemymodelisdrawnandcommencesthewallhack.Inthisexample,thevaluesrepresentinganenemymodelare24and12.

Themagichappensinsidetheif()statement.Usingjustafewlinesofcode,thewallhackdrawsthemodelinawaythatignoresz-buffering,likeso:

device->SetRenderState(D3DRS_ZENABLE,false);//disablez-bufferingDirectXHook::origDrawIndexedPrimitive(//drawmodeldevice,primType,baseVertexIndex,minVertexIndex,numVertices,startIndex,primCount);device->SetRenderState(D3DRS_ZENABLE,true);//enablez-buffering

Simplyput,thiscodedisablesz-bufferingwhendrawingtheenemymodelandreenablesitafterward.Withz-bufferingoff,theenemyisdrawninfrontofeverything.

ChanginganEnemyTextureWhenamodelisrenderedonscreen,atextureisusedtoskinthemodel.Texturesare2Dimagesthatarestretchedaround3Dmodelstoapplythecolorsandpatternsthatmakeupthemodel’s3Dartwork.Tochangethewayanenemylookswhenit’sdrawninyourwallhack,youcansetittobedrawnwithadifferenttexture,asinthisexample:

//whenhookinitializesLPDIRECT3DTEXTURE9red;D3DXCreateTextureFromFile(device,"red.png",&red);//justbeforedrawingtheprimitivedevice->SetTexture(0,red);

Thefirstblockofthiscodeloadsthetexturefromafileandisexecutedonlyonce—whenthehookisinitialized.Thefullexamplecodedoesthisinaninitialize()function,whichgetscalledthefirsttimetheEndScene()hookcallbackisinvoked.ThesecondblockofthiscodehappensrightbeforethecalltotheoriginalDrawIndexedPrimitive()functioninthewallhack,anditcausesthemodeltobedrawnwiththecustomtexture.

FingerprintingtheModelYouWanttoRevealThetrickiestparttocreatingagoodwallhackisfindingtherightvaluesfornumVerticesandprimCount.Todothis,youcancreateatoolthatlogseveryuniquecombinationofthetwovariablesandallowsyoutoiterateoverthelistusingyourkeyboard.Workingexamplecodeforthistoolwon’tbeusefulintheexampleapplicationprovidedwiththischapter,butI’llgiveyousomehigh-levelimplementationdetails.

First,intheglobalscope,you’ddeclareastructurethathasmemberstostorethefollowing:

•numVerticesandprimCount

•Astd::setofthisstructure(let’scallitseenParams)

•Aninstanceofthatstructure(let’scallitcurrentParams)

Thestd::setrequiresacomparatorforthisstructure,soyou’dalsodeclareacomparisonfunctorthatcallsmemcmp()tocomparetwoofthestructuresusingmemcmp().EachtimetheDrawIndexedPrimitive()callbackis

invoked,yourhackcouldcreateastructureinstancewiththeinterceptedvaluesandpassittoaseenParams.insert()function,whichshouldinserttheparameterpairintothelistonlyifthepairisn’talreadythere.

UsingtheGetAsyncKeyState()WindowsAPIfunction,youcouldthendetectwhenthespacebarispressedandexecutesomethingsimilartothispseudocode:

autocurrent=seenParams.find(currentParam);if(current==seenParams.end())current=seenParams.begin();elsecurrent++;currentParams=*current;

ThiswouldsetcurrentParamstothenextpairinseenParamswhenthespacebarispressed.Withthiscodeinplace,youcouldusecodesimilartoawallhacktochangethetextureofmodelsmatchingcurrentParams.numVerticesandcurrentParams.primCount.Thetoolcouldalsodrawthosevaluesonthescreensoyoucouldseethemandwritethemdown.

Withatoollikethis,findingthepropermodelsisaseasyasstartingupagameinamodewhereyourcharacterwon’tdie(againstafriend,inacustomizationmode,andsoon),runningthebot,andpressingthespacebaruntileachmodelyouneedishighlighted.Onceyouhavethevaluesforyourtargetmodels,you’llmodifythenumVerticesandprimCountcheckinyourwallhacksoitknowswhichmodelstohighlight.

NOTE

Charactermodelsarecommonlymadeupofsmallermodelsforindividualbodysegments,andgamesoftenshowdifferentmodelsofacharacteratdifferentdistances.Thatmeansagamemayhave20ormoremodelsforonetypeofcharacter.Eveninthatcase,selectingonlyonemodel(say,theenemy’storso)toshowinyourwallhackmaybeenough.

GettingaWiderFieldofVisionwithZoomhacksManygamesintheMOBAandreal-timestrategy(RTS)genresusea3Dtop-downstylethatmakesthemimmunetowallhacks.Theyalsousedarknessonthemapasatypeoffog,butshowingthedarkareasusinga

darknessonthemapasatypeoffog,butshowingthedarkareasusingalighthackdoesn’tgiveanyextrainformation;modelshiddeninsidethefogareknownonlytothegameserver,nottotheclient.

ThisstylemakesmosttypesofESPhacksuseless:there’slittleunknowninformationtoreveal,sothesehacksonlyaugmentyourviewoftheinformationyoucanalreadysee.OnetypeofESPhack,however,canstillbehelpful.Zoomhacksletyouzoomoutmuchfartherthanagamenormallyallows,effectivelyrevealinglargeportionsofthemapthatyoucouldn’tseeotherwise—andthusgettingaroundthegame’swallhackandlighthackimmunity.

UsingNOPingZoomhacksMOBAandRTSgamestypicallyallowplayersavariablebutlimitedamountofzoom.Thesimplesttypeofzoomhackfindsthevalueofthezoomfactor(amultiplierthatchangesasthezoomlevelchanges,typicallyafloatordouble)andoverwritesitwithalargervalue.

Tofindthezoomfactor,fireupCheatEngineandsearchforafloatwithanunknowninitialvalue.(TobrushuponCheatEngine,headoverto“CheatEngine’sMemoryScanner”onpage5.)Forrescans,repeatthefollowingprocessuntilthereareonlyafewvalueslefttofindthezoomfactor:

1. Gotothegamewindowandzoomin.

2. SearchforanincreasedvalueinCheatEngine.

3. Gotothegamewindowandzoomout.

4. SearchforadecreasedvalueinCheatEngine.

Trytogetthevaluelistdowntooneoption.Toconfirmthattheremainingvalueisthezoomfactor,freezeitinCheatEngineandseehowzoombehavesin-game;freezingthepropervaluewilldisablezooming.Ifyoufailtofindthezoomfactorusingafloatsearch,retrythesearchusingadouble.Ifbothsearchesfail,trythemagainbutcorrespondzoominginwithdecreasedvaluesandzoomingoutwithincreasedvaluesinstead.Onceyou’vefoundthezoomfactorinmemory,youcanwriteasmallbottooverwriteittothezoomfactorthatbestsuitsyou.

MoreadvancedzoomhacksNOPthegamecoderesponsibleformakingsurethezoomfactoriswithinasetrange.YoushouldbeabletofindthiscodewithOllyDbg.Setamemoryon-writebreakpointonthezoomfactor,zoomin-gametotriggerthebreakpoint,andinspectthecodeatthebreakpoint.(TohoneyourOllyDbgmemorybreakpointskills,flipto“ControllingOllyDbgThroughtheCommandLine”onpage43.)Youshouldseethecodethatmodifiedthezoomfactor.Zoomlimitationcodeistypicallyeasytospot:constantsthatmatchtheminimumandmaximumzoomvaluesareadeadgiveaway.

Ifyoucan’tfindthelimitationcodeusingthismethod,thenthelimitationmaybeappliedwhenthegraphicsareredrawnatanewzoomlevel,ratherthanwhenthezoomfactorchanges.Inthiscase,switchyourbreakpointtomemoryon-readandlookforthesameclues.

ScratchingtheSurfaceofHookingZoomhacksYoucanalsocreatezoomhacksbyusingaDirect3Dhookonthefunctiondevice->SetTransform(type,matrix),butthisrequiresadeepunderstandingofhowagamesetsuptheplayer’sperspective.Thereareafewdifferentwaystomanageperspective,butyoucontrolzoomlevelusingeitherview(transformtypeD3DTS_VIEW)orprojection(transformtypeD3DTS_PROJECTION).

Properlymanipulatingtransformmatricesthatcontrolviewandprojectionrequiressomeprettyextensiveknowledgeofthemathematicsbehind3Dgraphics,though,soIstayawayfromthismethodatallcosts—andI’veneverhadtroublesimplymanipulatingthezoomfactor.Ifyou’reinterestedinthiskindofhack,though,Irecommendreadinga3Dgameprogrammingbooktolearnmoreabout3Dmathematicsfirst.

Butsometimes,evenazoomhackisn’tenough.Someusefulinformationmayremainhiddenasapartofagame’sinternalstateormaysimplybehardforaplayertodetermineatamoment’sglance.Forthesesituations,aheads-updisplayisthetoolforthejob.

DisplayingHiddenDatawithHUDsAheads-updisplay(HUD)isatypeofESPhackthatdisplayscriticalgameinformationinanoverlay.HUDsoftenresembleagame’sexistinginterface

fordisplayinginformationlikeyourremainingammunition,amini-map,yourcurrenthealthlevel,anyactiveabilitycooldowns,andsoon.HUDstypicallydisplayeitherhistoricaloraggregatedinformation,andthey’remostlyusedonMMORPGs.Theyareoftentextbased,butsomealsocontainsprites,shapes,andothersmallvisualeffects.

TheHUDsyoucancreatedependonwhatdataisavailableinthegame.Commondatapointsarethese:

•Experiencegainperhour(exp/h)

•Creaturekillsperhour(KPH)

•Damagepersecond(DPS)

•Goldlootedperhour(GPH)

•Healingperminute

•Estimatedtimeuntilnextlevel

•Amountofgoldspentonsupplies

•Overallgoldvalueofitemslooted

MoreadvancedcustomHUDsmaydisplaylargetablescontainingitemslooted,suppliesused,thenumberofkillsforeachtypeofcreature,andthenamesofplayersthathaverecentlybeenseen.

Beyondwhatyou’vealreadylearnedaboutreadingmemory,hookinggraphicsengines,anddisplayingcustomizeddata,there’snotmuchelseIcanteachyouabouthowtocreateaHUD.Mostgameshaveasimpleenougharchitecturethatyoucaneasilyobtainmostoftheinformationyouneedfrommemory.Then,youcanrunsomebasichourly,percentage,orsummationcalculationstogetthedataintoausableformat.

CreatinganExperienceHUDImagineyouwantaHUDthatdisplaysyourcurrentlevel,hourlyexperience,andhowlongyou’llhavetoplaybeforeyourcharacterlevelsup.First,youcoulduseCheatEnginetofindthevariablesthatcontainyourlevelandexperience.Whenyouknowthosevalues,youcanuseeitheragame-specificalgorithmorahardcodedexperiencetabletocalculatetheexperiencerequiredtoreachthenextlevel.

Whenyouknowhowmuchexperienceyouneedtolevelup,youcancalculateyourhourlyexperience.Putintopseudocode,thatprocessmightlooklikethis:

//thisexampleassumesthetimeisstoredinmilliseconds//forseconds,removethe"1000*"timeUnitsPerHour=1000*60*60timePassed=(currentTime-startTime)➊timePassedToHourRatio=timeUnitsPerHour/timePassed➋expGained=(currentExp-startExp)hourlyExp=expGained*timePassedToHourRatio

➌remainingExp=nextExp-currentExp➍hoursToGo=remainingExp/hourlyExp

Tofindyourhourlyexperience,hourlyExp,you’dstoreyourexperienceandthetimewhenyourHUDfirststarts;thesearestartExpandstartTime,respectively.ThisexamplealsoassumescurrentLevelandcurrentExparepreviouslydefined,wherecurrentLevelisthecharacter’slevelandcurrentExpisthecurrentamountofexperience.

Withthesevalues,hourlyExpcanbecalculatedbymultiplyingaratio➊ofthetimeunitsinanhourtothetimethathaspassedbytheexperiencegainedsincestartTime➋.Inthiscase,thetimeunitisamillisecond,sothetimeunitsgetmultipliedby1,000.

Next,currentExpissubtractedfromnextExptodeterminetheremainingexperience➌tolevelup.Tocalculatehowmanyhoursarelefttolevelup,yourremainingexperienceisdividedbyyourhourlyexperience➍.

Whenyouhaveallthisinformation,youcanfinallydisplayitonscreen.UsingtheDirect3Dhookingengineprovidedinthisbook’sexamplecode,you’ddrawthetextusingthiscallinsidetheEndScene()hookcallback:

hook->drawText(10,10,D3DCOLOR_ARGB(255,255,0,0),"Willreachlevel%din%0.20fhours(%dexpperhour)",currentLevel,hoursToGo,hourlyExp);

That’sallyouneedforaworking,experience-trackingHUD.VariationsofthesesameequationscanbeusedtocalculateKPH,DPS,GPH,andotherusefultime-basedmeasures.Furthermore,youcanusethedrawText()

functionoftheDirect3Dhooktodisplayanyinformationyoucanlocateandnormalize.ThehookalsocontainsaddSpriteImage()anddrawSpriteImage()functionsthatyoucanusetodrawyourowncustomimages,allowingyoutomakeyourHUDsasfancyasyouwant.

UsingHookstoLocateDataMemoryreadingisn’ttheonlywaytogetdataforacustomHUD.YoucanalsogatherinformationbycountingthenumberoftimesaspecificmodelisdrawnbytheDrawIndexedPrimitive()function,hookingthegame’sinternalfunctionsresponsiblefordrawingcertaintypesoftext,oreveninterceptingfunctioncallsresponsibleforprocessingdatapacketsfromthegameserver.Themethodsyouusetodothiswillbedrasticallydifferentforeverygame,andfindingthosemethodswillrequireyoutopaireverythingyou’velearnedfromthisbookwithyourowningenuityandprogramminginstincts.

Forinstance,tocreateaHUDthatdisplayshowmanyenemiesareonthemap,youcouldusethemodel-fingerprintingmethodsusedbywallhackstocountthenumberofenemiesandoutputthatnumbertothescreen.Thismethodisbetterthancreatingawaytoreadthelistofenemiesfrommemory,sinceitdoesn’trequirenewmemoryaddresseseverytimethegamepatches.

Anotherexampleisdisplayingalistofenemycooldowns,whichwouldrequireyoutointerceptincomingpacketsthattelltheclientwhichspelleffectstodisplay.Youcouldthencorrelatecertainspellswithcertainenemiesbasedonspellandenemylocation,spelltype,andsoon,andusethatinformationtotrackspellseachenemyhasused.Ifyoucorrelatethedatawithadatabaseofcooldowntimes,youcandisplayexactlywheneachenemyspellcanbeusedagain.Thisisespeciallypowerfulbecausemostgamesdon’tstoreenemycooldownsinmemory.

AnOverviewofOtherESPHacksInadditiontothehacksdiscussedinthischapter,thereareanumberofESPhacksthatdon’thavecommonnamesandarespecifictocertaingenresorevencertaingames.I’llquicklytakeyouthroughthetheory,background,andarchitectureofsomeofthesehacks.

RangeHacksRange hacks use a method similar to wallhacks to detect when themodelsfordifferenttypesofchampionsorheroesaredrawn.Thentheydrawcirclesonthegroundaroundeachheromodel.Theradiusofeachcirclecorrespondstothemaximumattackrangeofthechampionorheroitsurrounds,effectivelyshowingyouareaswhereyoucanbedamagedbyeachenemy.

Loading-ScreenHUDsLoading-screen HUDs are common in MOBA and RTS games thatrequireallplayerstositthroughaloadingscreenwhileeveryone’sgameis starting up.These hacks take advantage of the fact that such gamesoftenhavewebsiteswherehistoricalplayerstatisticscanbequeried.Youcanwriteabotthatautomaticallyqueriesthestatisticsofeachplayerinthegameandseamlesslydisplaysthe informationasanoverlayonyourloadingscreen,allowingyoutostudyyourenemiesbeforelaunchingintobattle.

Pick-PhaseHUDsPick-phaseHUDs are similar to their loading-screen cousins, but theyare displayed during the pregame phase when each player is picking achampion or hero to play. Instead of showing enemy statistics, pick-phase HUDs show statistics about allies. This allows you to quicklyassessthestrengthsandweaknessesofyouralliessoyoucanmakebetterdecisionsaboutwhichcharactertoplay.

FloorSpyHacksFloor spy hacks are common in older 2D top-down games that havedifferent distinct floors or platforms. If you’re on the top floor, youmightwanttoknowwhat’sgoingondownstairsbeforeyougochargingin. You can write floor spy hacks that modify the current floor value(typically an unsigned int) to a different floor above or below you,allowingyoutospyonotherfloors.

Gamesoftenrecalculatethecurrentfloorvalueeveryframebasedonplayerposition,soNOPsaresometimesrequiredtokeepthevaluefrombeingreseteverytimeaframeisredrawn.FindingthecurrentfloorvalueandthecodetoNOPwouldbesimilartofindingthezoomfactor,as

discussedin“UsingNOPingZoomhacks”onpage197.

ClosingThoughtsESPhacksarepowerfulwaystoobtainextrainformationaboutagame.SomeofthemcanbedoneprettyeasilythroughDirect3Dhooksorsimplememoryediting.Othersrequireyoutolearnaboutagame’sinternaldatastructuresandhookproprietaryfunctions,givingyouareasontoemployyourreverseengineeringskills.

IfyouwanttoexperimentwithESPhacks,studyandtweaktheexamplecodeforthischapter.ForpracticewithmorespecificESPhacks,Iencourageyoutogooutandfindsomegamestoplayaroundwith.

10RESPONSIVEHACKS

Theaveragegamerhasareactiontimeof250milliseconds,oraquarterofasecond.Professionalgamersaverageafifthofasecond,butsomecanreactinasixthofasecond.Thesefiguresarebasedononlineteststhatmeasureplayers’reactiontimestosingular,predictableevents.Inactualgames,though,playersmustreacttodozensofdifferentevents,likehealthloss,incomingskillshots,abilitiescomingoffofcooldown,enemyattacks,andmanyothers.Onlyveryskilledgamerscanmaintainafourth-orfifth-of-a-secondreactiontimeinsuchdynamicenvironments;theonlywaytobefasteristobeacomputer.

Inthischapter,you’lllearnhowtomakebotsthatreactfasterthananyplayer.First,I’llshowyousomecodepatternsyoucanincorporateintoabottodetectwhencertaineventshappenwithinagame.Next,you’lllearnhowtomakeabotthatmovesyourcharacter,heals,orcastsspellsallonitsown.Onceyou’veexploredthosefundamentaltechniques,I’llhelpyoutiethemtogethertoimplementsomeofthemostcommon,andmostpowerful,responsivehacks.

ObservingGameEventsWithinjustafewsecondsofplayingagame,mostpeoplecanmakeessentialobservationsaboutthegameenvironment.Youcanclearlyseewhenmissilesareflyingtowardyourcharacter,whenyourhealthistoolow,andwhen

areflyingtowardyourcharacter,whenyourhealthistoolow,andwhenabilitiescomeoffofcooldown.Forabot,though,theseseeminglyintuitiveobservationsarenotaseasytomake.Thebotmustdetecteacheventbylookingforchangesinmemory,detectingvisualcues,orinterceptingnetworktraffic.

MonitoringMemoryTodetectsimpleevents,suchasyourhealthbardroppinglow,youcanprogramabottoperiodicallyreadyourhealthfrommemoryandcompareittosomeminimumacceptablevalue,asinListing10-1.

//dothisevery10milliseconds(100timesasecond)autohealth=readMemory<int>(HEALTH_ADDRESS);if(health<=500){//somecodetotellthebothowtoreact}

Listing10-1:Anifstatementthatcheckshealth

Giventheaddressofyourcharacter’shealth,youcancheckthevaluethereasoftenasyouneed;every10millisecondsistypicallyagoodrate.(FlipbacktoChapter1ifyouneedarefresheronlocatingvaluesinmemory.)Oncehealthdropsbelowacertainvalue,you’llwanttorunsomereactioncodetocastahealingspellordrinkapotion.I’lltalkabouthowyoucandothislaterinthechapter.

Ifyouwantyourbottohavemoregranularinformationandthechanceforagreatervarietyofresponses,youcanprogramittoreacttoanychangeinhealth,insteadofonlyafterasetthreshold.Todoso,changethecodeinListing10-1tocompareyourcurrenthealthtotheamountyouhadduringthepreviousexecution,asfollows:

//stilldothisevery10millisecondsstaticintpreviousHealth=0;autohealth=readMemory<int>(HEALTH_ADDRESS);if(health!=previousHealth){if(health>previousHealth){//reacttoincrease}else{//reacttodecrease}previousHealth=health;}

Now,thiscodeusesastaticvariablecalledpreviousHealthtotrackthevalueofhealthonthepreviousiteration.IfpreviousHealthandhealthdiffer,thebotnotonlyreactstothechangeinhealthbutalsoreactsdifferentlytohealthincreasesanddecreases.Thistechniqueisthesimplest,andmostcommon,waytoreacttochangesinagamestate.Withthepropermemoryaddresses,youcanusethiscodepatterntoobservechangesinhealth,mana,abilitycooldowns,andothercriticalinformation.

DetectingVisualCuesHealthisrelativelysimpleforabottocheckbecauseit’sjustanumber,butsomegameelementshavetoberelayedtothebotdifferently.Forexample,whenstatusailmentsorbuffsareaffectingacharacter,theeasiestwayforyoutotellistosimplylookforanonscreenstatusindicator,andthesameistrueforbots.

Whenreadingmemoryisn’tenough,youcandetectcertaineventsbyhookingagame’sgraphicsengineandwaitingforthegametorenderaspecificmodel.(Referbackto“ApplyingJumpHooksandVFHookstoDirect3D”onpage175and“CreatingaDirect3DWallhack”onpage194togetrefreshedonDirect3Dhooks.)Whenthemodelisdrawn,youcanqueueupareactiontobeexecutedaftertheframeisdrawn,likethis:

//belowisthedrawIndexedPrimitivehookvoidonDrawIndexedPrimitive(...){if(numVertices==EVENT_VERT&&primCount==EVENT_PRIM){//react,preferablyafterdrawingisdone}}

Usingthesamemodel-fingerprintingtrickasthewallhackcodeinChapter9,thiscodedetectswhenaspecificmodelisdrawntothescreenandreactsaccordingly.Thiscodereactstotheeventeverysingleframe,though,andthatcanmakeyourgameunplayable.You’llprobablywantsomeinternalcooldowntoavoidspammingareaction.Incaseswheretheindicatormodelispersistentlydrawn(thatis,notblinking),youcanactuallytrackitacrossframestodeterminewhenitappearsanddisappears.

Here’sacodesnippetthatalsohandlestracking:

booleventActive=false;booleventActiveLastFrame=false;

//belowisthedrawIndexedPrimitivehookvoidonDrawIndexedPrimitive(...){if(numVertices==EVENT_VERT&&primCount==EVENT_PRIM)eventActive=true;}

//belowistheendScenehookvoidonDrawFrame(...){if(eventActive){if(!eventActiveLastFrame){//reacttoeventmodelappear}eventActiveLastFrame=true;}else{if(eventActiveLastFrame){//reacttoeventmodeldisappear}eventActiveLastFrame=false;}eventActive=false;}

TheonDrawIndexedPrimitive()functionstillcheckswhetheracertainmodelwasdrawn,butnow,twoBooleanstrackwhetherthemodelwasdrawnthisframeorthepreviousframe.Then,whentheframeiscompletelydrawn,thebotcancheckthesevariablesandreacttothemodeleitherappearingordisappearing.

Thismethodworksgreatfordetectingvisualstatusindicatorsthatappearonlywhenyourcharacterisaffectedbystuns,movementslows,snares,poisons,andsoon.YoucanalsouseittodetectwhenenemiesappearanddisappearinMOBAandRTSgames,asthesegamesdrawonlyenemiesthatareexplicitlyinthesightrangeofanalliedunitorplayer.

InterceptingNetworkTrafficOneofthemostreliablewaystoobserveeventsisthesamewaythegameclientdoes:bywaitingforthegameservertotellyouthattheyoccurred.Inthistypeofcommunication,thegameserversendsbytearrayscalledpacketsoverthenetworktotheclient,usingsockets.Thepacketsaretypicallyencryptedandcontainblobsofdataserializedthroughaproprietaryformat.

ATypicalPacket-ParsingFunctionToreceiveandprocesspackets,agameclientdoessomethinglikeListing

10-2beforeitdrawsaframe.

voidparseNextPacket(){if(!network->packetReady())return;

autopacket=network->getPacket();autodata=packet->decrypt();switch(data->getType()){casePACKET_HEALTH_CHANGE:onHealthChange(data->getMessage());break;casePACKET_MANA_CHANGE:onManaChange(data->getMessage());break;//morecasesformorepackettypes}}

Listing10-2:Asimplifiedlookathowagameparsespackets

Theexactcodeforanyparticulargamemightlookdifferent,butthecontrolflowisalwaysthesame:receiveapacket,decryptit,decidewhatkindofmessageitcontains,andcallafunctionthatknowswhattodowithit.Somegamehackersinterceptrawnetworkpacketsandreplicatethisfunctionalityintheirbots.Thistechniqueworks,butitrequiresextensiveknowledgeofencryption,acompleteunderstandingofhowthegamestoresdatainsideapacket,theabilitytoman-in-the-middlethenetworkconnection,andawaytolocatethedecryptionkeysbeingusedbythegameclient.

Hookingthefunctionsresponsibleforhandlingthepacketsaftertheyaredecryptedandparsedisamuchbetterapproach;inListing10-2,thosefunctionsaretheonHealthChange()andonManaChange()functions.Thismethodleveragesthegame’sinherentabilitytoprocesspackets,allowingabottoremainignorantofthevariousnetworkfacilitiesthegameuses.Italsogivesyoudiscretionoverwhichnetworkdatayouintercept,asyouneedtohookonlythehandlersthatmeetyourneeds.

NOTE

Interceptingentirepacketscansometimesbeadvantageous—forexample,inanygamethatusesAdobeAIRandcommunicatesusingRTMPS.SinceRTMPSissoheavilydocumented,there’snoneedtoreverseengineertheformatorencryption.Chapter8explainshowtohookRTMPSindetail.

Thereareafewtricksyoucanusetoeasilyfindtheparserfunctionand,ultimately,theswitch()statementthatdispatchespacketstotheirhandlers.ThemostusefulmethodI’vefoundistoplaceabreakpointonthefunctionthegameusestoreceivedatafromthenetwork,andthenanalyzetheflowoftheapplicationwhenthebreakpointishit.

Let’swalkthroughhowyoumightdothiswithOllyDbgattachedtoyourtargetgame.InWindows,recv()istheAPIfunctiontoreceivedatafromasocket.FromtheOllyDbgcommandline,youcansetabreakpointonrecv()byenteringthebprecvcommand.Whenthebreakpointishit,youcanclimbthecallstackusingCTRL-F9,theshortcutforexecuteuntilreturn,andF8,theshortcutforstepover.Thiscombinationessentiallyletstheprogramexecuteuntilthecalleehasreturnedtothecaller,allowingyoutoclimbthecallstackintandemwiththegame.Ateachstacklevel,youcaninspectthecodeofeachcalleruntilyoufindonethathasabigswitch()statement;thisshouldbethepacketparser.

ATrickierParserDependingonthegame’sarchitecture,though,findingtheparserfunctionmaynotbethatsimple.Consideragamewithaparserfunctionthatlookslikethis:

packetHandlers[PACKET_HEALTH_CHANGE]=onHealthChange;packetHandlers[PACKET_MANA_CHANGE]=onManaChange;

voidparseNextPacket(){if(!network->packetReady())return;autopacket=network->getPacket();autodata=packet->decrypt();autohandler=packetHandlers[data->getType()];handler->invoke(data->getMessage());}

SincetheparseNextPacket()functiondoesn’thaveaswitch()statement,there’snoobviouswaytoidentifyitinmemory.Unlessyoupayverycloseattention,you’lllikelyclimbrightpastitonthecallstack.Whenagamehasaparserfunctionlikethis,tryingtofigureoutwhattheparserfunctionlookslikemightbepointless.Ifyoudon’tseeaswitch()statementwhenclimbing

therecv()callstack,you’llhavetonoteeverycalleeonthecallstackinstead.Insteadofclimbingupthecallstackfromthebreakpoint,you’dgoto

everyaddressmarkedasaRETURNbelowESPintheOllyDbgstackpane.Thesearethereturnaddressesintoeachcallerforeachcallee.Ateachreturnaddress,you’dneedtofindthetopofthecallerinOllyDbg’sdisassemblypaneandnotetheaddress.Asaresult,you’dhavealistofeveryfunctioncallleadinguptotherecv()call.

Next,you’drepeatthesamelist-makingprocessfrombreakpointsplacedonafewofthegame’shandlerfunctions.Youcanfindahandlerfunctionbymonitoringmemorythatitwillinevitablyuse.Thehandlerforahealthchangepacket,forinstance,willupdateyourhealthinmemory.UsingOllyDbg,youcansetamemoryonwritebreakpointtothehealthaddress.Whenthebreakpointgetstriggered,itmeansthegameupdatedthehealthvaluefromahandlerfunction.Thisshouldworkthesamewayformostvaluesthatarecontrolledbytheserver.Theserverwillcontrolanygame-criticalvalues,suchashealth,mana,level,items,andsoon.

Onceyou’verecordedthecallstackfromrecv()andafewhandlerfunctions,youcancorrelatethemtolocatetheparserfunction.Forexample,considerthethreepseudo–callstacksinTable10-1.

Table10-1:Pseudo–CallStacksforThreePacket-RelatedFunctions

recv()stack onHealthChange()stackonManaChange()stack

0x0BADF00D 0x101E1337 0x14141414

0x40404040 0x50505050 0x60606060

0xDEADBEEF0xDEADBEEF 0xDEADBEEF

0x30303030 0x30303030 0x30303030

0x20202020 0x20202020 0x20202020

0x10101010 0x10101010 0x10101010

Thesestacksshowwhatmemorymightlooklikeduringacalltorecv()andtoagame’shypotheticalonHealthChange()andonManaChange()functions.Noticethateachfunctionoriginatesfromachainoffourcommonfunctioncalls(showninboldface).Thedeepestcommonaddress,0xDEADBEEF,istheaddressoftheparser.Forabetterunderstandingofthisstructure,lookat

thecallstackslaidoutinatreeview,asinFigure10-1.

Figure10-1:Treeviewofourthreecallstacks

Eachfunction’scallstackbranchesoutfromthefunctionat0xDEADBEEF,meaningthatfunctionisacommonpointoforiginforallthreecalls.TheexampleparseNextPacket()functionisresponsibleforcallingthesefunctions,soitmustbethemostrecentcommonancestorat0xDEADBEEF.

NOTE

Thesecallstacksarehypothetical,andthey’resimplifiedbeyondwhatyou’lltypicallyencounter.Realcallstackswillprobablyhavequiteafewmorefunctioncalls,andcomparingthemwon’tbeaseasy.

AHybridParsingSystemAthirdvariationoftheparsingloopmightbeahybridoftheprevioustwothatusesaswitch()statementafterafunctioncall.Here’sanotherhypotheticalfunction:

voidprocessNextPacket(){if(!network->packetReady())return;autopacket=network->getPacket();

autodata=packet->decrypt();dispatchPacket(data);}

voiddispatchPacket(data){switch(data->getType()){casePACKET_HEALTH_CHANGE:processHealthChangePacket(data->getMessage());break;casePACKET_MANA_CHANGE:processManaChangePacket(data->getMessage());break;//morecasesformoredatatypes}}

TheprocessNextPacket()functionfetchesanewpacketandcallsdispatchPacket()tohandlethedata.Inthiscase,thedispatchPacket()functionexistsinthecallstackofeachhandler,butnotintheonefortherecv()function.LookatthehypotheticalstacksinTable10-2,forexample.

Table10-2:Pseudo–CallStacksforThreePacket-RelatedFunctions

recv()stack onHealthChange()stackonManaChange()stack

0x0BADF00D 0x101E1337 0x14141414

0x40404040 0x00ABCDEF 0x00ABCDEF

0xDEADBEEF0xDEADBEEF 0xDEADBEEF

0x30303030 0x30303030 0x30303030

0x20202020 0x20202020 0x20202020

0x10101010 0x10101010 0x10101010

Althoughthesethreefunctionshavethesamefirstfouraddressesintheircallstacks,onlythetwohandlershaveonemoreaddressincommon(againshowninboldface).That’s0x00ABCDEF,andit’stheaddressofthedispatchPacket()function.Onceagain,youcanimaginetheselaidoutinatreeview,asinFigure10-2.

Figure10-2:Treeviewofourthreecallstacks

AParserHackOnceyou’velocatedthefunctionresponsiblefordispatchingpacketstotheirhandlers,you’llbeabletospoteveryhandlerthatcanbecalled.Youcandeduceahandler’spurposebyplacingabreakpointonitandwatchingwhatvalueschangeinmemorywhenitexecutes.Then,youcanhookanyhandlersthatyourbotneedstoreactto.(FlipbacktoChapter8ifyouneedarefresheronhowyoumighthookthesefunctions.)

Ofcourse,thereareendlesswaystoimplementnetworkbehavior.Ican’tcoverthemall,butseeingthesethreecommontechniquesshouldhelpyouunderstandthemethodology.Nomatterwhatgameyou’redealingwith,abreakpointonrecv()shouldbeastepintherightdirection.

PerformingIn-GameActionsBeforeabotcanreacttoevents,youhavetoteachittoplaythegame.Itneedstobeabletocastspells,movearound,andactivateitems.Onthisfront,botsaren’tmuchdifferentfrompeople:theycanjustbetoldwhichbuttonstopress.Pressingbuttonsissimpleandsufficesinmanycases,butinmoreintricatesituations,abotmayhavetocommunicateonthenetworkandtelltheserverwhatit’stryingtodo.

Tofollowalongwiththeexamplesinthissectionandexploreonyourownafterward,openthefilesintheGameHackingExamples/Chapter10_

ResponsiveHacks/folderinthisbook’sresourcefiles.

EmulatingtheKeyboardThemostcommonbuttonsyou’llpressinagamearekeyboardkeys,andthereareacoupleofwaysyoucanteachyourbottotype.

TheSendInput()FunctionOnecommonwaytoemulatethekeyboardiswiththeSendInput()WindowsAPIfunction.Thisfunction,whichsendskeyboardandmouseinputtothetopmostwindow,hasthefollowingprototype:

UINTSendInput(UINTinputCount,LPINPUTinputs,intsize);

Thefirstparameter,inputCount,isthenumberofinputsbeingsent.Fortheexamplesinthisbook,I’llalwaysuseavalueof1.Thesecondparameter,inputs,isapointertoastructure(oranarrayofstructureswhoselengthmatchestheinputCountvalue)withthepredefinedtypeINPUT.Thefinalparameteristhesizeofinputsinmemory,ascalculatedwiththeformulasize=inputCount×sizeof(INPUT).

TheINPUTstructuretellstheSendInput()functionwhattypeofinputtosend,andthefollowingcodeshowshowyoumightinitializeaninstanceofINPUTtopresstheF1key:

INPUTinput={0};input.type=INPUT_KEYBOARD;input.ki.wVk=VK_F1;

TohaveyourbotactuallypressF1,you’dneedtosendthisinputtwice,likeso:

SendInput(1,&input,sizeof(input));//changeinputtokeyupinput.ki.dwFlags|=KEYEVENTF_KEYUP;SendInput(1,&input,sizeof(input));

ThefirstcalltoSendInput()pressesF1,andthesecondreleasesit.Thereleasehappensnotbecausetheinputwassenttwice,butbecausethesecondcallwasmadewiththeKEYEVENTF_KEYUPflagenabledintheinputparameter’skeyboardflagsfield.Sincesettingupinputforevenasinglekeyisabit

messy,it’sbesttowrapeverythinginsideafunction.TheresultlookssomethinglikeListing10-3.

voidsendKeyWithSendInput(WORDkey,boolup){INPUTinput={0};input.type=INPUT_KEYBOARD;input.ki.wVk=key;input.ki.dwFlags=0;

if(up)input.ki.dwFlags|=KEYEVENTF_KEYUP;SendInput(1,&input,sizeof(input));}sendKeyWithSendInput(VK_F1,false);//presssendKeyWithSendInput(VK_F1,true);//release

Listing10-3:AwrapperforemulatingkeystrokeswithSendInput()

Thisfunctioninitializesinputwiththegivenkey,enablestheflagKEYEVENTF_KEYUPifupisset,andcallstheSendInput()function.ThismeanssendKeyWithSendInput()mustbecalledasecondtimetosendthekeyrelease,eventhoughthereleaseisalwaysrequired.ThefunctioniswrittenthiswaybecausekeycombinationsthatinvolvemodifierslikeSHIFT,ALT,orCTRL

mustbesentabitdifferently;themodifier’spressmustcomebeforethekey’spress,butitsreleasemustcomeafterthekey’srelease.

Thefollowingcodeshowshowyou’dusethesendKeyWithSendInput()functiontotellabottopressSHIFT-F1:

sendKeyWithSendInput(VK_LSHIFT,false);//pressshiftsendKeyWithSendInput(VK_F1,false);//pressF1sendKeyWithSendInput(VK_F1,true);//releaseF1sendKeyWithSendInput(VK_LSHIFT,true);//releaseshift

You’dhavetocallsendKeyWithSendInput()fourtimes,butthat’sstilleasierthanusingthecodewithoutawrapperfunction.

TheSendMessage()FunctionAnalternativemethodforsendingkeystrokesreliesontheSendMessage()WindowsAPIfunction.Thisfunctionallowsyoutosendinputtoanywindow,evenifit’sminimizedorhidden,bypostingdatadirectlytothetargetwindow’smessagequeue.Thisadvantagemakesitthemethodof

choiceforgamehackers,becauseitenablesuserstodootherthingswhiletheirbotplaysthegameinthebackground.SendMessage()hasthefollowingprototype:

LRESULTSendMessage(HWNDwindow,UINTmessage,WPARAMwparam,LPARAMlparam);

Thefirstparameter,window,isahandletothewindowthattheinputisbeingsentto.Thesecondparameter,message,isthetypeofinputbeingsent;forkeyboardinput,thisparameterisWM_KEYUP,WM_KEYDOWN,orWM_CHAR.Thethirdparameter,wparam,shouldbethekeycode.Thefinalparameter,lparam,shouldbe0whenthemessageisWM_KEYDOWNand1otherwise.

BeforeyoucanusetheSendMessage()function,youmustobtainahandletothetargetprocess’smainwindow.Giventhetitleofthewindow,youcanobtainahandleusingtheFindWindow()WindowsAPIfunction,asfollows:

autowindow=FindWindowA(NULL,"TitleOfGameWindow");

Withavalidwindowhandle,makingacalltoSendMessage()lookssomethinglikethis:

SendMessageA(window,WM_KEYDOWN,VK_F1,0);SendMessageA(window,WM_KEYUP,VK_F1,0);

ThefirstcallpressestheF1key,andthesecondcallreleasesit.Keepinmind,however,thatthisseriesofcallsworksonlyforkeysthatdon’tinputtext,likeF1,INSERT,orTAB.Tohaveyourbotpresskeysthatinputtext,youmustalsosendaWM_CHARmessagebetweenthedownandupmessages.TotypeW,forinstance,you’ddosomethinglikethis:

DWORDkey=(DWORD)'W';SendMessageA(window,WM_KEYDOWN,key,0);SendMessageA(window,WM_CHAR,key,1);SendMessageA(window,WM_KEYUP,key,1);

Thiscreatesakeyvariablesotheletterkeytopresscanbechangedeasily.ThenitfollowsthesamestepstheF1exampleused,justwithaWM_CHARmessageinbetween.

NOTE

YoucanactuallysendnothingbuttheWM_CHARmessageandgetthesameresult,butit’sbestpracticetosendallthreemessages.GamedeveloperscaneasilyshutdownbotsbypatchingthegametoignoreWM_CHARmessagesthatdon’tfollowWM_KEYDOWN,andtheycanevenuseitasawaytodetectyourbotandbanyou.

AsIshowedwiththeSendInput()technique,youcancreateawrapperaroundthisfunctionalitytomakeyourbotcodeeasiertoworkwith.Thewrapperlookssomethinglikethis:

voidsendKeyWithSendMessage(HWNDwindow,WORDkey,charletter){SendMessageA(window,WM_KEYDOWN,key,0);if(letter!=0)SendMessageA(window,WM_CHAR,letter,1);SendMessageA(window,WM_KEYUP,key,1);}

UnlikeListing10-3,thiswrapperactuallysendsboththepressandrelease.ThisisbecauseSendMessage()can’tbeusedtosendkeystrokeswithmodifiers,sothere’sneveranyneedtoinsertcodebetweenthetwocalls.

NOTE

Therearemultiplewaysagamemightcheckwhetheramodifierkeyispressed,though.YoumightbeabletosendmodifierkeystocertaingamesbycallingtheSendMessage()function,butitdependsonhowthosegamesdetectmodifiers.

YoucanusethiswrapperinasimilarwayastheoneinListing10-3.Forexample,thiscodesendsF1followedbyW:

sendKeyWithSendMessage(window,VK_F1,0);sendKeyWithSendMessage(window,'W','W');

Thisexample,likealloftheSendMessage()codeI’veshownsofar,simplygetsthejobdone.Itcaninputtext,butitdoesn’texactlysendpropermessages.

Therearealotofsmalldetailsyouhavetogetrightifyouwanttosend100percentvalidmessageswiththeSendMessage()function.Forinstance,

thefirst16bitsoflparamshouldstorethenumberoftimesthekeyhasbeenautomaticallyrepeatedasaresultofbeinghelddown.Thenext8bitsshouldstorethescancode,akeyidentifierthatisspecifictoeachkeyboardmanufacturer.Thenextbit,number24,shouldbesetonlyifthebuttonisonanextendedpartofthekeyboard,suchasthenumberpad.Thefollowing4bitsareundocumented,andthenextbitshouldbesetonlyiftheALTkeywasdownwhenthemessageoriginated.Thelast2bitsarethepreviousstateflagandthetransitionstateflag.Thepreviousstateflagissetonlyifthekeywaspreviouslydown,andthetransitionstateissetonlyifthekeywaspreviouslyinthestateoppositeitscurrentposition(thatis,ifthekeyisnowupandwaspreviouslydown,orviceversa).

Thankfully,theaveragegamedoesn’tconsidermostofthesevalues.Forthatmatter,theaveragepieceofsoftwaredoesn’tcareaboutthemeither.Ifyouhavetofillallofthesevalueswithproperdatatomakeyourbotwork,you’removinginthewrongdirection.Therearemanyotherwaystoperformactions,themajorityofwhicharesimplerthantryingtoemulatetheexactbehavioroftheoperatingsystem’skernel-levelkeyboardinputhandler/dispatcher.Infact,there’salreadyafunctionthatdoesthat,andI’vealreadytalkedaboutit:theSendInput()function.

YoucanalsocontrolthemousewiththeSendInput()andSendMessage()functions,butIhighlyrecommendavoidingit.Anymousecommandsyousendwillaffect,andbeaffectedby,anylegitimatemousemovements,mouseclicks,orkeystrokessentbytheplayer.Thesameistrueforkeyboardinput,butthecomplicationsaremuchrarer.

SendingPacketsBeforeagamedrawsaframe,itchecksforkeyboardandmouseinput.Whenitreceivesinputthatresultsinanaction,suchasmovingaroundorcastingaspell,itcheckstomakesuretheactionispossibleand,ifso,tellsthegameserverthattheactionhasbeenperformed.Thegamecodetocheckforeventsandalerttheserveroftenlookssomethinglikethis:

voidprocessInput(){do{autoinput=getNextInput();if(input.isKeyboard())processKeyboardInput(input);//handleotherinputtypes(e.g.,mouse)

}while(!input.isEmpty());}voidprocessKeyboardInput(input){if(input.isKeyPress()){if(input.getKey()=='W')step(FORWARD);elseif(input.getKey()=='A')step(BACKWARD);//handleotherkeystrokes(e.g.,'S'and'D')}}voidstep(intdirection){if(!map->canWalkOn(player->position))return;playerMovePacketpacket(direction);network->send(packet);}

TheprocessInput()functioniscalledeveryframe.Thefunctioniteratesoverallpendinginputsanddispatchesdifferenttypesofinputstotheirrelevanthandlers.Inthiscase,whenkeyboardinputisreceived,it’sdispatchedtotheprocessKeyboardInput()function.ThishandlerthencheckswhetherthekeyiseitherWorS,and,ifso,callsstep()tomovetheplayerinthecorrespondingdirection.

Sincestep()isusedtoperformanaction,itiscalledanactorfunction.Theinvocationofanactorfunctioniscalledactuation.Youcandirectlycallagame’sactorfunctionsfromyourbottoperformanactionwhilecompletelybypassingtheinputlayer.

Beforeyoucancallanactor,though,youmustfinditsaddress.Todothis,youcanattachOllyDbgtothegame,openthecommandline,andenterbpsend.Thiswillplaceabreakpointonthesend()function,whichisusedtosenddataoverthenetwork.Whenyouplaythegame,everytimeyoutakeastep,castaspell,pickuploot,ordoanythingelse,yourbreakpointshouldtrigger,andyoucannoteeachfunctioninthecallstack.

NOTE

Thegameshouldcallsend()everytimeyoudoanythingwhileplaying.Payattentiontowhatyoudidbeforeeachsend()breakpointishit,asthatwillgiveyouaroughideaofwhatactioneachcalliscommunicatingtotheserver,and,ultimately,whattheactoryoufindisresponsiblefor.

Onceyouhaveafewdifferentcallstacks,youcancomparethemtolocatetheactorfunctions.Toseehowtospottheactorfunctions,let’scomparethetwoannotatedcallstacksinFigure10-3.

Figure10-3:Treeviewofcallstackstotwoactorfunctions

Likethesetwostacks,thecallstacksyoufindshouldbeidenticalatthetop,sharingacoupleofcommonfunctionsresponsibleforgenericnetworktransmission.Theyshouldalsobeidenticalonthebottom,sinceeachcalltosend()shouldhaveoriginatedfromtheprocessInput()function.Eachstackshouldhavesomeuniquefunctionsbetweentheseidenticalregions,though,andthosearetheactorfunctionsyou’relookingfor.Typically,thefunctionofinterestisimmediatelybeneaththecommonnetworkcalls.Inthiscase,thetwoactorsarethestep()andcastSpell()functions.

Afterhackingthesamegameforawhile,you’lllearnhowfarupthestacktheactorfunctionsarefromthesend()call.InFigure10-3,forexample,theactorshappenthreecallsbeforethesend()call.Knowingthis,youcouldjustclimbthestackinOllyDbg(CTRL-F9followedbyF8)threetimeswhenyoursend()breakpointishitandbeinsidetheactorfunctionthatsentthedata.

Onceyou’vefoundanactorfunction,youcancallitfromaninjectedDLL.Here’showyoumightcallstep()ifyoufounditat0xDEADBEEF:

typedefvoid_step(intdirection);autostepActor=(_step*)0xDEADBEEF;

autostepActor=(_step*)0xDEADBEEF;

stepActor(FORWARD);

Sincethebotwon’tknowtheactualnameforthisgamefunction,thecodeassignsthecontentsofmemoryat0xDEADBEEFtoaconvenientlynamedvariable:stepActor.Then,thecodejustcallsstepActor()likeanyotherfunction.

Ifyou’vegottherightaddress,functionprototype,andparameters,thisshouldworkbeautifully;you’llbeabletoautomateactionsasifyouhaveaccesstothegame’ssourcecode.Justmakesuretocalltheactorfunctionsfrominsidethesamethreadasthegame,oryoucanrunintothreadingissues.ThebestwaytodothisistocalltheactorsfromahookonamajorfunctionlikeDirect3D’sEndScene()ortheWindowsAPI’sPeekMessage()function,asthesefunctionswillusuallybecalledonlyfromthegame’smainthread.

USINGTHISTOCALL__THISCALLIfyoutrytocallanactorfunctionthat’sanonstaticmemberofaclass,thefunctionwillhavea_thiscallcallingconvention,whichmeansyou’llneedtopasstheinstanceoftheclassontheECXregister.(Youcanbrushuponcallingconventionsin“FunctionCalls”onpage94.)Passingtheinstanceisstraightforward,butyou’llhavetolocateapointerchaintotheclassinstancefirst.

Tofindthepointerchain,youcandropabreakpointontheactorfunction,grabtheclassinstancevaluefromECXwhenthebreakpointkicks,andthrowthatvalueintoaCheatEnginepointerscan.Then,tocallthefunction,you’dwalkthepointerchain,obtainthecurrentinstanceaddress,anduseinlineassemblytosetupECXandmaketheactualfunctioncall.ThisprocessworkssimilarlytothewayVFhookcallbackscalltheiroriginalcounterparts,asshownin“WritingaVFTableHook”onpage156.

TyingthePiecesTogetherAfteryou’vecreatedframeworksforobservingeventsandperformingactions,youcantiethemtogethertocreateresponsivehacks.Responsivehackscomeinmanyflavors,butthereareafewcommonones.

MakingthePerfectHealerAfavoritebotamonggamersisautohealing,ahackthatautomaticallyusesahealingspellwhentheplayer’shealthdecreasesdrasticallyordropsbelowacertainthreshold.Givenawaytodetectchangesinhealthandanactorfunctiontocastspells,anautohealermightlooksomethinglikethis:

voidonHealthDecrease(inthealth,intdelta){if(health<=500)//healthbelow500castHealing();elseif(delta>=400)//largedropinhealthcastHealing();}

Thisautohealingfunctionisprettysimple,butitworkswell.Moreadvancedautohealersmighthavemanymorelevelsofhealingandbeabletolearnastheygo.You’llgetworkingexamplecodeandanin-depthexplanationofadvancedautohealersin“ControlTheoryandGameHacking”onpage222.

ResistingEnemyCrowd-ControlAttacksAnti-crowd-controlhacksdetectincomingcrowd-controlattacksandautomaticallycastspellsthatreducetheireffectsorcompletelynegatethem.Crowd-controlattacksdisableplayersinsomeway,sohavingenemiescastthemonyoucanbeapain.

Givenawaytodetectincomingoractivecrowd-controleffects,suchasbydetectingaDirect3Dmodelorbyinterceptinganincomingpacket,andanactorfunctiontocastspells,youcouldhaveabotreactinstantlytosuchattackslikeso:

voidonIncomingCrowdControl(){//castashieldtoblockthecrowdcontrolcastSpellShield();

}voidonReceiveCrowdControl(){//cleansecrowdcontrolthathasalreadytakeneffectcastCleanse();}

AnonIncomingCrowdControl()functionmighttrytostopthecrowd-controlspellfromeverhittingyou.Failingthat,thebotcouldcallanonReceiveCrowdControl()spelltoremovetheeffects.

AvoidingWastedManaSpelltrainersarealsoquitecommonamongbotters.Spelltrainerswaituntiltheplayerhasfullmanaandthencastspellstoincreasetheplayer’smagiclevelorstats.Thisallowsplayerstoquicklyincreasetheirmagicskills,astheywillneverwastemanaregenerationjustbecausetheyhavefullmana.

Givenawaytodetectchangesinmanaandanactorfunctiontocastspells,abotmightincludethefollowingpseudocodeforaspelltrainer:

voidonManaIncrease(intmana,intdelta){if(delta>=100)//playerisusingmanapotions,return;//theymustneedthemana,abortif(mana>=MAX_MANA-10)//manaisnearlyfull,wastesomecastManaWasteSpell();}

Thisfunctiontakestheplayer’smanaandtheincreaseinthatplayer’smana(delta)asparameters.Iftheincreaseinmanaisaboveacertainamount,itassumestheplayerisusingpotionsorotheritemstoreplenishmana,anditwon’tcastanyextraspells.Otherwise,iftheplayerhasplentyofmana,thefunctionfiresoffanyoldspelltogettheplayersomeexperiencepoints.

Othercommonresponsivehacksareautoreloadtoinstantlyreloadammo,autododgetoevadeincomingprojectiles,andautocombotoinstantlyattackthesametargetasanearbyally.Really,theonlylimittothenumberofresponsivehacksyoucanaddtoabotisthenumberofeventsyourbotcanobserveinthegame,multipliedbythenumberofvalidandhelpfulresponsesitcansendforeachevent.

ClosingThoughtsUsinghooks,memorymanipulation,andkeyboardsimulation,youcanbegincreatingyourfirstresponsivehacks.Thesehacksareyourentrypointintogamingautonomy,butthey’reonlyaglimpseofwhat’spossible.Chapter11willbethepinnacleofyourgame-hackingadventure.Usingeverythingyou’velearnedsofar,andbuildingontheprinciplesofresponsivehacks,you’lllearnhowtoautomateadvancedactionsandcreateatrulyautonomousbot.

Ifyou’renotfeelingquitereadytogodeeper,Istronglyrecommendreviewingtheearliermaterialandthengettingsomepracticeinanisolatedenvironmentonyourownmachine.Implementingbotslikethisisaloteasierthanyoumightthink,andit’sanamazinglysatisfyingexperience.Onceyou’recomfortablemakingautohealersandotherbasicresponsivehacks,you’llbereadytostartcompletelyautomatinggameplay.

11PUTTINGITALLTOGETHER:WRITING

AUTONOMOUSBOTS

Theendgoalofgamehackingistomakeafull-fledgedautomatedbotcapableofplayingagameforhoursonend.Suchbotscanheal,drinkpotions,farmmonsters,lootcorpses,walkaround,sellloot,buysupplies,andmore.Makingbotsthispowerfulrequiresyoutocombineyourhooksandmemoryreadswithconceptslikecontroltheory,statemachines,andsearchalgorithms,whichareallcoveredinthischapter.

Throughoutthelessonshere,you’llalsolearnaboutcommonautomatedhacksandhowtheyshouldbehaveatahighlevel.Aftercoveringthetheoryandcodebehindautomatedhacks,I’llgiveyouahigh-levellookattwotypesofbotsthatrelyonsuchcode:cavebots,whichcanexplorecavesandbringhometheloot,andwarbots,whichcanfightenemiesforyou.Bytheendofthechapter,youshouldbereadytobustoutyourtools,fireupyourdevelopmentenvironment,andstartmakingsomereallyawesomebots.

ControlTheoryandGameHackingControltheoryisabranchofengineeringthatprovidesawaytocontrolthebehaviorofdynamicsystems.Controltheorydeterminesthestateofasystemusingsensors,afterwhichacontrollerdeterminesthesetofactionsneededto

bringthesystem’scurrentstatetosomeotherdesiredstate.Afterthecontrollerexecutesthefirstactionintheset,theentireprocess—knownasafeedbackloop—repeats(seeFigure11-1).

Figure11-1:Acontroltheoryfeedbackloop

Let’sapplythisfeedbacklooptogamehacking.Toautomateplaywithinagame(thesystem),abotimplementssomealgorithms(thecontroller)thatunderstandhowtoplaythegameinanystateobservedbythememoryreads,networkhooks,andsoon(thesensors).Thecontrollertypicallyhassomehumaninputs,likethepathtowalk,creaturestoattack,andloottopickup.Thus,toreachthedesiredstate,thecontrollermustperformsomesubsetoftheseinputsthatarepossiblegiventhecurrentstate.

Forinstance,iftherearenocreaturesonscreenandnocorpsestoloot,thedesiredstatemaybefortheplayertoreachthenextlocation(calledawaypoint)inthepredefinedpath.Inthiscase,thecontrollermovestheplayeronestepclosertothewaypointoneachiteration.Iftheplayerencountersacreature,thecontrollermightdecidetoattackthecreatureinthefirstframeand,inthefollowingframes,switchbetweenrunningfromthecreature(knownaskiting)andshootingspellsatit.Oncethecreaturedies,thecontrollerexecutesasetofactionstolootthebodyandcontinuetothenextwaypoint.

Giventhisexampleofhowafeedbackloopmightoperate,itmightseemoverwhelmingtocodesuchasystem.Luckily,thereareafewdesignpatternsthatmakethetaskmucheasierthanitsounds.

StateMachinesStatemachinesaremathematicalmodelsofcomputationthatdescribehowasystembehavesbasedoninput.Figure11-2showsasimplestatemachine

thatreadsalistofbinarydigits.ThemachinestartswithaninitialstateofS1.Asititeratesoverthedigitsintheinput,itchangesitsstateaccordingly.Inthiscase,statesS1andS2repeatthemselveswhenthemachineencountersa1andactivateoneanotherwhenitencountersa0.Forexample,forthebinarydigits11000111,thestatetransitionswouldbeS1,S1,S2,S1,S2,S2,S2,andfinallyS2.

Figure11-2:Asimplestatemachine

Withasmallspinontheclassicalstatemachinetheory,astatemachinecanbethecontrollerinacontroltheoryfeedbackloop.Thistweakedversionofastatemachinecomprisesalistofstates,theconditionssignifyingeachstate,andtheactionsthatmusthappentoreacheachstate.

STATEMACHINESANDGAMEHACKINGAgame-hackingstatemachinenotonlymustkeepaninternalstatebutalsomustrespondto(oractuate)thegameenvironmentbasedonthatstate.Theoverallgamestatecanchangebasedonyourbot’sactuation,thebehaviorofotherplayers,andotherunpredictableoccurrencesinthegameenvironment.Forthisreason,tryingtopersistentlywalkastatemachinebasedontheobservedgameenvironmentisfutile;it’snearlyimpossibletocreateasetoftransitionsforeachstatetoaccountforeverypossibleobservationthatcanbemadebetweeniterations.Itmakesmoresenseforthestatemachinetoreevaluatethegameenvironmentasafreshslateeachtimeitconsiderstheinput.Todothis,thestatemachinemustusethegameenvironmentitselfasthemechanismfortransitioningbetween

states—thatis,themachine’sactuationontheenvironmentshouldhaveenoughofaneffectonthenextiterationsthatitactivatesanewstate.Classicalstatemachinescanbedevisedthatarecapableofworkinglikethis,butwe’regoingtoflattenthemoutandusetheminamuchsimpler,yetstillverypowerful,way.

Ifyou’refamiliarwithclassicalstatemachines,thismaynotseemintuitive,butinthecomingsectionsyou’llseehowstatemachinescanbemutatedandpairedwithcontroltheorytoachievewhatwewant.

Themajordifferenceisthatinsteadofonestatemerelyactivatinganother,foreachstateinagameautomationstatemachine,abotwillperformin-gameactionsthatchangetheoverallstateofthegameand,thus,thestatethatisdetectedonthenextiterationofthefeedbackloop.Incode,anobjecttorepresentastateinthismachinemightlooklikethis:

classStateDefinition{public:StateDefinition(){}~StateDefinition(){}boolcondition();voidreach();};

YoucanassembleStateDefinitionobjectsintoastatemachinewithasimplestd::vectordefinition,likethis:

std::vector<StateDefinition>stateMachine;

Andpresto,youhavetheskeletonofastatemachine,readytoreceiveanyStateDefinitionobjectsyoucreate.Inconjunctionwithafeedbackloop,thisstatemachinecanbeusedtodefinetheflowofautomation.

First,youcancreatealistofdefinitionsthatmodelyourbot’sdesiredbehavior,orderedinthevectorbyimportance.EachStateDefinitionobjectcanuseinformationfromyoursensorsasinput,passingthatdatatothecondition()functiontodeterminewhetherornotthestateshouldbeactivated.Then,youcancreateacontrollerthatloopsoverthelistofstates,callingthereach()functionofthefirststatewhosecondition()functionreturnsfalse.Finally,youcanwrapthecontrollerinafeedbackloop.Ifyoudon’tseehowthisfeedbackloopwouldworkyet,don’tworry;I’llshowyou

howtocodeitnow.

NOTE

Youcanthinkofthestatementinyourcondition()functionasarequirementforthemachinetotransitiontothenextstate.Ifthestatementistrue,itmeansnoactuationmusthappenbeforethenextstateinthelistcanbeevaluatedandtheloopcancontinueiterating.Ifthestatementisfalse,itmeanssomeactuatormustoccurbeforethetransitioncanhappen.

You’llfindalloftheexamplecodeforthefollowingsectionand“ErrorCorrection”onpage230intheGameHackingExamples/Chapter11_StateMachinesdirectoryofthisbook’ssourcefiles.TheincludedprojectscanbecompiledwithVisualStudio2010,buttheyshouldalsoworkwithanyotherC++compiler.Downloadthemathttps://www.nostarch.com/gamehacking/andcompilethemifyouwanttofollowalong.

CombiningControlTheoryandStateMachinesTotiestatestogetherwithafeedbackloop,firstyouhavetoprovideeachStateDefinitionobjectwithagenericwaytoaccessthesensorsandactuatorsthatyou’veimplemented.TheStateDefinitionclassthenbecomesthefollowing:

classStateDefinition{public:StateDefinition(){}~StateDefinition(){}boolcondition(GameSensors*sensors);voidreach(GameSensors*sensors,GameActuators*actuators);};

Thischangesimplymodifiesthecondition()andreach()functionstoacceptinstancesoftheclassesGameSensorsandGameActuatorsasarguments.GameSensorsandGameActuatorsareclassesyouneedtodefine;GameSensorswillcontaintheresultsofmemoryreads,networkhooks,andotherdatasourcesyourbotinterceptsfromthegame,whileGameActuatorswillbea

collectionofactorfunctionscapableofperformingactionsinsidethegame.Next,youneedagenericwaytodefineeachindividualstate.Youcould

abstractthedefinitionofeachstatetoitsownclassthatinheritsStateDefinitionandimplementscondition()andreach()asvirtualfunctions.Alternatively,ifthesourcecodeneedstofitinasmallspace(likeabook,winkwink),youcouldkeepasingleclasstorepresenteachdefinitionandusestd::functiontoimplementthecondition()andreach()functionsoutsidetheclassdefinition.

Followingthatalternativemethod,thefinalversionofStateDefinitionwouldlooklikethis:

classStateDefinition{public:StateDefinition(){}~StateDefinition(){}std::function<bool(GameSensors*)>condition;std::function<void(GameSensors*,GameActuators*)>reach;};

WiththisversionoftheStateDefinitionclass,youcoulddefineanewstatebycreatinganinstanceoftheclassandassigningcondition()andreach()tofunctionsthatcorrespondwiththeintendedbehavior.

ABasicHealerStateMachineThenextstepisdefiningthebot’sactualbehavior.Tokeeptheexamplecodesimple,let’ssayyou’reimplementinganautomatichealer.Thishealerhastwohealingmethods:itusesstronghealingiftheplayerisatorbelow50percenthealthandweakhealingiftheplayerisbetween51and70percenthealth.

Astatemachinerepresentingthisbehaviorneedstwostates,oneforstronghealingandoneforweakhealing.Tostart,youneedtodefinethestatemachineasavectorwithtwoStateDefinitionobjects:

std::vector<StateDefinition>stateMachine(2);

ThiscodecreatesastatemachinecalledstateMachineandinitializesitwithtwoemptyStateDefinitionobjects.Next,youdefinethecondition()andreach()functionsforthesestatedefinitions.Thestronghealingstateisthemostimportantbecauseitkeepsthecharacterfromdying,soitshould

comefirstinthevector,asshowninListing11-1.

autocurDef=stateMachine.begin();curDef->condition=[](GameSensors*sensors){➊returnsensors->getHealthPercent()>50;};curDef->reach=[](GameSensors*sensors,GameActuators*actuators){➋actuators->strongHeal();};

Listing11-1:Codeforastronghealingstate

ThiscodefirstcreatesaniteratorcalledcurDefthatpointstothefirstStateDefinitionobjectinthestateMachinevector.Theobject’scondition()functionisthendefined➊;inEnglish,thisdefinitionsays,“Thestateismetiftheplayer’shealthpercentisgreaterthan50.”Ifthestateisn’tmet,thentheobject’sreach()functioncallsthestrongHeal()actorfunction➋sothatstronghealingcanbeperformed.

Withthestronghealingstatedefined,nextyoudefinetheweakhealingstate,asshowninListing11-2.

curDef++;curDef->condition=[](GameSensors*sensors){➊returnsensors->getHealthPercent()>70;};curDef->reach=[](GameSensors*sensors,GameActuators*actuators){➋actuators->weakHeal();};

Listing11-2:Codeforweakhealing

AfterincrementingcurDefsoitpointstothesecondStateDefinitionobjectinthestateMachinevector,thiscodedefinestheobject’scondition()function➊as,“Thestateismetiftheplayer’shealthpercentisgreaterthan70.”Italsodefinestheobject’sreach()functionasanactuators->weakHeal()call➋.

Onceyou’vefinisheddefiningthestatemachine,youmustimplementthecontroller.Sincetheactualbehaviorofthecontrolleriscontainedinthestatemachine,youonlyneedtoaddasimplelooptocompleteit:

for(autostate=stateMachine.begin();state!=stateMachine.end();state++){

if(➊!state->condition(&sensors)){state->reach(&sensors,&actuators);break;}}

Thiscontrollerloopiteratesoverthestatemachine,executesthereach()functionofthefirststatewhosecondition()functionreturnsfalse➊,andbreaksoutifanyreach()functioniscalled.Thefinalstepistoimplementthefeedbackloopandplopthecontrollerloopinsideit,asshowninListing11-3.

while(true){for(autostate=stateMachine.begin();state!=stateMachine.end();state++){if(!state->condition(&sensors)){state->reach(&sensors,&actuators);break;}Sleep(FEEDBACK_LOOP_TIMEOUT);}

Listing11-3:Finalhealingstatemachineandfeedbackloop

ThisloopcontinuouslyexecutesthecontrollerloopandsleepsforFEEDBACK_LOOP_TIMEOUTmillisecondsbetweeneachexecution.TheSleep()callallowsthegameservertoreceiveandprocessanyactuationfromthepreviousiterationandallowsthegameclienttoreceiveanyresultsoftheactuationfromtheserverbeforeexecutingthenextcontrollerloop.

Ifyou’restillabitconfusedaboutwhatIjustshowedyou,checkoutFigure11-3,whichshowshowtheinfinitelyloopingcodeinListing11-3works.First,itcheckswhetherthestronghealingconditionistrue,andifitis,theweakhealingconditionischecked.Ifthestronghealingconditionisfalse,thentheplayer’shealthmustbeatorbelow50percent,soastronghealingmethodgetscalled.Iftheweakhealingconditioncheckisfalse,thentheplayer’shealthmustbebetween51and70percent,sotheweakhealingmethodisexecuted.

Figure11-3:Flowchartofthehealingstatemachineandfeedbackloop

Aftereithermethod,themachinesleeps.Ifbothconditionchecksaretrue,thentheplayerneedsnohealing.Themachinedoesnothingtochangethestateandsleepsbeforestartingagainatthetopofthewhileloop.

AComplexHypotheticalStateMachineThebehaviorimplementedinthehealingstatemachineissimple,sorollingitintothiskindofcontrolstructuremayseemlikeoverkill,butit’susefulifyouwanttoexpandthecontroller.If,forexample,youwantedtocombinethehealingstatemachinewiththe“walk,attack,loot”behaviorthatI

discussedin“ControlTheoryandGameHacking”onpage222,thecontrolstructurewouldbemuchmorecomplex.Let’stakeahigh-levellookatthestatesyou’dneed:

StronghealingConditionmetifhealthisover50percent.Reachbycastingstronghealingspell.

WeakhealingConditionmetifhealthisover70percent.Reachbycastingweakhealingspell.

AttackspellConditionmetifnotargetisavailableorifattackspellisoncooldown.Reachbycastingattackspellontarget.

KitemonsterConditionmetifnotargetisavailableorifdistancefromtargetisadequate.(Thedefinitionof“adequate”dependsonhowfarawayyouwanttobefromenemieswhenkiting.)Reachbytakingastepawayfromtarget.

TargetmonsterConditionmetifthere’snocreaturetoattack.Reachbyattackingacreature.

LootitemConditionmetifthere’snocorpseopenorifopencorpsehasnothingtoloot.Reachbytakinganitemfromopencorpse.

ApproachcorpseConditionmetiftherearenocorpsestoopenorifadjacenttoacorpse.Reachbytakingasteptowardacorpsethatwillbeopened.

OpencorpseConditionmetifthecharacterisnotadjacenttoacorpsethatcanbeopened.Reachbyopeningadjacentcorpse.

FollowpathConditionmetifthecharacterisunabletomovetocurrentwaypointorifstandingoncurrentwaypoint.Reachbytakingasteptowardcurrentwaypoint.

AdvancewaypointConditionmetiftherearenowaypointslefttofollow.Reachbyupdatingthecurrentwaypointtothenextwaypointinthelist.Ifthecharactercan’treachthecurrentwaypointforsomereason(say,ifthecharacterisstuck),thentheAdvanceWaypointstatekeepsitfrombeingstuck.Ifthecharacterhasreachedthecurrentwaypoint,AdvanceWaypointselectsthenextwaypointtokeepthingsmovingalong.

Thisstatemachineisquiteabitmorecomplexthanthehealing-onlystatemachine.IfIdiagrammedthisstatemachine,therewouldbe23objectsinthediagram,witharrowsgoingover33controlpaths.ComparethattoFigure11-3,whichhasonly7objectsand9controlpaths.

Youcouldcodethehealerbehaviorwithoutusingastatemachineorfeedbackloop,butIcan’timaginehowtoeasilydothesameforthisfull-fledgedbot.Eachofthese10statesreliesonnotonlyitsownconditionbutalsotheconditionofeverystateprecedingit.Moreover,hardcodingthelogicwouldeitherrequireatonofnestedif()statementsorabunchofstackedif()/return()statements—and,eitherway,itwouldjustbehaveexactlylikethestatemachinebutwithnoruntimeflexibility.

Runtimeflexibilityreferstoastatemachine’sabilitytomutate.Unlikehardcodedconditionchecks,statedefinitionsinastatemachinecanbemoved,removed,andaddeddynamically.Thestatemachinemethodallowsyoutoplugandplaydifferentbehaviorsandfeaturesdependingonuserinput.

Totakethisconceptastepfurther,youcouldexposeyoursensorsandactuatorstoaLuaenvironment,createLuafunctionscapableofaddingandremovingstatesfromthestatemachine,andmodifytheStateDefinitionsothatitscondition()andreach()functionscancallLuafunctionsexposedbytheLuaenvironment.Writingacontrolsystemthiswaywouldallowyoutocodethecoreofyourbot(hooks,memoryreading,actuation)inC++whilemakingLua(ahigh-level,dynamiclanguage)availabletoyouforautomation.

NOTE

YoucanembedLuainyourownprogramsbyincludingafewheadersandlinkingagainsttheLualibrary.Thisprocessisnotdifficult,butit’soutsidethescopeofthisbook,soIencourageyoutocheckoutChapter24ofProgramminginLuabyRobertoIerusalimschy(http://www.lua.org/pil/24.html)formoreinformation.

ErrorCorrectionAnotherpieceofcontroltheorythat’susefulforgamehackingiserror

correction.Anerrorcorrectionmechanisminacontrollerobservestheoutcomeofactuation,comparestheoutcometoanexpectedresult,andadjustsfuturecalculationstobringlateroutcomesclosertotheexpectedone.Errorcorrectioncancomeinhandywhenyou’reworkingwithstochasticsystems,wheretheoutputgeneratedfromagiveninputisnotfullypredictable.

Gamesasawholearestochastic,but,luckilyforgamehackers,theresultsofactionsaremostlydeterministic.Takethehealingcontroller,forexample.Inmostgames,youcancalculateexactlyhowmuchhealthyoucanhealwithagivenspell,and,thus,youknowexactlywhentoheal.Butimagineyou’rewritingahealerforthesmallspectrumofsituationswhereyourhealingisimpossibletocalculate;forinstance,maybethebotissupposedtoworkonavarietyofcharactersspanningmanylevelswithoutuserinput.

Errorcorrectioncouldenableyourbottolearnhowtobesthealtheplayers.Inthisscenario,therearetwowaysyoucanimplementerrorcorrection,eachofwhichdependsonhowthehealingsystemworks.

AdjustingforaConstantRatioIfyouhealforaconstantratioofhealth,you’llonlyneedtoadjustyourcontrollerafterthefirstheal.Assumingthatyoursensorscandetecthowmuchyou’vehealed,thisaddsonlyafewlinesofcode.YoucouldeasilymodifytheweakhealingstateinListing11-2tosomethinglikethis:

curDef->condition=[](GameSensors*sensors)->bool{staticfloathealAt=70;staticboolhasLearned=false;if(!hasLearned&&sensors->detectedWeakHeal()){hasLearned=true;healAt=100-sensors->getWeakHealIncrease();}returnsensors->getHealthPercent()>healAt;};

Insteadofhardcoding70asthethresholdforweakhealing,thiscodemovesthethresholdtoastaticvariablecalledhealAt.ItalsoaddsanotherstaticvariablecalledhasLearnedsothatthecodeknowswhenlearningiscomplete.

Oneachinvocationofthiscondition()function,thecodecheckstwoconditions:whetherhasLearnedisfalseandwhetherthesensorsdetecteda

weakhealingevent.Whenthischeckpasses,thecodesetshasLearnedtotrueandupdateshealAttohealatorbelowtheperfectpercentage;thatis,ifyourweakhealingmusteredupa20percentincreaseinhealth,healAtwouldbesetto80percenthealthinsteadof70percent,soeachhealwouldbringtheplayerbackupto100percenthealth.

ImplementingAdaptableErrorCorrectionButwhatifyourhealingpowerincreases?Ifacharactercangainlevels,applyskillpoints,orincreasemaximumhealth,theamountofhealthitcanhealmaychangeaccordingly.Forexample,ifyoustartabotonalevel-10characterandletitrununtilthecharacterislevel40,yourhealingcodewillneedtoadapt.Alevel-40characterhealinglikeitdidatlevel10wouldeitherimmenselyoverhealordiequicklyagainston-levelgameenemies.

Tohandlethisscenario,abotneedstoconstantlyupdateitshealingthresholdtoreflecttheobservedhealingamount.Listing11-4showshowyoucanmodifythestronghealingconditionfunctioninListing11-1todothis.

curDef->condition=[](GameSensors*sensors)->bool{staticfloathealAt=50;➊if(sensors->detectedStrongHeal()){autonewHealAt=100-sensors->getStrongHealIncrease();➋healAt=(healAt+newHealAt)/2.00f;➌sensors->clearStrongHealInfo();}returnsensors->getHealthPercent()>healAt;};

Listing11-4:Tweakingthestronghealingconditioncode

Asinthemodifiedweakhealingfunction,thehealingthresholdhasbeenmovedtoastaticvariablecalledhealAt,butthistime,thelogicisabitdifferent.Sincelearningmusthappencontinually,there’snovariabletotrackwhetherthebothasalreadylearneditstruehealingcapacity.Instead,thecodejustcheckswhetherthesensorshaveseenastronghealingeventsinceitslastinvocation➊.Ifso,thecodereplaceshealAtwiththeaverageofhealAtandnewHealAtandcallsafunctiontoclearthesensorsofinformationrelatedtostronghealing➌.

Clearingthesensorsisactuallyveryimportant,becauseitkeepsthecode

fromconstantlyupdatinghealAtagainstfeedbackfromthesamestronghealingcast.Notice,too,thatthisfunctiondoesn’tupdatehealAttoaperfectvaluebutinsteadslidesittowardtheobservedoptimalvalue.Thisbehaviormakesthenewfunctionidealforsituationswherethereissomeamountofrandomnessinhowmuchyoucanactuallyheal.Ifyourbotneedstoslidetowardthenewvaluefaster,youmightchangethelineat➋tosomethinglikethis:

healAt=(healAt+newHealAt*2)/3.00f;

ThiscodetoupdatehealAtusesanaverageweightedtowardthenewHealAtvalue.Thereareafewpointstoconsiderwhenusingthisapproach,however.First,whathappenswhenyouoverheal?Insomegames,whenyouhealtofullhealth,yoursensorsmightbeabletodetectonlyhowmuchyouactuallyhealed.Inothergames,yoursensorsmaybeabletodetecttheactualamounthealed.Putanotherway,ifyoucasta30percentstronghealfrom85percenthealth,doyoursensorsseeahealof30percentor15percent?Iftheansweris30percent,you’reset.Iftheansweris15percent,yourcodeneedsawaytoadjustdownward.

OnewaytoadjustaccordinglyistodecrementhealAtwhenyoursensorsseeahealthatbringsyoutofullhealth,likethis:

curDef->condition=[](GameSensors*sensors)->bool{staticfloathealAt=50;if(sensors->detectedStrongHeal()){➊if(sensors->getStrongHealMaxed()){healAt--;}else{autonewHealAt=100-sensors->getStrongHealIncrease();healAt=(healAt+newHealAt)/2.00f;}sensors->clearStrongHealInfo();}returnsensors->getHealthPercent()>healAt;};

ThiscodeisalmostthesameasListing11-4,butitaddsanif()clausetodecrementhealAtifamaxhealisdetected➊.Otherwise,thefunctionshouldbehavelikeListing11-4.

Healingisasimplecase,butthiscodeshowsagreatexampleofhowyoucanuseerrorcorrectiontodynamicallyimproveyourbots’behavior.Onemoreadvancedusecaseisadjustingskillshotstoaccountforenemy

moreadvancedusecaseisadjustingskillshotstoaccountforenemymovementpatterns.Everyplayerhaspatternsinhowtheyavoidskillshots,soifyoursensorsareabletomeasurethedirectionanddistanceanenemymoveswhendodgingaskillshot,yourcontrollercodecanadjustthelocationwherethebotinitiallyshootstheskillshot.Inthissamescenario,learningwouldalsohelpthebotaccountfordifferencesingameserverlatency,charactermovementspeed,andsoon.

Whenusingerrorcorrection,notethatyourcodewillbecleanerandmoreportableifyourstatedefinitionshavesomeformofinternalbookkeepingotherthanstaticvariables.Moreover,toavoidclutteringyourstatedefinitions,Isuggestencapsulatingtheerrorcorrectionlogicinsomeexternalmodulesthatareeasilyinvokedwhenneeded.

PathfindingwithSearchAlgorithmsOnecommonchallengeyou’llfacewhenwritinganautonomousbotiscalculatingapathforacharactertofollowfromonelocationtoanother.Asidefromthesheerreverseengineeringchallengeofcreatingsensorstoreadwhichcoordinatesonthegamemapareblockingforwardmovementornot,there’salsothealgorithmicchallengeofcalculatingapathwithinthatmap.Calculatingapathiscalledpathfinding,andgamehackersoftenuseasearchalgorithmtotackleit.

TwoCommonSearchTechniquesGivenagridoftiles,astartinglocationa,andanendinglocationb,asearchalgorithmcalculatesapathfromatob.Thealgorithmdoesthisbycreatinganodeata,addingnodesadjacenttoatoalistoftilestobeexplored(calledthefrontier),updatingthenodetothebesttileinthefrontier,andrepeatingtheprocessuntilthenodereachesb.Differentsearchalgorithmsselectthebestnodedifferently,usingeitheracost,aheuristic,orboth.

Dijkstra’salgorithm,forexample,calculatesthecostofatilebasedonitsdistancefromtheanodeandselectsthetilewiththelowestcost.Imagineanemptytwo-dimensionalgridwithainthemiddle.InasearchfollowingDijkstra’salgorithm,thefrontierwillexpandinacircularpatternaroundauntilbliesontheedgeofthecircle,asseeninFigure11-4.

Thegreedybest-firstsearchalgorithm,insteadofprioritizingnodesbytheirdistancefromthestartingpoint,usesaheuristictoestimatethedistancefromanodeinthefrontiertob.Thealgorithmthenselectsthenodewiththeshortestestimateddistance.Imaginethisalgorithminthesamegridasbefore;thefrontierwouldbealinegoingalmostdirectlyfromatob,asseeninFigure11-5.

Figure11-4:ThefrontierofDijkstra’salgorithm.Lightertilesarehighercost.

Figure11-5:Thefrontierofthegreedybest-firstsearchalgorithm.Lightertilesarehighercost.

HowObstaclesDisruptSearchesThedifferenceinhowthesealgorithmsbehavebecomescleareronceobstaclesareaddedtothegrid.If,forinstance,awallseparatesaandb,Dijkstra’salgorithmwillalwaysfindthequickestpath,butwithahugeconsequence.Theradiusofthecircularfrontieraroundawillbeequaltothe

lengthofthefinalpath;let’scallthatradiusr.Ifnogridboundariesclipthefrontier,youcanroughlycalculatethenumberofnodesopenedbytakingtheareaofacirclewithradiusr.Ifthepatharoundthewallis50tiles,thealgorithmwillopenroughly7,854tiles,asshowninthisequation:

π×502=7,854

Inthesamescenario,greedybest-firstsearchwillcalculatealess-than-optimalpathbutopensubstantiallyfewertiles.It’snotaseasytovisualizehowthefrontierwillexpand,andit’snotimportantrightnow,soIwon’tgointoithere.Attheendoftheday,neitherofthesealgorithmsreallyfitsthepathfindingproblemwell.Theoptimalpathisslow,andthefastpathisn’toptimal.

Toquicklycalculateanoptimalpath,youneedtofuseDijkstra’salgorithmwithgreedybest-firstsearch.Luckily,someonehasalreadydonethis,andtheresultingalgorithmisamonsterknownasA-starsearch(oftenjustcalledA*).

A*usesthesumofacost,calledg,andaheuristic,calledh,toselectnodes.Theseresultingsumiscalledthescore.Putsimply,score=g+h.LikeDijkstra’salgorithm,A*cancalculatethemostoptimalpathfromatob,andlikegreedybest-firstsearch,itcandosorelativelyquickly.

AnA*SearchAlgorithmNowthatyouknowthefundamentals,let’swritecodetoimplementtheA*algorithm.Thisimplementationwillworkinatwo-dimensionalgrid.Itwon’tallowdiagonalmovementatfirst,butI’lldiscussinabithowyoucanmodifythecodetoworkwithdiagonalmovement,too.

AlloftheexamplecodeforthissectionisintheGameHackingExamples/Chapter11_SearchAlgorithmsdirectoryofthisbook’ssourcefiles.TheincludedprojectscanbecompiledwithVisualStudio2010,buttheyshouldalsoworkwithanyotherC++compiler.Downloadthemathttps://www.nostarch.com/gamehacking/andcompilethemtofollowalong.IfyouexecuteChapter11_SearchAlgorithms.exe,you’llbeabletodefineyourown20×20gridandwatchthealgorithmcalculateasearchpath.

CreatinganA*Node

Tostart,defineanemptyAStarNodeclassasfollows:

typedefstd::shared_ptr<classAStarNode>AStarNodePtr;classAStarNode{public:};

ThiscodedefinestheAStarNodeclassandastd::shared_ptrtypedefinitioncalledAStarNodePtrtomakeiteasiertocreatesafepointerstotheclass.Next,withinthepublicscopeofthisclass,declaremembervariablesforthenode’sx-position,y-position,cost,andnode’sscore:

intx,y;intg,score;

Additionally,youneedapublicmemberoftypeAStarNodePtrthatreferencestheparentnode:

AStarNodePtrparent;

Afterdeclaringallmembervariables,declareapublicconstructorthatinitializesthemuponinstancecreation,asfollows:

AStarNode(intx,inty,intcost,AStarNodePtrp,intscore=0):x(x),y(y),g(cost),score(score),parent(p){}

Now,tomakecreatingsafepointerseasier,addastatichelperfunctionlikethis:

staticAStarNodePtrmakePtr(intx,inty,intcost,AStarNodePtrp,intscore=0){returnAStarNodePtr(newAStarNode(x,y,cost,p,score));}

ThismakePtr()functioncreatesanewinstanceofAStarNodeandreturnstheinstancewrappedinsideofanAstarNodePtr.

Let’srecap.TheAStarNodeclasshasmembervariablesx,y,g,score,andparent.Whentheclassisconstructed,allofthesemembersareinitializedfromvaluespassedtotheconstructor,withtheexceptionofscore,whichis

optional(becauseyouuseitonlywhenmakingcopiesofanAStarNodeinstance)andsetto0ifnotprovided.

Next,defineapublicmemberfunctiontocalculatetheheuristicwhengiventhedestinationcoordinates:

intheuristic(constintdestx,intdesty)const{intxd=destx-x;intyd=desty-y;➊returnabs(xd)+abs(yd);}

ThisfunctionreturnstheManhattandistanceheuristic➊,adistancecalculationdesignedforgridswherediagonalmovementisnotpossible:

|Δx|+|Δy|Tocalculateapaththatallowsdiagonalmovement,you’dneedtomodify

thisfunctiontousetheEuclideandistanceheuristic,whichlookslikethis:

Theclassalsoneedsafunctiontoupdatescore.Youaddthatfunctiontothepublicscopeasfollows:

#defineTILE_COST1voidupdateScore(intendx,intendy){autoh=this->heuristic(endx,endy)*TILE_COST;this->score=g+h;}

Now,scoreshouldchangetog+hwhengivendestinationcoordinatestocalculateh.

Towrapup,thenodeclassalsoneedsafunctionthatcancalculateallofitschildnodes.Thefunctioncoulddothisbycreatingnewnodesforeachtileadjacenttothecurrentnode.Eachnewnodereferstothecurrentnodeasitsparent,sotheclassneedstobeabletocreateanAStarNodePtrtoacopyofthecurrentnodeaswell.Here’showallthatworks:

AStarNodePtrgetCopy(){returnAStarNode::makePtr(x,y,g,parent,score);

}std::vector<AStarNodePtr>getChildren(intwidth,intheight){std::vector<AStarNodePtr>ret;autocopy=getCopy();if(x>0)➊ret.push_back(AStarNode::makePtr(x-1,y,g+TILE_COST,copy));if(y>0)➋ret.push_back(AStarNode::makePtr(x,y-1,g+TILE_COST,copy));if(x<width-1)➌ret.push_back(AStarNode::makePtr(x+1,y,g+TILE_COST,copy));if(y<height-1)➍ret.push_back(AStarNode::makePtr(x,y+1,g+TILE_COST,copy));returnret;}

Thisfunctioncreateschildnodesat(x–1,y)➊,(x,y–1)➋,(x+1,y)➌,and(x,y+1)➍.TheirparentisthenodethatcalledgetChildren,andtheirgistheparent’sgplusTILE_COST.

Toallowfordiagonalmovement,thisfunctionneedstoaddchildrenat(x–1,y–1),(x+1,y–1),(x+1,y+1),and(x–1,y+1).Additionally,ifmovingdiagonallywouldcostmore—thatis,ifthecharacterrequiresmoretimetodoit—you’dalsoneedtodothefollowing:

1. ChangeTILE_COSTto10.

2. DefineaconstantDIAG_TILE_COSTasTILE_COSTmultipliedbythetimeincrease.Ifadiagonalsteptakes1.5timesaslong,DIAG_TILE_COSTwouldbe15.

3. Givediagonalchildrenagoftheparent’sgplusDIAG_TILE_COST.

TofinishoffAStarNode,declareoperatorsforcomparingthepriorityandequalityoftwonodes.Youcouldplacethesedeclarationsoutsidetheclassinglobalscopelikethis:

➊booloperator<(constAStarNodePtr&a,constAStarNodePtr&b){returna.score>b.score;}

➋booloperator==(constAStarNodePtr&a,constAStarNodePtr&b){returna.x==b.x&&a.y==b.y;}

Theseoperatorsallowstd::priority_queuetosortnodesbyscore➊andstd::findtodeterminenodeequalitybylocation➋.

WritingtheA*SearchFunctionNowthatyou’vecompletedtheAStarNodeclass,youcancodetheactualsearchfunction.Startbydefiningthefunctionprototype:

template<intWIDTH,intHEIGHT,intBLOCKING>booldoAStarSearch(intmap[WIDTH][HEIGHT],intstartx,intstarty,intendx,intendy,intpath[WIDTH][HEIGHT]){}

Theprototypeacceptsthegamemap’swidthandheight,aswellasthevaluethatsignifiesablockingtileonthemap,astemplateparameters.ThedoAStarSearch()functionalsotakesthemapitself(map),thestartingcoordinates(startxandstarty),thedestinationcoordinates(endxandendy),andablankmap(path)whereitcanfillthecalculatedpathwhenitfinishes.

NOTE

Thefirstthreeparametersaretemplateparameters,soyoucanpassthemascompiletimeconstants.I’vedonethisfortheexamplecodetoallowexplicitarraysizedeclarationsforthemapandpathparametersandtoallowadefinitevaluetosignifyblockingtilesonthemap.Inpractice,themapyoureadfromagamewillhaveadynamicsize,andyou’llprobablyneedamorerobustwaytopassthisdata.

Next,thedoAStarSearch()functionneedsasortedlisttoholdthefrontierandacontainertotrackallcreatednotessoyoucanupdatethescoreandparentofanexistingnodeifit’sopenedasachildofadifferentparent.Youcancreatetheseasfollows:

std::vector<AStarNodePtr>allNodes;

std::vector<AStarNodePtr>allNodes;std::priority_queue<AStarNodePtr>frontier;

Thefrontierisdefinedwithstd::priority_queuesinceitcanautomaticallysortthenodesbasedontheirscore.Thenodecontainer,allNodes,isdefinedasastd::vector.

Now,let’screatethefirstnode:

autonode=AStarNode::makePtr(startx,starty,0,nullptr);node->updateScore(endx,endy);allNodes.push_back(node);

Thefirstnodeisano-costorphannodeattheposition(startx,starty).ThenodeisgivenascorebasedonwhattheupdateScore()functionreturns,andthenit’saddedtotheallNodescontainer.

Withanodeinthecontainer,it’stimetowritethemeatoftheA*algorithm,startingwithasimpleloop:

while(true){}

Untilotherwisespecified,therestofthecodeinthissectionwillappearinsideofthisloop,intheordershown.

Fromhere,thefirststepistocheckthegoalstate.Inthiscase,thegoalistofindapathfortheplayertofollowtothenextwaypoint,whichhappenswhenthenodeobject’spositionis(endx,endy).Thus,tocheckthegoalstate,theprogramneedstocheckwhethernodehasreachedthosecoordinatesornot.Here’showthatcheckshouldlook:

if(node->x==endx&&node->y==endy){makeList<WIDTH,HEIGHT>(node,allNodes,path);returntrue;}

Whenthegoalstateismet,theprogramreportstruebacktothecallerandfillspathwiththefinalpath.Fornow,assumeafunctioncalledmakeList()canfillinpathforyou;I’llshowyouthisfunctionshortly.Ifthegoalstateisn’tmet,youneedtoexpandthechildrenofnode,whichisactuallyaprettycomplicatedprocess:

autochildren=node->getChildren(WIDTH,HEIGHT);for(autoc=children.begin();c!=children.end();c++){

➊if(map[(*c)->x][(*c)->y]==BLOCKING)continue;autofound=std::find(allNodes.rbegin(),allNodes.rend(),*c);➋if(found!=allNodes.rend()){➌if(*found>*c){(*found)->g=(*c)->g;(*found)->parent=(*c)->parent;(*found)->updateScore(endx,endy);}}else{(*c)->updateScore(endx,endy);➍frontier.push(*c);➎allNodes.push_back(*c);}}

Aftercallingnode->getChildrentogeneratealistofnodesthatcanbeaddedtothefrontier,thecodeiteratesovereachchildandignoresanythatareonblockingtiles➊.Next,foreachchild,thecodecheckswhetheranodehasalreadybeenopenedatthesamecoordinates➋.Ifso,andifthescoreoftheexistingnodeisgreaterthanthescoreofthenewchild,theexistingnodeisupdatedtotheparent,cost,andscoreofthenewchildbytheif()statementat➌.Ifthenewchilddoesn’thaveabrother-from-another-mother,itwillbeaddedasistothefrontier➍andthenodelist➎.

Alsonoticethatstd::findusesthereversebeginandreverseenditeratorsofallNodesinsteadoftheregulariterators➊.Theexampledoesthisbecausenewnodesareappendedtotheendofthevectorandduplicatenodeswillbeclosetogether,soduplicateswillusuallybeclosertotheendofthevector.(Thisstepcouldalsobedonedirectlyagainstthefrontier,butstd::priority_queuedoesn’tallowiterationovernodesandwritingthesortinplacewouldmakethecodetoolargeforprint.)

Eventually,thefunctionwillrunoutofnewchildrentoaddtothefrontier;thefollowingif()statementhandlesthatsituation:

if(frontier.size()==0)returnfalse;➊node=frontier.top();➋frontier.pop();

Thiscodepointsnodetothecheapestnodefromthefrontier➊,removesitfromthefrontier➋,andletsthelooprepeat.Ifthefrontierendsup

empty,thefunctionreportsfalsebacktothecaller,sincethere’snothinglefttosearch.

CreatingthePathListFinally,it’stimetoimplementthemakeList()function:

template<intWIDTH,intHEIGHT>voidmakeList(AStarNodePtrend,std::vector<AStarNodePtr>nodes,intpath[WIDTH][HEIGHT]){for(auton=nodes.begin();n!=nodes.end();n++)➊path[(*n)->x][(*n)->y]=2;autonode=end;while(node.get()!=nullptr){➋path[node->x][node->y]=1;node=node->parent;}}

Thisfunctionupdatespathwithbothalistofclosednodes➊andthecalculatedpath➋.Forthisexample,thevalue2representstheclosednodesand1representsthepathnodes.Theprogramcalculatesnodesinthepathbyfollowingparentnodesfromthegoalnodeuntilitreachesthestartingnode,whichisanorphanwithnullptrasaparent.

WhenA*SearchesAreParticularlyUsefulMakesuretoplaywiththeexamplecodeandexecutablefortheprevioussection,becausethat’stheonlywayyou’llreallygetacquaintedwiththebehaviorofA*searches.Inmostnewergames,youshouldbeabletojustsendapacketwiththedestinationorevenemulateaclickonthemapatthedesiredspot,butwhenyoucomeacrossasituationwhereyouneedtocalculateapath,you’llbegladyoulearnedA*.

Thereareactuallymanysituationswherecalculatingapathcanbeuseful:

SelectingtargetsWhen your bot is selecting targets to attack, you may want to checkwhetheryourcharactercanactuallyreachthem.Otherwise,ifanenemy

isisolatedinanunreachableroom,youmightgetstuckinplacetryingtotargetthemforever!

SelectingcorpsesAs your looting state(s) determine which corpses to open, you canoptimizebyalwaystryingtoloottheclosestcorpsefirst.

EmulatingmousemovementsVery rarely, some heavily protected games actually correlate in-gameactionswithmousemovementstoensurethatthere’snobotrunning.Inthis case, you might need to emulate the mouse. Using a modifiedversionofA*where the screen is themap, there are no blocking tiles,and node costs are slightly randomized, you can calculate human-likepathsforyourmousetofollowwhenyousimulatemovement.

KitingmonstersIfyoueverneed towritecode tokitemonsters,youcan implementA*withagoalstateofbeingNunitsawayfromallcreatures.Usingthesamecostmechanismshown in thischapter,playwith theheuristic togiveahigher cost to nodes that are closer to creatures.Kiting isn’t exactly aconventionalusecase,andtheheuristicwillrequireabunchoftweaking,butitworksamazinglyonceyou’vegotitgoing.Someimplementationscankiteanynumberofmonstersbetterthanahuman!

PredictingenemymovementsIfyou’rewritingabotthatfightsotherplayers,youcanuseA*topredicttheirmovementsandactaccordingly.Forinstance,ifyourenemystartsrunning away, your bot can assume they are running to their base,calculatetheirroute,anduseaspelltoblocktheirpathoreventeleporttoalocationwhereitexpectsthemtobe.

ThesearejustafewusecasesforA*searches,andyou’lldefinitelyfindmanymoreasyouimproveyourbots.Fortherestofthechapter,I’lldescribesomepopularautomatedhacksthatyoucanimplementusingthetechniquesdescribedinthisbook.

OTHERUSESFORA*SEARCHA*isn’tjustforcalculatingpaths.WithabstractionsontopoftheAStarNodeclass,youcanadaptthesamealgorithmtoanysearchproblem.Realistically,A*isjustaweightediterationoveramultidimensionaldatasetthatiteratesuntilsomegoalobjectisfound,and,thus,itcansolveanyproblemthatcanberepresentedasamultidimensionaldataset.MoreadvancedapplicationsforA*includeplayingchessandcheckers,and—whenit’spairedwithathree-dimensionalManhattandistanceheuristicandadepth-firstsearchimplementation—evensolvingaRubik’scube.Sadly,I’mnotgoingtogointotheseusecases;ifyouwanttogetreallygoodwithsearchalgorithms,Iencourageyoutoresearchmoreonline.

CommonandCoolAutomatedHacksNowthatyou’veseenthedesignpatternsandalgorithmsneededtocreateefficient,self-teachingbots,it’stimetolearnaboutsomepopularautomatedhacksthatgobeyondsimplehealingandpathfinding.Let’sflyupto10,000feettoexploretwotypesofbotsatahighlevel.

LootingwithCavebotsWhilediscussingcontroltheory,statemachines,andsearchalgorithms,Itouchedontheideaofacavebotthatkillscreatures,grabsloot,andwalksaroundcaves.Theabilitiesofcavebotscanvarygreatly.

DepositingGoldandRestockingSuppliesIfyouwanttoleaveacharacterbottingfordaysonend,you’llneedadepositorandarefiller.Adepositorcandepositlootinyourbankorvault,whilearefillerrefillsyourpotions,runes,andothersupplies.Thesefeaturescanbedescribedwithsixbasicstates:

LeavespawnConditionmetifthecharacterisinthespawnareaorcave,

ifithasnothingtodeposit,andifithasenoughsupplies.Reachthisstatebyexitingthespawnareaorcave.

WalktotownConditionmetifthecharacterisinthespawnareaorcave.Reachthisstatebywalkingfromthespawnorcavetotown.

DepositConditionmetifthecharacterisinthespawnareaorcave,orifthecharacterisintownandhasnothingtodeposit.Reachthisstatebyputtinglootinthebankorvault.

WithdrawcashConditionmetifthecharacterisinthespawnareaorcave,isintownwithnosuppliestopurchase,orhasenoughgoldtopurchasesupplies.Reachthisstatebywithdrawinggoldfromthebankorvault.

PurchasesuppliesConditionmetifthecharacterisinthespawnareaorcaveorifthecharacterhasenoughsuppliestostarthunting.Reachbybuyingsupplies.

EnterspawnConditionmetifthecharacterisinthespawnareaorcave.Reachthisstatebywalkingtothespawnareaorcave.

Thesestateswouldcomebeforethestatesrelatedtofollowingwaypoints(Idescribeacoupleofthosestatesin“AComplexHypotheticalStateMachine”onpage228)inthevectorofStateDefinitionobjects.Placingthemfirstgivesthempriorityoverremaininginthecave,whilestillallowingthecharactertotarget,kill,andlootmonstersonthewaybacktotown.Dependingonwhereyou’rehuntingandhowyouwantthebottobehave,youmayalsotellyourtargetingstatesnottoattackcreaturesifthecharacterisn’tinthespawnareaorcave,andyoumightaddanextrastatebeforewalktotownthatattacksonlycreaturesthatblockthecharacter’spathtotown.Specifyingthatextrastateincreasesthebot’sefficiency,sincetripstoandfromtownwillbemuchquickerifthemonstersonthewayaren’tworthkilling.

UsingtheCharacterasBaitTwoothercavebotfeaturesthatcanmakeyourbotawesomeareluremodeanddynamiclure.Youwouldn’timplementthesetwofeaturesasactualstatesinacomplexbot;rather,you’dhavetheminformthebot’stargetingandwalkingstatestohelpthebotmakedecisions.

Youcancontrolluremodewithspecialwaypointsinyourpath,anditscodewilltellyourtargetingstatestoattackcreaturesonlyifthebotisstuck,similartothemechanismdiscussedforwalkingtoorfromtown.Thedifferenceisthatluremodecanbeswitchedonandoffatdifferentareasinthecave,allowingyoutoluremultiplemobsofmonsterstocertainlocationsbeforeattackingthem.Thiscanmakeyourbotmuchmoreefficient,ascertaintypesofcharactersmayexcelatkillingmanymonstersatonce.

Dynamiclureissimilar,butinsteadofturningitonandoffatdefinitelocationsviawaypoints,youcanautomaticallyturnluremodeonwhentherearen’tenoughmonsters.Forexample,abotwiththedynamiclurefeaturemighttellthetargetingstatesnottoattackanycreatureuntilfivemonstersareonscreen.Thetargetingstateswouldresumeattackingandkitinguntilallfivemonstersaredead,andthebotwouldsnapbackintoluremodeuntilasuitablysizedmobappearsagain.

Ifyourcharacterisquickenoughtooutrunmonsters,though,you’llneedtomodifyyourbot’swalkingstatestowalkslowlywhenluremodeisonandcreaturesarepresent.Otherwise,yourcharacterwillleavemobsbehindwithoutkillingthem.Youcanslowdownacharacterbyaddingastatebeforethefollowpathstateinyourstatemachinedefinitionthatdelaysmovementslightlywhenluremodeisonandanycreaturesaretoofaraway.

AllowingPlayerstoScriptCustomBehaviorsNearlyeverycavebotincludesascriptinginterfacethatallowsplayerstoaddtheirownbehaviors.Youcouldimplementthisinterfaceasawaytospecifycustomwaypointstofollow,spellstouse,oritemstoloot.Inmoreadvancedbots,youmightmakeyourtargeting,looting,walking,andluringsystemsasdynamicaspossiblesoplayerscanadduniquefeatures.IfyouimplementyourautomationinLua,thirdpartiescouldeasilyimproveandexpandyourbot’sabilities.

Makingyourboteasytowritescriptsfortakesalotofworkoffyourshoulders,sinceotherprogrammerswhoplaythegamemightreleasescriptstoaddsupportfornewhuntingspotsandimproveyourautomation.Suchscriptingservicesarecommoninbottingcommunities,andplayersoftencreateandsellprofessional-gradescriptsthatintegratewithbots.

AutomatingCombatwithWarbotsAnotherclassofautomatedbotsisusedforplayerversusplayer(PvP)combat.Thesewarbots,orPvPbots,havemanyfeaturescategorizedasresponsiveorESPhacks,sincethebotsfocusonrespondingtoincomingdamageorspells,revealinghiddenenemies,andgivingtheplayeraninformationadvantage.

Fullyautomatedwarbotsarerare,butI’vealreadylightlydiscussedhowyoucanusesomeautomationtechniquestomakesmarterhealers,teachbotstolandmoreaccurateskillshots,andpredictplayers’pathstostopthemintheirtracks.Let’sexploreafewothercoolhacksthatfallonthefringeofresponsive,ESP,andautomated.

NOTE

IngamesthatarecompletelyPvPbased,suchasbattlegroundsorreal-timestrategygames,someplayersmightalsojustcallthesebots,sincewarorPvPisthebot’sonlypurpose.

AutowallBotsIfyourcharacterhasa spell tocreatea temporarywall,youcancodeabot that automatically blocks enemy players when they enter smallcorridors.Usingerrorcorrection, thebotcould learnhow faraheadoftheenemytoplacethewall.Withsomereallycreativeengineering,thebot could even learn which enemies can jump over walls by checkingwhethereachenemymanagestogetpastthewallbeforeitdisappears.

AutosnipeBotsForcharacterswithalong-rangeskillshotorglobalexecutionspell,youcan use automation to detect when an enemy across themap has lowhealthandcastyourspelltokillthem.Youcanalsouseerrorcorrectiontomoreaccuratelyguesswheretoshootalong-rangeskillshot.Ifyou’reunabletocalculateexactdamageamounts,errorcorrectioncanalsohelpa bot determine howmuch damage a spell does and tweak the castingthresholdaccordingly.

AutokiteBotsIf you’re playing a carry character that does most of its damage by

attackingatashortdistance,youmightimplementabottoautomaticallykiteenemies.Usingasetofstatessimilartotheonesacavebotmightuseto kite monsters, you can make a bot that automatically kites enemycharacterswhenyouattack them.Whenyou stop targeting theenemy,the bot can stop kiting. Using A* search, you can improve the kitingmechanism to avoidmultiple enemies, or, if you want to escape whileattacking,guidethekitingmechanismbacktoasafeplace,suchasyourteam’sbaseoraneutrallocation.

ClosingThoughtsBythispoint,youshouldbereadytogooutandmakesomeprettyawesomebots.Don’tworryifyou’restillnotcompletelycomfortablewiththetechniquesinthischapter;thebestwaytolearnistojustdiveinandstarthacking.Usethethousandsoflinesofexamplecodeprovidedforthisbooktogetstartedwithoutworkingfromscratch,andmostofall,havefun!

Inthenextchapter,I’lldiscusswaysthatbotscanhidefromanti-cheatmechanisms,whicharepiecesofsoftwarethatgamesusetodetectandstopbotters.

12STAYINGHIDDEN

Gamehackingisanever-evolvingpractice,agameofcatandmousebetweenhackersandgamedeveloperswhereeachpartyworkstosubverttheother.Aslongaspeoplemakebots,gamecompanieswillfindwaystohinderbotadvancesandbanplayerswhousebots.Ratherthanmakingtheirgamesinherentlyhardertohack,though,gamecompaniesfocusondetection.

Thelargestgamecompanieshaveverysophisticateddetectionsuitescalledanti-cheatsoftware.Inthebeginningofthischapter,I’lldiscussthecapabilitiesofthemostcommonanti-cheatsuites.Afterrevealinghowthesesuitesdetectbots,I’llteachyousomepowerfulwaystoevadethem.

ProminentAnti-CheatSoftwareThebest-knownanti-cheatsuitesusethesamemethodsasmostantivirussoftwaretoscanforbotsandflagthemasthreats.Someanti-cheatsuitesarealsodynamic,meaningtheirinnerworkingsandcapabilitiescanchangebasedonthegamethey’reprotecting.Anti-cheatsoftwaredevelopersalsotrackdownandpatchtheirsuitesagainstbypasssoftware,soalwaysdoyourownin-depthresearchofanyanti-cheatsoftwarethatyoumightface.

Whenthesesuitesdetectabotter,theyflagthebotter’saccountforbanishment.Everyfewweeks,gamecompanyadministratorsbantheflaggedplayersinabanwave.Gamecompaniesusebanwavesinsteadof

instantaneousbansbecausebanninginwavesismoreprofitable.Ifbottersarebannedafterafewweeksofplaying,theirfamiliaritywiththegamewillmakethemmorelikelytobuyanewaccountthaniftheywerebannedthemomenttheirbotstartedrunning.

Therearedozensofanti-cheatsuites,butI’llfocusonthefivepackagesthatarethemostcommonandthoroughlyunderstood:PunkBuster,ESEAAnti-Cheat,ValveAnti-Cheat(VAC),GameGuard,andWarden.

ThePunkBusterToolkitPunkBuster,madebyEvenBalanceInc.,istheoriginalanti-cheattoolkit.ManygamesusePunkBuster,butit’smostcommoninfirst-personshootergameslikeMedalofHonor,FarCry3,andseveralinstallmentsoftheBattlefieldseries.

Thetoolkitusesamyriadofdetectionmethods,themostformidableofwhicharesignature-baseddetection(SBD),screenshots,andhashvalidation.PunkBusterisalsoknownforimposinghardwarebansthatpermanentlybanacheater’scomputer,ratherthanjusttheirgameaccount,bysavingafingerprintofthehardware’sserialnumbersandblockingloginsfromamachinethatmatchesit.

Signature-BasedDetectionPunkBusterscansthememoryofallprocessesonasystemrunningagamethatemploysit,searchingforbytepatternsuniquetoknowncheatsoftware,calledsignatures.IfPunkBusterdetectsasignature,theplayerisflaggedforaban.PunkBustercarriesoutmemoryscansfromusermodeusingtheNtQueryVirtualMemory()WindowsAPIfunction,anditsometimesrunsscansfrommultiplehiddenprocesses.

Signature-baseddetectionisblindtocontextbydesign,anditultimatelysuffersfromafatalflaw:falsepositives.OnMarch23,2008,ateamofhackerssetouttoprovetheexistenceofthisflawbyspammingpublicchatroomswithatextstringthatPunkBusterwouldidentifyasabotsignature.SinceSBDblindlyscansprocessmemoryformatchingpatterns,anyandalllegitimateplayersinsidethesepublicchatroomswereflaggedasbotters.

Thiscausedthousandsoffairplayerstobebannedwithnojustification.AsimilarsituationhappenedagaininNovember2013:PunkBusterfalselybannedthousandsofplayersonBattlefield4.Thattime,noonewastryingtoproveapoint;thecompanyhadjustaddedabadsignaturetoitssoftware.

PunkBusterresolvedbothoftheseissuesbyrestoringtheplayers’accounts,buttheseincidentsshowjusthowaggressiveitsflavorofSBDis.Inthetimesincetheseattacks,though,PunkBuster’sSBDhasreducedthenumberoffalsepositivesbycheckingonlyforsignaturesatpredefinedbinaryoffsets.

ScreenshotsAsanothermethodofbotdetection,PunkBusteralsoperiodicallytakesscreenshotsofaplayer’sscreenandsendsthemtothecentralgameserver.Thisformofdetectionisanuisance,andit’sweakcomparedtoSDB.Game-hackingcommunitiesspeculatethatPunkBusterimplementedthisfeaturetogivegameadminsproofagainstbotterswhodisputebans.

HashValidationInadditiontoemployingSBDandscreenshots,PunkBusterdetectsbotsbycreatingcryptographichashesofagame’sexecutablebinariesonaplayer’ssystemandcomparingthemtohashesstoredonacentralserver.Ifthehashesdonotmatch,theplayerisflaggedforaban.Thischeckiscarriedoutonlyonthebinariesonthefilesystem,notonin-memorybinaries.

TheESEAAnti-CheatToolkitTheESEAAnti-CheattoolkitisusedbytheE-SportsEntertainmentAssociation(ESEA),primarilyforitsCounter-Strike:GlobalOffensiveleague.UnlikePunkBuster,thissuiteisknownforgeneratingveryfewfalsepositivesandbeinghighlyeffectiveatcatchingcheaters.

ESEAAnti-Cheat’sdetectioncapabilitiesresemblethoseofPunkBuster,withonenoteworthydifference.ESEAAnti-Cheat’sSBDalgorithmiscarriedoutfromakernel-modedriverusingthreedifferentWindowsKernelfunctions:theMmGetPhysicalMemoryRanges()function,theZwOpenSection()

function,andtheZwMapViewOfSection()function.Thisimplementationmakestheanti-cheatsystemnearlyimmunetomemoryspoofing(acommonwaytodefeatSBD),asthefunctionsusedbythescanaremuchhardertohookwhenthey’recalledfromadriver.

TheVACToolkitVACisthetoolkitValveCorporationappliestoitsowngamesandmanyofthethird-partygamesavailableonitsSteamgamingplatform.VACusesSDBandhashvalidationmethodsthatresemblePunkBuster’sdetectiontechniques,anditalsousesDomainNameSystem(DNS)cachescansandbinaryvalidation.

DNSCacheScansDNSisaprotocolthatconvertsbetweendomainnamesandIPaddressessmoothly,andtheDNScacheiswherethatinformationgetsstoredonacomputer.WhenVAC’sSBDalgorithmdetectscheatsoftware,VACscanstheplayer’sDNScacheforanydomainnamesassociatedwithcheatingwebsites.It’snotcertainwhetherapositiveDNScachescanisrequiredforVAC’sSBDalgorithmtoflagaplayerforbanishment,oriftheDNScachescansimplyactsasanothernailinthecoffinforplayerswhoarealreadyflaggedbySBD.

NOTE

ToseeyourDNScache,enteripconfig/displaydnsatacommandprompt.Yes,VAClooksatallofthat.

BinaryValidationVACalsousesbinaryvalidationtopreventin-memorytamperingofexecutablebinaries.ItscansformodificationslikeIAT,jump,andcodehookingbycomparinghashesofin-memorybinarycodetohashesofthesamecodeinthebinariesonthefilesystem.Ifitfindsamismatch,VACflagstheplayerforaban.

Thisdetectionmethodisformidable,butValve’sinitialimplementationofthealgorithmwasflawed.InJuly2010,VAC’sbinaryvalidationfalselybanned12,000CallofDutyplayers.ThebinaryvalidationmodulefailedtoaccountforaSteamupdate,anditbannedtheplayerswhentheirin-memorycodedidnotmatchtheupdatedbinariesonthefilesystem.

FalsePositivesVAChashadotherissueswithfalsepositives.Itsinitialreleaseroutinelybannedfairplayersfor“faultymemory.”ThissameearlyversionbannedplayersforusingCedega,aplatformthatranWindowsgamesonLinux.AndonApril1,2004,Valvefalselybannedacouplethousandplayersduetoaserver-sideglitch.Ontwoseparateoccasions,oneinJune2011andoneinFebruary2014,VACalsofalselybannedthousandsofTeamFortress2andCounter-Strikeplayersduetobugsthatthecompanyrefusestodisclose.AswithPunkBuster,theseincidentsshowthatVACisveryaggressive.

TheGameGuardToolkitGameGuardisananti-cheattoolkitmadebyINCAInternetCo.Ltd.andusedbymanyMMORPGs,includingLineageII,CabalOnline,andRagnarokOnline.InadditiontosomemildlyaggressiveSBD,GameGuardusesrootkitstoproactivelypreventcheatsoftwarefromrunning.

User-ModeRootkitGameGuardutilizesauser-moderootkittodenybotsaccesstotheWindowsAPIfunctionstheyusetooperate.Therootkithooksthefunctionsattheirlowest-levelentrypoint,ofteninsideundocumentedfunctionsinntdll.dll,user32.dll,andkernel32.dll.ThesearethemostnotableAPIfunctionsGameGuardhooks,andhere’swhatGameGuarddoesfrominsideeachhookedfunction:

NtOpenProcess()BlocksanyOpenProcess()attemptsonthegamebeingprotected.

NtProtectVirtualMemory()BlocksanyVirtualProtect()or

VirtualProtectEx()attemptsonthegame.

NtReadVirtualMemory()andNtWriteVirtualMemory()BlockanyReadProcessMemory()andWriteProcessMemory()attemptsonthegame.

NtSuspendProcess()andNtSuspendThread()BlockanyattemptstosuspendGameGuard.

NtTerminateProcess()andNtTerminateThread()BlockanyattemptstoterminateGameGuard.

PostMessage(),SendMessage(),andSendInput()Blockanyattemptstosendprogrammaticinputtothegame.

SetWindowsHookEx()Preventsbotsfromgloballyinterceptingmouseandkeyboardinput.

CreateProcessInternal()Automaticallydetectsandhooksintonewprocesses.

GetProcAddress(),LoadLibraryEx(),andMapViewOfFileEx()PreventanyattempttoinjectlibrariesintothegameorGameGuard.

Kernel-ModeRootkitGameGuardalsousesadriver-basedrootkittopreventbotsthatworkinthekernel.Thisrootkithasthesameabilitiesasitsuser-modecounterpart,anditworksbyhookingZwProtectVirtualMemory(),ZwReadVirtualMemory(),ZwWriteVirtualMemory(),SendInput(),andsimilarfunctions.

TheWardenToolkitWarden,madeexclusivelyforBlizzard’sgames,isbyfarthemostadvancedanti-bottoolkitI’veencountered.It’shardtosaywhatexactlyWardendoes,becauseitdownloadsdynamiccodeatruntime.Thiscode,deliveredascompiledshellcode,typicallyhastworesponsibilities:

•Detectbots.

•Periodicallysendaheartbeatsignaltothegameserver.Thevaluesentisnotpredefinedbutinsteadisgeneratedbysomesubsetofthedetectioncode.

code.

IfWardenfailstocompletethesecondtaskorsendsthewrongvalue,thegameserverwillknowthatit’sbeendisabledortamperedwith.Furthermore,abotcan’tdisablethedetectioncodeandleavetheheartbeatcoderunning.

THEHALTINGPROBLEM

AbotthatcoulddisableWarden’sdetectioncodeandstillsendtheheartbeatsignalwouldsolvethehaltingproblem,whichAlanTuringprovedtobeimpossiblein1936.Thehaltingproblemistheproblemofdetermining,withagenericalgorithm,whetheraprogramwillfinishrunningorcontinueforever.BecauseWardendoestwotasksusingthesameshellcode,writingagenericalgorithmthatcandisablejustonetaskisavariationofthehaltingproblem:thealgorithmcan’tbesurewhichpartsofthecodewilldefinitelyexecute,whichpartswon’t,andwhichpartsareresponsibleforeachtask.

Wardenisformidablebecauseyounotonlyhavenowaytoknowwhatyou’rehidingfrombutalsohavenowaytodisablethetoolkit.Evenifyoumanagetoavoiddetectiontoday,anewdetectionmethodmightbeusedtomorrow.

Ifyouplanonpubliclydistributingbots,youwilleventuallymeetoneoftheanti-cheatsolutionsdescribedintheprevioussections—andyou’llhavetobeatit.Dependingonyourbot’sfootprint,thetypeofdetectioninthegameyou’rebotting,andyourimplementation,thedifficultyofevadingoneofthesetoolkitscanrangefromtrivialtoextremelyhard.

CarefullyManagingaBot’sFootprintAbot’sfootprintishowmanyunique,detectablecharacteristicsithas.Forexample,abotthathooks100functionswilltypicallybeeasiertodetectthanabotthathooksonly10functionsbecausetheformermakesanorderofmagnitudemorechangestoagame’scodethanthelatter.Sinceatargeted

detectionsystemneedstodetectonlyonehook,thedeveloperoftheformerbotneedstospendmuchmoretimemakingsureallofthebot’shooksareasstealthyaspossible.

Anotherfootprintcharacteristicishowdetailedabot’suserinterfaceis.Ifaknownbothasmanydialogboxesthatallhavespecifictitles,agamecompanycanjusthaveitsanti-cheatsoftwaredetectthebotbysearchingforwindowsthathavethosetitles.Thissamebasicreasoningcanbeusedwithprocessnamesandfilenames.

MinimizingaBot’sFootprintDependingonhowyourbotworks,therearemanywaystominimizeitsfootprint.Ifyourbotreliesheavilyonhooks,forinstance,youcanavoiddirectlyhookingagame’scodeandinsteadfocusonhookingWindowsAPIfunctions.WindowsAPIhookingissurprisinglycommon,sodeveloperscan’tassumeaprogramthathookstheWindowsAPIisabot.

Ifyourbothasawell-defineduserinterface,youcanmasktheinterfacebyremovingallstringsfromwindowbars,buttons,andsoon.Instead,displayimagesthatshowtext.Ifyou’reworriedaboutspecificprocessnamesorfilenamesbeingdetectedbytheanti-cheatsoftware,usegenericfilenamesandmakeyourbotcopyitselftoanew,randomizeddirectoryeverytimeitlaunches.

MaskingYourFootprintMinimizingyourfootprintisapreferredwaytoavoiddetection,butit’snotnecessary.Youcanalsoobfuscateyourbot,makingitharderforanyonetofigureouthowitworks.Obfuscationcanpreventbothanti-botdevelopersfromtryingtodetectyourbotandotherbotdevelopersfromanalyzingyourbottostealproprietaryfunctionality.Ifyousellyourbot,obfuscationpreventspeoplefromcrackingittobypassyourpurchaseverification,too.

Onecommontypeofobfuscationiscalledpacking.Packinganexecutableencryptsitandhidesitinsideanotherexecutable.Whenthecontainerexecutableislaunched,thepackedexecutableisdecryptedandexecutedin-memory.Whenabotispacked,analyzingthebinarytolearnwhatthebotdoesisimpossible,anddebuggingthebotprocessismuchharder.SomecommonpackerprogramsareUPX,Armadillo,Themida,andASPack.

TeachingaBottoDetectDebuggersWhenanti-botdevelopers(orotherbotcreators)candebugabot,theycanfigureouthowitworksandthushowtostopit.Ifsomeoneisactivelytryingtopickapartabot,packingtheexecutablemaynotbeenoughtoevadethem.Toprotectagainstthis,botsoftenemployanti-debuggingtechniques,whichobfuscatecontrolflowbychangingthebot’sbehaviorwhenadebuggerisdetected.Inthissection,I’llquicklycoversomewell-knownmethodsfordetectingwhenadebuggerisattachedtoyourbot,andinthenext,I’llshowyousometricksforobfuscation.

CallingCheckRemoteDebuggerPresent()CheckRemoteDebuggerPresent()isaWindowsAPIfunctionthatcantellyouifadebuggerisattachedtothecurrentprocess.Codetocheckforadebuggermightlooklikethis:

boolIsRemoteDebuggerPresent(){BOOLdbg=false;CheckRemoteDebuggerPresent(GetCurrentProcess(),&dbg);returndbg;}

Thischeckisprettystraightforward—itcallsCheckRemoteDebuggerPresent()withthecurrentprocessandapointertothedbgBoolean.Callingthisfunctionistheeasiestwaytodetectadebugger,butit’salsoveryeasyforadebuggertoevade.

CheckingforInterruptHandlersInterruptsaresignalstheprocessorsendstotriggeracorrespondinghandlerintheWindowskernel.Interruptsaretypicallygeneratedbyhardwareevents,buttheycanalsobegeneratedinsoftwareusingtheINTassemblyinstruction.Thekernelallowssomeinterrupts—namely,interrupts0x2Dand0x03—totriggeruser-modeinterrupthandlersintheformofexceptionhandlers.Youcantakeadvantageoftheseinterruptstodetectdebuggers.

Whenadebuggersetsabreakpointonaninstruction,itreplacesthatinstructionwithabreakpointinstruction,suchasINT0x03.Whentheinterruptisexecuted,thedebuggerisnotifiedviaanexceptionhandler,whereithandlesthebreakpoint,replacestheoriginalcode,andallowstheapplicationtoresumeexecutionseamlessly.Whenfacedwithan

applicationtoresumeexecutionseamlessly.Whenfacedwithanunrecognizedinterrupt,somedebuggersevensilentlystepoverthatinterruptandallowexecutiontocontinuenormally,withouttriggeringanyotherexceptionhandlers.

Youcandetectthisbehaviorbypurposelygeneratinginterruptswithinexceptionhandlersinyourcode,asshowninListing12-1.

inlineboolHas2DBreakpointHandler(){__try{__asmINT0x2D}__except(EXCEPTION_EXECUTE_HANDLER){returnfalse;}returntrue;}

inlineboolHas03BreakpointHandler(){__try{__asmINT0x03}__except(EXCEPTION_EXECUTE_HANDLER){returnfalse;}returntrue;}

Listing12-1:Detectinginterrupthandlers

Duringnormalexecution,theseinterruptstriggertheexceptionhandlerssurroundingtheminthecode.Duringadebuggingsession,somedebuggersmightintercepttheexceptionsgeneratedbytheseinterruptsandsilentlyignorethem,preventingthesurroundingexceptionhandlersfromexecuting.Thus,iftheinterruptsdon’ttriggeryourexceptionhandler,thenadebuggerispresent.

CheckingforHardwareBreakpointsDebuggerscanalsosetbreakpointsusingtheprocessor’sdebugregisters;thesearecalledhardwarebreakpoints.Adebuggercansetahardwarebreakpointonaninstructionbywritingtheaddressoftheinstructiontooneofthefourdebugregisters.

Whenanaddresspresentonadebugregisterisexecuted,thedebuggerisnotified.Todetecthardwarebreakpoints(andthus,thepresenceofadebugger),youcancheckfornonzerovaluesonanyofthefourdebugregisterslikethis:

boolHasHardwareBreakpoints(){CONTEXTctx={0};ctx.ContextFlags=CONTEXT_DEBUG_REGISTERS;autohThread=GetCurrentThread();

if(GetThreadContext(hThread,&ctx)==0)returnfalse;return(ctx.Dr0!=0||ctx.Dr1!=0||ctx.Dr2!=0||ctx.Dr3!=0);}

PrintingDebugStringsOutputDebugString()isaWindowsAPIfunctionthatcanbeusedtoprintlogmessagestoadebuggerconsole.Ifnodebuggerispresent,thefunctionwillreturnwithanerrorcode.Ifadebuggerispresent,however,thefunctionwillreturnwithnoerrorcode.Here’showyoucanusethisfunctionasatrivialdebuggercheck:

inlineboolCanCallOutputDebugString(){SetLastError(0);OutputDebugStringA("test");return(GetLastError()==0);}

LiketheCheckRemoteDebuggerPresent()method,thismethodisverystraightforwardbutalsoveryeasyforadebuggertoevade.

CheckingforDBG_RIPEXCEPTIONHandlersDebuggerstypicallyhaveexceptionhandlersthatblindlycatchexceptionswithWindows’DBG_RIPEXCEPTIONexceptioncode,makingthatcodeaclearwaytospotadebugger.YoucandetecttheseexceptionhandlersinmuchthesamewayListing12-1detectsinterrupthandlers:

#defineDBG_RIPEXCEPTION0x40010007inlineboolhasRIPExceptionHandler(){__try{RaiseException(DBG_RIPEXCEPTION,0,0,0);}__except(EXCEPTION_EXECUTE_HANDLER){returnfalse;}returntrue;}

TimingControl-CriticalRoutinesIfananti-botdeveloperisdebuggingyourbot,thedeveloperwilllikelyplacebreakpointsonandsingle-stepthroughpartsofyourcodethatarecriticaltothebot’sbehavior.Youcandetectthisactivitybymeasuringcodeexecutiontimes;whensomeonestepsthroughcode,executiontakesalotlongerthanusual.

usual.Forexample,ifafunctiononlyplacessomehooks,youcanbesurethat

thecodeshouldn’ttakemorethanatenthofasecondtodothememoryprotection.YoucouldchecktheexecutiontimeformemoryprotectionwithhelpfromtheGetTickCount()WindowsAPIfunction,asfollows:

--snip--autostartTime=GetTickCount();protectMemory<>(...);if(GetTickCount()-startTime>=100)debuggerDetectedGoConfuseIt();--snip--

CheckingforDebugDriversSomedebuggersloadkernel-modedriverstoassisttheiroperation.Youcandetectthesedebuggersbyattemptingtogetahandletotheirkernel-modedrivers,likethis:

boolDebuggerDriversPresent(){//anarrayofcommondebuggerdriverdevicenamesconstchardrivers[9][20]={"\\\\.\\EXTREM","\\\\.\\ICEEXT","\\\\.\\NDBGMSG.VXD","\\\\.\\RING0","\\\\.\\SIWVID","\\\\.\\SYSER","\\\\.\\TRW","\\\\.\\SYSERBOOT","\0"};for(inti=0;drivers[i][0]!='\0';i++){autoh=CreateFileA(drivers[i],0,0,0,OPEN_EXISTING,0,0);if(h!=INVALID_HANDLE_VALUE){CloseHandle(h);returntrue;}}returnfalse;}

Thereareafewcommonkernel-modedriverdevicenamestocheckfor,like\\\\.\\EXTREMandtheothersshowninthedriversarray.Ifthishandle-fetchingcodesucceeds,thenthere’sadebuggerrunningonthesystem.Unlikewiththepreviousmethods,though,obtainingahandletooneofthosedriversdoesn’talwaysmeanthedebuggerisattachedtoyourbot.

Anti-DebuggingTechniquesOnceyoudetectadebugger,therearemultiplewaystoobfuscateyourcontrolflow.Forinstance,youmighttrytocrashthedebugger.ThefollowingcodecrashesOllyDbgv1.10:

OutputDebugString("%s%s%s%s");

Thestring"%s%s%s%s"containsformatspecifiers,andOllyDbgpassesittoprintf()withoutanyextraparameters,whichiswhythedebuggercrashes.Youcouldplacethiscodeinafunctionthatgetscalledinresponsetodetectingadebugger,butthisoptionworksonlyagainstOllyDbg.

CausinganUnavoidableInfiniteLoopAnotherobfuscationmethodtotryisoverloadingthesystemuntilthepersondebuggingyourbotisforcedtoclosethebotanddebugger.Thisfunctiondoesthetrick:

voidSelfDestruct(){std::vector<char*>explosion;while(true)explosion.push_back(newchar[10000]);}

Theinfinitewhileloopjustkeepsaddingelementstoexplosionuntiltheprocessrunsoutofmemoryorsomeonepullstheplug.

OverflowingtheStackIfyouwanttoreallyconfusetheanalyst,youcanmakeachainoffunctionsthateventuallycauseastackoverflow,butinanindirectway:

#include<random>typedefvoid(*_recurse)();voidrecurse1();voidrecurse2();voidrecurse3();voidrecurse4();voidrecurse5();_recurserecfuncs[5]={&recurse1,&recurse2,&recurse3,&recurse4,&recurse5};voidrecurse1(){recfuncs[rand()%5]();}voidrecurse2(){recfuncs[(rand()%3)+2]();}

voidrecurse3(){if(rand()%100<50)recurse1();elserecfuncs[(rand()%3)+1]();}voidrecurse4(){recfuncs[rand()%2]();}voidrecurse5(){for(inti=0;i<100;i++)if(rand()%50==1)recfuncs[i%5]();recurse5();}//callanyoftheabovefunctionstotriggerastackoverflow

Inanutshell,thesefunctionsrandomlyandinfinitelyrecurseuntilthere’snoroomleftonthecallstack.Causingtheoverflowindirectlymakesithardfortheanalysttopauseandexaminepreviouscallsbeforetheyrealizewhat’shappened.

CausingaBSODIfyou’reseriousaboutobfuscation,youcaneventriggeraBlueScreenofDeath(BSOD)whenyoudetectadebugger.Onewaytodothatistosetyourbot’sprocessascriticalusingtheSetProcessIsCritical()WindowsAPIfunctionandthencallexit(),sinceWindowswilltriggeraBSODwhenacriticalprocessiskilled.Here’showyoumightdothat:

voidBSODBaby(){typedeflong(WINAPI*RtlSetProcessIsCritical)(BOOLEANNew,BOOLEAN*Old,BOOLEANNeedScb);autontdll=LoadLibraryA("ntdll.dll");if(ntdll){autoSetProcessIsCritical=(RtlSetProcessIsCritical)GetProcAddress(ntdll,"RtlSetProcessIsCritical");if(SetProcessIsCritical)SetProcessIsCritical(1,0,0);}}

BSODBaby();exit(1);

Ormaybeyou’reevil,inwhichcaseyoucandothis:

BSODBaby();OutputDebugString("%s%s%s%s");recurse1();exit(1);

Assumingyou’veimplementedallofthetechniquesdescribedinthissection,thiscodewouldcauseaBSOD,crashthedebugger(ifit’sOllyDbgv1.10),overflowthestack,andexittherunningprogram.Ifanyoneofthemethodsfailsorgetspatched,theanalyststillhastodealwiththeremainingonesbeforetheycancontinuedebugging.

DefeatingSignature-BasedDetectionEvenwithamazingobfuscation,youwon’teasilybeatsignaturedetection.Engineerswhoanalyzebotsandwritesignaturesareveryskilled,andobfuscationis,atbest,anuisancethatmakestheirjobmarginallyharder.

TocompletelyevadeSBD,youneedtosubvertthedetectioncode.ThisrequiresknowingexactlyhowtheSBDworks.PunkBuster,forinstance,usesNtQueryVirtualMemory()toscanthememoryofallrunningprocessesforanysignatures.Ifyouwanttobypassthis,youcaninjectcodeintoallPunkBusterprocesseswithahookontheNtQueryVirtualMemory()function.

Whenthefunctiontriestoquerymemoryfromyourbotprocess,youcangiveitwhateverdatayouwant,likethis:

NTSTATUSonNtQueryVirtualMemory(HANDLEprocess,PVOIDbaseAddress,MEMORY_INFORMATION_CLASSmemoryInformationClass,PVOIDbuffer,ULONGnumberOfBytes,PULONGnumberOfBytesRead){

//ifthescanisonthisprocess,makesureitcan'tseethehookDLLif((process==INVALID_HANDLE_VALUE||process==GetCurrentProcess())&&baseAddress>=MY_HOOK_DLL_BASE&&baseAddress<=MY_HOOK_DLL_BASE_PLUS_SIZE)➊returnSTATUS_ACCESS_DENIED;

//ifthescanisonthebot,zerothereturnedmemoryautoret=origNtQueryVirtualMemory(process,baseAddress,memoryInformationClass,buffer,numberOfBytes,numberOfBytesRead);if(GetProcessId(process)==MY_BOT_PROCESS)➋ZeroMemory(buffer,numberOfBytesRead);returnret;}

ThisonNtQueryVirtualMemory()hookreturnsSTATUS_ACCESS_DENIED➊whenNtQueryVirtualMemory()triestoquerythehookDLL’smemory,butitgiveszeroedmemory➋whenNtQueryVirtualMemory()triestoquerythebot’smemory.Thedifferenceisn’tforanyspecificreason;I’mjustshowingtwowaysyoucanhidefromtheNtQueryVirtualMemory()functioncall.Ifyou’rereallyparanoid,youcanevenreplacetheentirebufferwitharandombytesequence.

Ofcourse,thismethodworksonlyforSBDthathappensfromusermode,liketheSBDinPunkBusterorVAC.SBDthathappensfromthedriver,likeESEA’s,orthatisn’tpredictable,likeWarden’s,isn’taseasytobypass.

Inthosecases,youcantakeprecautionstoeliminateuniquesignaturesinyourbot.Ifyou’redistributingthebottomorethanadozenorsopeople,however,removingalldistinguishingpropertiesistricky.Tothrowanalystsoffthescent,eachtimeyougivesomebodyacopyofthebot,youcouldtrysomecombinationofthefollowing:

•Compilingthebotusingadifferentcompiler

•Changingthecompileroptimizationsettings

•Togglingbetweenusing__fastcalland__cdecl

•Packingthebinariesusingadifferentpacker

•Switchingbetweenstaticanddynamiclinkingofruntimelibraries

Varyingtheseelementscreatesadifferentassemblyforeachuser,butthere’salimitonhowmanyuniqueversionsofthebotyoucanproducethatway.Pastsomepoint,thismethoddoesn’tscaletodemand,andeventually,gamecompanieswillhavesignaturesforeveryincarnationofyourbot.

Apartfromobfuscationandcodemutation,therearen’tmanywaystodefeatadvancedSBDmechanisms.Youcouldimplementyourbotinadriverorcreateakernel-moderootkittohideyourbot,buteventhosemethodsaren’tfoolproof.

NOTE

Thisbookdoesn’tcoverimplementingabotinadriverorcreatingarootkitto

hideabot,asbothtopicsareprettycomplex.Rootkitdevelopmentaloneisasubjectthatdozensofbookshavecoveredalready.I’drecommendBillBlunden’sTheRootkitArsenal:EscapeandEvasioninTheDarkCornersofTheSystem(Jones&BartlettLearning,2009).

Somegamehackerstrytocovereverysinglebase,hookingeverymemory-readingfunctionandtheentirefilesystemAPI,butstillgetcaughtbydeterminedsystemslikeWarden.Infact,IrecommendstayingawayfromWardenandBlizzardatallcosts.

DefeatingScreenshotsIfyouencounteradetectionmechanismthatusesscreenshotsasadditionalprooftonailbotters,you’reinluck.Bypassingscreenshotmechanismsiseasy:don’tletyourbotbeseen.

YoucansubvertthistypeofdetectionbykeepingaminimalUIandmakingnovisiblydistinguishablechangestothegameclient.IfyourbotrequiresaHUDorotherdistinctiveUIdisplays,though,don’tfret—youcanhaveyourcakeandeatit,too.Aslongasyoucaninterceptthescreenshotcode,youcanhideyourfingerprintswhileascreenshotistaken.

InsomeversionsofPunkBuster,forexample,theWindowsAPIfunctionGetSystemTimeAsFileTime()iscalledjustbeforeascreenshotistaken.YoucanuseahookonthisfunctiontoquicklyhideyourUIforafewsecondstoensureit’snotseen:

voidonGetSystemTimeAsFileTime(LPFILETIMEsystemTimeAsFileTime){myBot->hideUI(2000);//hideUIfor2secondsorigGetSystemTimeAsFileTime(systemTimeAsFileTime);}

JusthookGetSystemTimeAsFileTime()usingthetechniquesdescribedin“HookingtoRedirectGameExecution”onpage153,writeahideUI()function,andcallthehideUI()functionbeforeexecutionresumes.

DefeatingBinaryValidationDefeatingbinaryvalidationisassimpleasnotplacinghooksinsidegame-

Defeatingbinaryvalidationisassimpleasnotplacinghooksinsidegame-specificbinaries.JumphooksandIAThooksonWindowsAPIfunctionsareextremelycommon,sowhereveryoucan,trytogetawaywithusingthosemethodsinsteadofusingjumpornear-callhooksinagamebinary.Incaseswhereyoumustdirectlyhookagame’scode,youcantricktheanti-cheatsoftware’sbinaryvalidationroutinesbyinterceptingthebinaryscanandspoofingthedatatomatchwhatthesoftwareexpectstosee.

LikeSBD,binaryvalidationoftenusesNtQueryVirtualMemory()toscanmemory.Totrickthevalidationcode,startwithahookonthatfunction.Then,writeafunctionlikethisonetospoofthedatawhenNtQueryVirtualMemory()iscalled:

NTSTATUSonNtQueryVirtualMemory(HANDLEprocess,PVOIDbaseAddress,MEMORY_INFORMATION_CLASSmemoryInformationClass,PVOIDbuffer,ULONGnumberOfBytes,PULONGnumberOfBytesRead){

autoret=origNtQueryVirtualMemory(process,baseAddress,memoryInformationClass,buffer,numberOfBytes,numberOfBytesRead);//placetrickycodesomewhereinherereturnret;}

Insidethishook,you’llneedtowatchforanymemoryscansovermemorythathasbeenmodifiedbyoneofyourhooks.

NOTE

ThisexampleassumesthebothasonlyonehookandthatvariablesprefixedwithHOOK_alreadyexistanddescribethecodethehookreplaces.

Listing12-2showssomescan-monitoringcode.

//isthescanonthecurrentprocess?boolcurrentProcess=process==INVALID_HANDLE_VALUE||process==GetCurrentProcess();

//isthehookinthememoryrangebeingscanned?autoendAddress=baseAddress+numberOfBytesRead-1;boolcontainsHook=(HOOK_START_ADDRESS>=baseAddress&&

HOOK_START_ADDRESS<=endAddress)||(HOOK_END_ADDRESS>=baseAddress&&HOOK_END_ADDRESS<=endAddress);➊if(currentProcess&&containsHook){//hidethehook}

Listing12-2:Checkingwhetherhookedmemoryisbeingscanned

Whenamemoryscanoverthehookedcodehappens(whichmakescurrentProcessandcontainsHookbecometrueatthesametime),codeinsidetheif()statement➊updatestheoutputbuffertoreflecttheoriginalcode.Thismeansyoumustknowwherethehookedcodeiswithinthescannedblock,takingintoaccountthefactthattheblockmayspanonlyasubsetofthehookedcode.

SoifbaseAddressmarkstheaddresswherethescanstarts,HOOK_START_ADDRESSmarksthespotwherethemodifiedcodestarts,endAddressmarkstheaddresswherethescanends,andHOOK_END_ADDRESSmarkstheaddresswherethemodifiedcodeends,youcanusesomesimplemathtocalculatewhichpartsofthemodifiedcodearepresentinwhichpartsofthebuffer.Youdosoasfollows,usingwriteStarttostoretheoffsetofthemodifiedcodeinthescanbufferandreadStarttostoretheoffsetofthescanbufferrelativetothemodifiedcode,incasethescanbufferstartsinthemiddleofthemodifiedcode:

intreadStart,writeStart;if(HOOK_START_ADDRESS>=baseAddress){readStart=0;writeStart=HOOK_START_ADDRESS-baseAddress;}else{readStart=baseAddress-HOOK_START_ADDRESS;writeStart=baseAddress;}

intreadEnd;if(HOOK_END_ADDRESS<=endAddress)readEnd=HOOK_LENGTH-readStart-1;elsereadEnd=endAddress–HOOK_START_ADDRESS;

Onceyouknowhowmanybytesyouneedtoreplace,wheretoputthem,andwheretogetthem,youcandothespoofwiththreelinesofcode:

char*replaceBuffer=(char*)buffer;

for(;readStart<=readEnd;readStart++,writeStart++)replaceBuffer[writeStart]=HOOK_ORIG_DATA[readStart];

Completelyassembled,thecodelookslikethis:

NTSTATUSonNtQueryVirtualMemory(HANDLEprocess,PVOIDbaseAddress,MEMORY_INFORMATION_CLASSmemoryInformationClass,PVOIDbuffer,ULONGnumberOfBytes,PULONGnumberOfBytesRead){autoret=origNtQueryVirtualMemory(process,baseAddress,memoryInformationClass,buffer,numberOfBytes,numberOfBytesRead);boolcurrentProcess=process==INVALID_HANDLE_VALUE||process==GetCurrentProcess();autoendAddress=baseAddress+numberOfBytesRead-1;boolcontainsHook=(HOOK_START_ADDRESS>=baseAddress&&HOOK_START_ADDRESS<=endAddress)||(HOOK_END_ADDRESS>=baseAddress&&HOOK_END_ADDRESS<=endAddress);if(currentProcess&&containsHook){intreadStart,writeStart;if(HOOK_START_ADDRESS>=baseAddress){readStart=0;writeStart=HOOK_START_ADDRESS-baseAddress;}else{readStart=baseAddress-HOOK_START_ADDRESS;writeStart=baseAddress;}

intreadEnd;if(HOOK_END_ADDRESS<=endAddress)readEnd=HOOK_LENGTH-readStart-1;elsereadEnd=endAddress–HOOK_START_ADDRESS;

char*replaceBuffer=(char*)buffer;for(;readStart<=readEnd;readStart++,writeStart++)replaceBuffer[writeStart]=HOOK_ORIG_DATA[readStart];}returnret;}

Ofcourse,ifyouhadmultiplehooksthatyouneededtohidefrombinaryvalidationscans,youwouldneedtoimplementthisfunctionalityinamorerobustwaythatwouldallowittotrackmultiplemodifiedcoderegionsaccordingly.

DefeatinganAnti-CheatRootkitGameGuardandsomeotheranti-cheatsuitescomewithuser-moderootkitsthatnotonlydetectbotsbutalsoproactivelypreventthemfromrunning.Todefeatthistypeofprotection,ratherthanthinkoutsidethebox,youcancompletelycopytheboxandworkinsidethatcopy.

Forexample,ifyouwanttowritememorytoagame,youmustcalltheWriteProcessMemory()function,whichisexportedbykernel32.dll.Whenyoucallthisfunction,itdirectlycallsNtWriteVirtualMemory()fromntdll.dll.GameGuardhooksntdll.NtWriteVirtualMemory()topreventyoufromwritingmemory.ButifNtWriteVirtualMemory()isexportedfrom,say,ntdll_copy.dll,GameGuardwon’thookthatfunction.

Thatmeansyoucancopyntdll.dllanddynamicallyimportallofthefunctionsyouneed,asfollows:

//copyandloadntdllcopyFile("ntdll.dll","ntdll_copy.dll");automodule=LoadLibrary("ntdll_copy.dll");

//dynamicallyimportNtWriteVirtualMemorytypedefNTSTATUS(WINAPI*_NtWriteVirtualMemory)(HANDLE,PVOID,PVOID,ULONG,PULONG);automyWriteVirtualMemory=(_NtWriteVirtualMemory)GetProcAddress(module,"NtWriteVirtualMemory");

//callNtWriteVirtualMemorymyWriteVirtualMemory(process,address,data,length,&writtenlength);

Aftercopyingntdll.dll,thiscodeimportstheNtWriteVirtualMemory()fromthecopywiththenamemyWriteVirtualMemory().Fromthere,thebotcanusethisfunctioninplaceoftheNtWriteVirtualMemory()function.They’reeffectivelythesamecodeinthesamelibrary,justloadedunderdifferentnames.

Copyingafunctionthatanti-cheatsoftwarehooksworksonlyifyoucallthatfunctionatitslowest-levelentrypoint,though.Ifthiscodecopiedkernel32.dllanddynamicallyimportedtheWriteProcessMemory()function,ananti-cheatrootkitwouldstillstopthebot,becausekernel32_copy.dllwouldstillrelyonntdll.NtWriteVirtualMemory()whencallingtheWriteProcessMemory()function.

DefeatingHeuristicsInadditiontoalloftheadvancedclient-sidedetectionmechanismswe’vejustdiscussed,gamecompanieswillemployserver-sideheuristicsthatcandetectbotssimplybymonitoringaplayer’sbehavior.Thesesystemslearntodistinguishbetweenhumanandautonomousplayerbehaviorthroughmachine-learningalgorithms.Theirdecision-makingprocessisofteninternalandincomprehensibletohumans,soit’sdifficulttopinpointexactlywhatfeaturesofgameplayleadtodetection.

Youdon’tneedtoknowhowsuchalgorithmsworktotrickthem;yourbotjustneedstoacthuman.Herearesomecommonbehaviorsthataredistinguishablydifferentbetweenhumansandbots:

IntervalsbetweenactionsManybots perform actions unreasonably fast or at consistent intervals.Bots will seem more human-like if they have a reasonable cooldownperiod between actions. They should also have some form ofrandomization to prevent them from repeating an action at a constantrate.

PathrepetitionBots that farm enemies automatically visit a preprogrammed list oflocations to kill creatures. These waypoint lists are often extremelyaccurate,indicatingeachlocationasanexactpixel.Humans,conversely,moveinlesspredictablewaysandvisitmoreuniquelocationsalongthewaytoafamiliararea.Toreplicatethisbehavior,abotmightwalktoarandomlocationwithinacertainrangeofatargetlocation,ratherthantothetargetlocationitself.Also,ifthebotrandomizestheorderinwhichitvisitstargetlocations,thevarietyofpathsittakeswillincreasefurther.

UnrealisticplaySome botters run their bots in the same location for hundreds ofconsecutivehours, buthumans can’t play a game that long.Encourageyourusers to refrain frombotting formore than eighthours at a timeand warn them that doing the same thing for seven straight days willdefinitelytriggeralarmsinaheuristicsystem.

PerfectaccuracyBotscanhitathousandheadshotsinarowwithoutfiringasingleextrabullet,andtheycanhiteveryskillshotwithconsistentprecision.Butit’svirtuallyimpossibleforahumantodothesame,soasmartbotshouldbeintentionallyinaccurateattimes.

Thesearejustafewexamples,butingeneral,youcansneakpastheuristicchecksifyoujustusecommonsense.Don’ttrytohaveabotdosomethingahumancan’t,anddon’thavethebotdoanysinglethingfortoolong.

ClosingThoughtsGamehackersandgamedevelopersareengagedinaconstantbattleofwits.Hackerswillkeepfindingwaystosubvertdetection,anddeveloperswillkeepfindingbetterwaystodetectthem.Ifyou’redetermined,however,theknowledgeinthischaptershouldhelpyoudefeatanyanti-cheatsoftwareyouencounter.

INDEX

AAbouttextfield,Trainergeneratordialog,9accessingmemory

ininjectedDLL,145–146forwritingandreading,122–124

ActionMessageFormat(AMF),169actorfunctions,216actuation,216,223Addresscolumn

EventPropertiesdialog,55OllyDbgdisassemblerpane,27

addresses,memory.SeememoryaddressesAddressSpaceLayoutRandomization(ASLR),128

bypassingininjectedDLL,146–147bypassinginproduction,128–130disablingforbotdevelopment,128inProcessExplorer,56,57

AdobeAIRhooking,169decode()function,172–173,174–175encode()function,171–172,174–175placinghooks,173–175RTMP,assessing,169–170

AdobeAIR.dll,173–175airlogtool,170alignment

innumericdata,68ofvariables,indatastructures,70–71

ambientlight,adding,190–192

AMF(ActionMessageFormat),169anti-cheatsoftware,245–246

anti-cheatrootkit,defeating,261–262binaryvalidation,defeating,259–261botfootprints,managing,250–256ESEAAnti-Cheattoolkit,247GameGuardtoolkit,248–249heuristics,defeating,262–263PunkBustertoolkit,246–247screenshots,defeating,258signature-baseddetection,evading,256–257VACtoolkit,247–248Wardentoolkit,249–250

anti-crowd-controlhacks,218anti-debuggingtechniques,251,255–256arithmeticinstructions,90–92A*searchalgorithm,234

cost,233creatingnode,234–237creatingpathlist,239–240score,234usesfor,240–241writingsearchfunction,237–239

ASLR.SeeAddressSpaceLayoutRandomization(ASLR)Asm2Clipboardplug-in,42assemblycode

copying,42tracing,32–33viewingandnavigatinginOllyDbg,27–29

assemblylanguage,78.Seealsox86assemblylanguageassemblypatterns,searchingfor,19–21AStarNodeclass,234–236AT&Tsyntax,80

autocombo,219autododge,219autokitebots,244automatichealer,218,225–228,230–232autonomousbots,221–222.Seealsocontroltheory;statemachines

cavebots,241–243complexhypotheticalstatemachine,228–230errorcorrection,230–232healerstatemachine,225–228pathfindingwithsearchalgorithms,232–234warbots,243–244

autoreload,219autosnipebots,244autowallbots,244

Bbanwaves,246BiggerThanscantype,CheatEngine,6binaryarithmeticinstructions,90binaryvalidation,248,259–261bits,EFLAGSregister,84BlueScreenofDeath(BSOD),256bots.Seealsoautonomousbots;extrasensoryperception(ESP)hacks

anti-crowd-controlhacks,218anti-debuggingtechniques,251,255–256automatichealer,218,225–228,230–232detectingdebuggers,251–254detectingvisualcues,205–206disablingASLRfordevelopment,128emulatingkeyboard,211–215footprints,managing,250–256gameupdates,dealingwith,101–104

interceptingnetworktraffic,206–211monitoringmemory,204–205obfuscation,251,255–256sendingpackets,215–217spelltrainers,219

branching,92–94breakpoints,30,34,38Breakpointswindow,OllyDbg,26BSOD(BlueScreenofDeath),256BYTEdatatype,67bytes,machinecode,78

CC++,66callee,94–95caller,94–95callHook()function,154callhooking,153–156.SeealsoAdobeAIRhookingcallingconventions,95

forcallhooks,155__cdecl,95,155__fastcall,95__stdcall,95__thiscall,95,217fortrampolinefunctions,168forVFtablehooks,156–158

CALLinstruction,94–95callstack

overflow,255–256viewing,30x86assemblylanguage,86–88

Callstackwindow,OllyDbg,26

capacityofstd::vector,109castingspells.Seespellscavebots,241–243__cdeclconvention,95,155ChangedValuescantype,CheatEngine,7characters.Seealsoenemies

healthbars,monitoringwithbots,204–205pausingexecutionwhenhealthdrops,39–42playerhealth,findingwithOllyDbg,99–101

chardatatype,67CheatEngine,3,5–6

automaticallylocatingstringaddresseswith,102cheattables,7–8correctaddress,determining,7firstscan,running,6installing,4Luascriptingenvironment,18–22memorymodification,8–11nextscan,running,7pointerscanningwith,14–18scantypes,6std::list,determiningwhetherdataisstoredin,112–113std::map,determiningwhetherdataisstoredin,117trainergenerator,9–11VFtables,78zoomfactor,finding,197

cheattables,CheatEngine,7–8CheatUtilityplug-in,42–43CheckRemoteDebuggerPresent()function,251classes,74–78classinstances,76CloseHandle()function,122,138closingmutexes,59–60

CMPinstruction,92codecaves,134

loadingDLLs,143–146threadhijacking,138–142threadinjection,134–138

codeinjection,133–134bypassingASLRinproduction,128–130DLLs,142–146withthreadhijacking,138–142withthreadinjection,134–138

codepatches,creating,31–32columnconfigurations,ProcessMonitor,51combat,automating,243–244commandlineplug-in,OllyDbg,43–44commandsyntax,x86assemblylanguage,79–81Commentcolumn,OllyDbgdisassemblerpane,28complexhypotheticalstatemachine,228–230conditionalbreakpoints,34,38conditionalstatements,93constantratioofhealth,adjustingfor,230–231control-criticalroutines,timing,254controlflowhacks,31controlflowmanipulation,149–150.SeealsoAdobeAIRhooking;Direct3D

hookingcallhooking,153–156IAThooking,160–165jumphooking,165–169NOPing,150–152VFtablehooking,156–160

controltheory,222combiningwithstatemachines,225complexhypotheticalstatemachine,228–230errorcorrection,230–232

healerstatemachine,225–228controlwindows,OllyDbg,25–26cooldowns,displayingenemy,200–201copyingassemblycode,42copy-on-writeprotection,126corpses,botbehaviortoward,229,240correctaddress,determininginCheatEngine,7CPUwindow,OllyDbg,26–30,40crashingdebuggers,255CreateRemoteThread()function,129,130,134,138CreateToolhelp32Snapshot()function,120,141creaturedata,knowingstructurebehind,106–107criticalgameinformation,displaying,198–201crowd-controlattacks,218cryptographicfunctions,hooking,170CSregister,85C-styleoperators,OllyDbg,34–35custombehaviorsforcavebots,scripting,243

Ddarkenvironments,lightingup,190–192datamodificationinstructions,89datastructures,71–73datatypes,66

classesandVFtables,74–78numericdata,67–69OllyDbg,36stringdata,69–71unions,73–74

DBG_RIPEXCEPTIONhandlers,checkingfor,253debugging.SeealsoOllyDbg

anti-debuggingtechniques,255–256

debugdrivers,checkingfor,254debugstrings,printing,253detectingdebuggers,251–254ProcessMonitor,52–53

__declspec(naked)convention,168decode()function,hooking,172–173,174–175DecreasedValueByscantype,CheatEngine,7DecreasedValuescantype,CheatEngine,7dependencies,DLL,145dependencyloading,160depositor,242destinationoperand,80detection,avoiding.Seeanti-cheatsoftwaredevice->SetRenderState()function,192Dijkstra’salgorithm,233–234Direct3D9,176Direct3Dhooking,175–176.Seealsoextrasensoryperception(ESP)hacks

detectingvisualcuesingames,205–206drawingloop,176–177findingdevices,177–181optionalfixesforstability,184writinghookforEndScene(),182–183writinghookforReset(),183–184

directionallighthacks,190–191disablingASLR,128disassemblerpane,OllyDbg,27–29,42Disassemblycolumn,OllyDbgdisassemblerpane,28dispatchPacket()function,210displaybase,27DLL(dynamiclinklibrary),injecting,142–146DllMain()entrypoint,144–145DLLsoption,ProcessExplorerpane,57

DomainNameSystem(DNS)cachescans,248DOSheader,160–161DrawIndexedPrimitive()function,194,195,196,200drawingloop,Direct3D,176–177DSregister,85dumppane,OllyDbg,29–30DWORDdatatype,67,145–146dynamicallyallocatedmemory,6,11,12dynamiclinklibrary(DLL),injecting,142–146dynamiclure,242–243dynamicstructures,105

std::listclass,110–113std::mapclass,114–118std::stringclass,105–108std::vectorclass,108–110

EEAXregister,81EBPregister,83EBXregister,82ECXregister,82,157EDIregister,83EDXregister,82EFLAGSregister,84,92EIPregister,83,139emulatingkeyboard,211–215enableLightHackDirectional()function,190–191encode()function,hooking,171–172,174–175EndScene()function

jumphooking,178–181stabilityof,184writinghookfor,182–183

endSceneTrampoline()function,181enemies.Seealsoextrasensoryperception(ESP)hacks

cooldowns,displaying,200–201criticalgameinformation,displaying,198–201predictingmovementsof,241texture,changing,195–196

entropy,5,7Environmenttab,ProcessExplorerPropertiesdialog,58errorcorrection,230–232ESEA(E-SportsEntertainmentAssociation),247ESEAAnti-Cheattoolkit,247ESIregister,83ESPhacks.Seeextrasensoryperception(ESP)hacksESPregister,83ESregister,85Euclideandistanceheuristic,236eventclassfilters,ProcessMonitor,51–52eventlog,ProcessMonitor,52–53EventPropertiesdialog,54–55ExactValuescantype,CheatEngine,6exceptionhandlers,checkingfor,253executeprotection,125–128Executeuntilreturnbutton,OllyDbg,25experience-trackingHUD,200exponent,floatdatatype,68expressions,OllyDbg,36–37

accessingmemorycontentswith,36elementsevaluatedby,35–36expressionengine,33–36pausingexecutionwhenhealthofcharacterdrops,39–42pausingexecutionwhennameofplayerisprinted,37–38supporteddatatypes,36

extrasensoryperception(ESP)hacks,189–190

backgroundknowledge,190floorspyhacks,201–202HUDs,198–201lighthacks,190–192loading-screenHUDs,201pick-phaseHUDs,201rangehacks,201wallhacks,192–197zoomhacks,197–198

Ffalsepositives,VACtoolkit,248__fastcallconvention,95feedbackloop,222fileaccesses,inspectinginProcessExplorer,60Filesystemeventclassfilter,52FILO(first-in-last-out),86filters,eventclass,51–52findItem()function,116–117findSequence()function,175first-in-last-out(FILO),86first-personshooter(FPS),xxii,246firstscan,runninginCheatEngine,6flags,processaccess,121floatdatatype,67–68floorspyhacks,201–202fogofwar,189.Seealsoextrasensoryperception(ESP)hacksfootprints,managing,250–256Foundintermodularcallswindow,OllyDbg,40FPS(first-personshooter),xxii,246FPUregisters,29Framecolumn,EventPropertieswindow,54

frames,inDirect3Ddrawingloop,176Freezeinterval,Trainergeneratordialog,9freezing

addresses,8mainthread,141

frontier,233FSregister,85functioncalls,x86assemblylanguage,94–95functionflowchart,OllyFlow,45functionnames,findingforIAThooking,163

GGameActuatorsclass,225gameautomationstatemachine,223–224GameGuardtoolkit,248–249gameupdates,determiningnewaddressesafter,101–104generalregisters,81–82genericmemoryfunctions,123–124getAddressforNOP()function,152GetAsyncKeyState()function,196GetExitCodeThread()function,129GetModuleFileName()function,144GetModuleHandle()function,129–130,134,144,146–147GetSystemTimeAsFileTime()function,258GetThreadContext()function,139,142GetTickCount()function,254GetWindowThreadProcessId()function,120goalstate,238GoTobutton,OllyDbg,25greedybest-firstsearchalgorithm,233–234GSregister,85

guardprotection,126

Hhaltingproblem,250handlemanipulationoptions,ProcessExplorer,59–60handlerfunctions,208handles,56,121,210–211,252Handlesoption,ProcessExplorerpane,57Handleswindow,OllyDbg,26hardwarebreakpoints,checkingfor,252–253hashvalidation,247heads-updisplay(HUD),198–201healerstatemachine,225–228,230–232healthofcharacters

healthbars,monitoringwithbots,204–205healthbarsofenemies,displaying,150–152pausingexecutionupondropin,39–42

heapdata,16heuristics,233

defeating,262–263Euclideandistance,236Manhattandistance,235

Hexdumpcolumn,OllyDbgdisassemblerpane,27–28hiddendata,displaying,198–201Hiddenoption,ProcessExplorerpane,57hooking,42,149,153.SeealsoAdobeAIRhooking;Direct3Dhooking;

extrasensoryperception(ESP)hackscall,153–156detectingvisualcuesingames,205–206IAT,160–165interceptingnetworktraffic,206–211jump,165–169

prewrittenlibraries,169signature-baseddetection,evading,257VFtable,156–160zoomhacks,198

hotkeysPatcheswindow,OllyDbg,32ProcessExplorer,57ProcessMonitor,52fortrainer,settingup,10

hourlyexperience,finding,200HTTP(HyperTextTransferProtocol),169HTTPS(HTTPSecure),169HUD(heads-updisplay),198–201

IIAT(importaddresstable)hooking,160–165IDIVinstruction,92IMAGE_DOS_HEADERstructure,161IMAGE_IMPORT_DESCRIPTORstructure,162IMAGE_OPTIONAL_HEADERstructure,161Imagetab,ProcessExplorerPropertiesdialog,57–58IMAGE_THUNK_DATAstructure,162immediatevalue,80importaddresstable(IAT)hooking,160–165importdescriptors,162IMULarithmeticinstruction,90–91IncreasedValueByscantype,CheatEngine,7IncreasedValuescantype,CheatEngine,7indexregisters,83infiniteloops,causingunavoidable,255in-gameactions,botsfor

anti-crowd-controlhacks,218

automatichealer,218,225–228,230–232emulatingkeyboard,211–215sendingpackets,215–217spelltrainers,219

in-gameevents,logging,50–52instructions,79

arithmetic,90–92branching,92–94datamodification,89functioncalls,94–95jump,92–94

intdatatype,67Intelsyntax,80interrupthandlers,checkingfor,252iterator,120

JjumpHookCallback()function,168jumphooking,165–169,178–181jumpinstructions,x86assemblylanguage,92–94

Kkernel-moderootkit,GameGuardtoolkit,249keyboard,emulating,211–215KEYEVENTF_KEYUPflag,212kiting,222,240–241

Llibraries,hooking,169lighthacks,190–192

listclass,110–111listItemclass,110–111little-endianordering,67loaderlock,144loading-screenHUDs,201LoadLibrary()function,143–144Locationcolumn,EventPropertieswindow,54loggingevents,ProcessMonitor,50–52Logwindow,OllyDbg,25longdatatype,67longlongdatatype,67looting,229,241–243Luascriptingenvironment,CheatEngine,18–22luremode,242

Mmachinecode,78mainloop

Direct3Ddrawingloop,176–177syncingwith,164–165

mana,avoidingwasted,219Manhattandistanceheuristic,235mantissa,floatdatatype,68massivelymultiplayeronlinerole-playinggames(MMORPGs),xxi–xxii,198,

248massiveonlinebattlearena(MOBA),xxii,189,197,201,206memcpy()function,136memory,65–66

classesandVFtables,74–78datastructures,71–73numericdata,67–69stringdata,69–71

unions,73–74memoryaccess

ininjectedDLL,145–146forwritingandreading,122–124

memoryaddresses,4accessingwithOllyDbgexpressions,36correct,determininginCheatEngine,7freezing,8new,determiningaftergameupdates,101–104rebasingatruntime,128–129static,6

memory-basedlighthacks,192memorydump

ofclassdata,76ofcodecave,137ofdatastructures,inspecting,70–71ofnumericdata,inspecting,68–69ofstringdata,inspecting,70

memoryforensics,97–98newaddresses,determiningaftergameupdates,101–104playerhealth,findingwithOllyDbg,99–101purposeofdata,deducing,98–99std::listclass,110–113std::mapclass,114–118std::stringclass,105–108std::vectorclass,108–110

memorymanipulation,119accessingmemory,122–124addressspacelayoutrandomization,128–130memoryprotection,124–128processidentifier,obtaining,120–122

Memorymapwindow,OllyDbg,26memorymodification,8–11memorymonitoringwithbots,204–205

memoryoffset,80memoryonwritebreakpoint,208memorypointer,11memoryprotection,124–128,151memoryscanning,3,98.SeealsoCheatEngine;pointerscanning

basic,4–5importanceof,4memorymodification,8–11newaddresses,determiningaftergameupdates,101–104optimizationofcode,22playerhealth,findingwithOllyDbg,99–101purposeofdata,deducing,98–99

MMORPGs(massivelymultiplayeronlinerole-playinggames),xxi–xxii,198,248

mnemonics,78MOBA(massiveonlinebattlearena),xxii,189,197,201,206modifyingmemoryvalues,8–11Module32First()function,144,174Module32Next()function,144,174Modulecolumn,EventPropertieswindow,54Moduleswindow,OllyDbg,25monitoringmemorywithbots,204–205monsters,kiting,240–241mousemovements,emulating,215,240MOVinstruction,89multiclientpatching,30mutexes,closing,59–60

Nnamedpipes,locating,60nameofspecificplayer,pausingexecutionwhenprinted,37–38Nameswindow,OllyDbg,29

nearcalls,153–154nearfunctioncall,39.NETprocesses,59Networkeventclassfilter,52newaddresses,determiningaftergameupdates,101–104nextscan,runninginCheatEngine,7nodes,233,234–238no-operation(NOP)commands,31,32NOPing,150–152

lighthacks,192zoomhacks,197–198

NtQueryVirtualMemory()function,246,257,259NtWriteVirtualMemory()function,261–262nullterminator,70numericdatatypes,67–69numericoperators,OllyDbg,34–35

Oobfuscation,251,255–256observinggameevents

detectingvisualcues,205–206interceptingnetworktraffic,206–211monitoringmemory,204–205

obstacles,searchesdisruptedby,233–234offset,54OllyDbg,23–24

assemblycode,27–29,32–33callstack,viewing,30codepatches,creating,31–32commandlinefor,43–44controlwindows,25–26CPUwindow,26–30

crashingdebuggers,255dealingwithgameupdates,104debuggerbuttonsandfunctions,25expressionengine,33–37memory,viewingandsearching,29–30memorydumpofnumericdata,68–69memorydumpofstringdata,70packetparser,finding,207–208Patcheswindow,31–32patchingif()statements,46–47pausingexecutionwhenhealthofcharacterdrops,39–42pausingexecutionwhennameofplayerisprinted,37–38plug-ins,42–46registercontents,viewingandediting,29Runtracewindow,32–33supporteddatatypes,36translatingcodecaveassemblytoshellcode,135–136userinterface,24–26zoomlimitationcode,finding,198

OllyFlowplug-in,45–46opcodes,78OpenProcess()function,121–122OpenThread()function,142operands

binaryarithmeticinstructions,90IDIVinstruction,92MOVinstruction,89syntax,80–81unaryarithmeticinstructions,90

operations,79operators,usinginOllyDbgexpressionengine,34–35optimizingmemorycode,22ordering,little-endian,67

orderofvariables,indatastructures,70–71OutputDebugString()function,253

Ppackets

intercepting,206–211sending,215–217

packing,251padding,68pageprotection,125–126pages,124parsingpackets,206–211Patcheswindow,OllyDbg,26,31–32patching,multiclient,30patchingif()statements,46–47Pathcolumn,EventPropertiesdialog,55pathfindingwithsearchalgorithms,232–234.SeealsoA*searchalgorithmpathlist,A*searchalgorithm,239–240Pausebutton,OllyDbg,25pausingexecution,37–38,39–42pausingthreads,184PEB(processenvironmentblock)structure,146PeekMessage()function,184PEheader,160–161pick-phaseHUDs,201PID(processidentifier),120–122pipes,locatingnamed,60Playbutton,OllyDbg,25playerhealth,findingwithOllyDbg,99–101playerversusplayer(PvP)combat,243–244plug-ins,OllyDbg,42–46

pointerchains,11–12pointerpath,11PointerscannerScanoptionsdialog,CheatEngine,14–16pointerscanning,11

basicsof,12–14withCheatEngine,14–18pointerchains,11–12rescanning,17–18

Pong,46–47Popuptraineronkeypressfield,Trainergeneratordialog,9predictingenemymovements,241prewrittenhookinglibraries,169printf()call,72,73–74,75printingdebugstrings,253Process32First()function,120Process32Next()function,120–121processaccessflags,121PROCESS_ALL_ACCESSflag,121Processandthreadactivityeventclassfilter,52PROCESS_CREATE_THREADflag,121processenvironmentblock(PEB)structure,146ProcessExplorer,49–50,55–56

configuringcolors,56handlemanipulationoptions,59–60hotkeys,57Propertiesdialog,57–59userinterfaceandcontrols,56–57

processhandles,obtaining,121processidentifier(PID),120–122processInput()function,215–216processKeyboardInput()function,216ProcessMonitor,49–50

configuringcolumnsin,51

debugging,53–55eventclassfilters,51–52high-scorefile,finding,55hotkeys,52inspectingeventsineventlog,52–53loggingin-gameevents,50–52

ProcessMonitorFilterdialog,50Processnamefield,Trainergeneratordialog,9processNextPacket()function,210processorregisters,81–86Processprofilingeventclassfilter,52PROCESS_VM_OPERATIONflag,121,122PROCESS_VM_READflag,121PROCESS_VM_WRITEflag,121Propertiesdialog,ProcessExplorer,57–59protection,memory,124–128,151PunkBustertoolkit,246–247,257purposeofdata,deducing,98–99PvP(playerversusplayer)combat,243–244

Rrangehacks,201readingfromgamememory,119

accessingmemory,122–124addressspacelayoutrandomization,128–130memoryprotection,124–128processidentifier,obtaining,120–122

ReadProcessMemory()function,122–124readprotection,125–128RealTimeMessagingProtocol(RTMP)

assessing,169–170decode()function,hooking,172–173,174–175

encode()function,hooking,171–172,174–175interceptingpackets,207

real-timestrategy(RTS),xxii,197,201,206,243rebasingaddressesatruntime,128–129reconnaissance,49–50

ProcessExplorer,55–60ProcessMonitor,50–55

recv()function,207–208red-blacktree,114–115Referenceswindow,OllyDbg,26,28–29,40,100refiller,242registers,processor,81–86registerspane,OllyDbg,29Registryeventclassfilter,51Rescanpointerlistwindow,CheatEngine,17–18responsivehacks,203

anti-crowd-controlhacks,218automatichealer,218,225–228,230–232detectingvisualcues,205–206emulatingkeyboard,211–215interceptingnetworktraffic,206–211monitoringmemory,204–205sendingpackets,215–217spelltrainers,219

rootkitsdefeatinganti-cheat,261–262GameGuardtoolkit,248–249

rootnode,113–114RTMP.SeeRealTimeMessagingProtocolRTS(real-timestrategy),xxii,197,201,206,243runtimeflexibility,229Runtracewindow,OllyDbg,26,32–33

SSBD.Seesignature-baseddetection(SBD)scancode,214scantypes,CheatEngine,6scanvalue,4score,234screenshots,247,258scriptingcustombehaviorsforcavebots,243scriptingengine,CheatEngine,18–22searchalgorithms,232–234.SeealsoA*searchalgorithmSecuritytab,ProcessExplorerPropertiesdialog,58segmentregisters,84–86send()function,216–217sendingpackets,215–217SendInput()function,211–212,215SendMessage()function,213–215sensors,ofasystem,222Set/Changehotkeyscreen,CheatEngine,10SetLight()memberfunction,192SetProcessIsCritical()function,256shellcode,134,135–136,138–141shortdatatype,67sign,floatdatatype,68signature-baseddetection(SBD)

ESEAAnti-Cheattoolkit,247evading,256–257PunkBustertoolkit,246–247

signatures,246single-instancelimitation,59–60skillshots,232Sleep()function,164–165,227

SmallerThanscantype,CheatEngine,6sourceoperand,80Sourcewindow,OllyDbg,26spawningthreads,129spells

anti-crowd-controlhacks,218complexhypotheticalstatemachine,228–230spelltrainers,219

SSregister,85stackframe,87–89stackoverflow,255–256stackpane,OllyDbg,30stacktrace,ProcessMonitor,54–55statemachines,223–224

automatedhealer,225–228combiningwithcontroltheory,225complexhypothetical,228–230errorcorrection,230–232Luafunctions,adding,229–230runtimeflexibility,229

staticaddresses,6__stdcallconvention,95std::listclass,110–113std::mapclass,114–118std::stringclass,105–108std::vectorclass,108–110Stepintobutton,OllyDbg,25Stepoverbutton,OllyDbg,25stochasticsystems,230stringdata,21,69–71,100–101stringoperators,OllyDbg,35Stringstab,ProcessExplorerPropertiesdialog,58

structmemberalignment,71structures,data,71–73subregisters,83SuspendThread()function,142,184syncingwithgamethreads,164–165systems,controllingbehaviorof,222

Ttargets,selecting,240TCP/IPtab,ProcessExplorerPropertiesdialog,58TEB(threadenvironmentblock),146templates

forchangingmemoryprotection,127memoryaccessfunctions,123–124,145–146

TESTinstruction,92textstrings,21,69–71,100–101textureofenemies,changing,195–196__thiscallconvention,95,156–158,217Thread32First()function,141Thread32Next()function,141threadenvironmentblock(TEB),146threads

hijacking,138–142injection,134–138spawning,129

Threadstab,ProcessExplorerPropertiesdialog,58Threadswindow,OllyDbg,26thunks,162–163timingcontrol-criticalroutines,254Titlefield,Trainergeneratordialog,9togglingz-buffering,195

Traceintobutton,OllyDbg,25Traceoverbutton,OllyDbg,25tracingwithOllyDbg,32–33,39–42trainergenerator,CheatEngine,9–11trampolinefunctions,165–168,181traversals

IAThooking,162VFtables,156

Uunaryarithmeticinstructions,90unavoidableinfiniteloops,causing,255UnchangedValuescantype,CheatEngine,7unions,73–74Unixsyntax,80UnknownInitialValuescantype,CheatEngine,6updates,determiningnewaddressesafter,101–104userinterface,ProcessExplorer,56–57user-moderootkit,GameGuardtoolkit,248–249

VVACtoolkit,247–248ValueBetweenscantype,CheatEngine,6ValueTypedirective,CheatEngine,6VF(virtualfunction)tables

classinstancesand,76–78findingDirect3Ddevices,177–181hooking,156–160,182–183traversals,156

VirtualAllocEx()function,136–137,138virtualfunctions,classeswith,75–76

VirtualProtectEx()function,126–128VirtualProtect()function,127

WWaitForSingleObject()function,129,138wallhacks,192

creatingforDirect3D,194–197renderingwithz-buffering,193–194

warbots,243–244Wardentoolkit,249–250waypoints,222,229wchar_tdatatype,67windowhandle,fetching,120Windowswindow,OllyDbg,26WM_CHARmessages,213–214WORDdatatype,67WriteProcessMemory()function,122–124,136–137,138writeprotection,125–128writingtogamememory,119

accessingmemory,122–124addressspacelayoutrandomization,128–130codecaves,136–137memoryprotection,124–128processidentifier,obtaining,120–122

Xx86assemblylanguage,78–79

arithmeticinstructions,90–92branchinginstructions,92–94callstack,86–88commandsyntax,79–81

datamodificationinstructions,89functioncalls,94–95jumpinstructions,92–94NOPing,150–152processorregisters,81–86

x86Windowsmemoryprotectionattributes,125–126

Zz-buffering,192–195zoomfactor,197zoomhacks,197–198

Footnotes

Chapter4:FROMCODETOMEMORY:AGENERALPRIMER1.RandallHyde’sTheArtofAssemblyLanguage,2ndedition(NoStarchPress,2010)isawonderfulbookthatcanteachyoueverythingthereistoknowaboutassembly.2.Eachcommandmustfitwithin15bytes.Mostcommandsare6orfewer.3.Thereisalsoanunsignedmultiplicationinstruction,MUL,whichonlyworkswithasingleoperand.4.JustasMUListoIMUL,DIVistheunsignedcounterparttoIDIV.

RESOURCESVisithttps://www.nostarch.com/gamehacking/forresources,errata,andotherinformation.

Moreno-nonsensebooksfrom NOSTARCHPRESS

BLACKHATPYTHONPythonProgrammingforHackersandPentestersbyJUSTINSEITZ

DEC2014,192PP.,$34.95ISBN978-1-59327-590-7

THECARHACKER’SHANDBOOKAGuideforthePenetrationTesterbyCRAIGSMITH

MAR2016,304PP.,$49.95ISBN978-1-59327-703-1

THEIDAPROBOOK,2NDEDITIONTheUnofficialGuidetotheWorld’sMostPopularDisassemblerbyCHRISEAGLE

JUL2011,672PP.,$69.95ISBN978-1-59327-289-0

PRACTICALFORENSICIMAGINGSecuringDigitalEvidencewithLinuxToolsbyBRUCENIKKEL

FALL2016,256PP.,$49.95

ISBN978-1-59327-793-2

IOSAPPLICATIONSECURITYTheDefinitiveGuideforHackersandDevelopersbyDAVIDTHIEL

FEB2016,296PP.,$49.95ISBN978-1-59327-601-0

PRACTICALMALWAREANALYSISTheHands-OnGuidetoDissectingMaliciousSoftwarebyMICHAELSIKORSKIandANDREWHONIG

FEB2012,800PP.,$59.95ISBN978-1-59327-290-6

800.420.7240OR415.863.9900|SALES@NOSTARCH.COM|WWW.NOSTARCH

GetInsidetheGameYoudon’tneedtobeawizardtotransformagameyoulikeintoagameyoulove.ImagineifyoucouldgiveyourfavoritePCgameamoreinformativeheads-updisplayorinstantlycollectallthatlootfromyourlatestepicbattle.

BringyourknowledgeofWindows-baseddevelopmentandmemorymanagement,andGameHackingwillteachyouwhatyouneedtobecomeatruegamehacker.Learnthebasics,likereverseengineering,assemblycodeanalysis,programmaticmemorymanipulation,andcodeinjection,andhoneyournewskillswithhands-onexamplecodeandpracticebinaries.

Levelupasyoulearnhowto:

ScanandmodifymemorywithCheatEngine

ExploreprogramstructureandexecutionflowwithOllyDbg

LogprocessesandpinpointusefuldatafileswithProcessMonitor

ManipulatecontrolflowthroughNOPing,hooking,andmore

Locateanddissectcommongamememorystructures

You’llevendiscoverthesecretsbehindcommongamebots,including:

Extrasensoryperceptionhacks,suchaswallhacksandheads-updisplays

Responsivehacks,suchasautohealersandcombobots

Botswithartificialintelligence,suchascavewalkersandautomaticlooters

Gamehackingmightseemlikeblackmagic,butitdoesn’thavetobe.Onceyouunderstandhowbotsaremade,you’llbebetterpositionedtodefendagainsttheminyourowngames.JourneythroughtheinnerworkingsofPCgameswithGameHacking,andleavewithadeeperunderstandingofbothgamedesignandcomputersecurity.

AbouttheAuthorNickCanowrotehisfirstscriptsforopensourcegameserverswhenhewas12andhasbeenapartofthegame-hackingcommunityeversince.Hehasyearsofexperienceindetectinganddefendingagainstmalware,andadvisesdevelopersanddesignersonbestpracticestoprotecttheirgamesagainstbots.Nickhasspokenabouthisresearchandtoolsatmanyconferences.

WARNING!Thisbookdoesnotcondonepiracy,violatingtheDMCA,infringingcopyright,orbreakingin-gameTermsofService.Gamehackershavebeenbannedfromgamesforlife,suedformillionsofdollars,andevenjailedfortheirwork.

THEFINESTINGEEKENTERTAINMENT™www.nostarch.com

top related