unit memscan; {$MODE Delphi} { This unit will hold the class object used to control scanning The old scanning routines will be moved out of cefuncproc and made object oriented into this class Special care should be taken to add multithreaded scanning routines } interface {$ifdef windows} uses windows, FileUtil, LCLIntf,sysutils, classes,ComCtrls,dialogs, NewKernelHandler,math, SyncObjs, windows7taskbar,SaveFirstScan, savedscanhandler, autoassembler, symbolhandler, CEFuncProc,shellapi, customtypehandler,lua,lualib,lauxlib, LuaHandler, fileaccess, groupscancommandparser, commonTypeDefs, LazUTF8, forms; {$define customtypeimplemented} {$endif} {$ifdef unix} uses sysutils, unixporthelper, customtypehandler, commonTypeDefs, classes, syncobjs, math, groupscancommandparser, NewKernelHandler, strutils, savedscanhandler; {$endif} type TMemScanGuiUpdateRoutine=procedure(sender: TObject; totaladdressestoscan: qword; currentlyscanned: qword; foundcount: qword) of object; TCheckRoutine=function(newvalue,oldvalue: pointer):boolean of object; TMultiAOBCheckRoutine=function(newvalue: pointer; mabsindex: integer):boolean of object; TStoreResultRoutine=procedure(address: ptruint; oldvalue: pointer) of object; TFlushRoutine=procedure of object; Tscanregionpreference=(scanDontCare, scanExclude, scanInclude); TAddresses=array of PtrUInt; type TMemScan=class; TScanController=class; TScanner=class; TGroupData=class //separate for each scanner object private fblocksize: integer; fAlignsize: integer; outoforder: boolean; outoforder_aligned: boolean; groupdata: array of record wildcard: boolean; //if set this is just used for offset calculations for non ooo scans offset: integer; //filled during out of order scans to determine if multiple items have been found (If you do OOO scans and use byte and 4 byte together, define byte later...) vartype: TVariableType; customtype: TCustomtype; value: string; widevalue: widestring; valuei: qword; valuef: double; minfvalue: double; maxfvalue: double; floataccuracy: integer; bytesize: integer; end; groupdatalength: integer; //saves a getLenghth lookup call fscanner: TScanner; function ByteScan(value: byte; buf: Pbytearray; var startoffset: integer): boolean; function WordScan(value: word; buf: pointer; var startoffset: integer): boolean; function DWordScan(value: dword; buf: pointer; var startoffset: integer): boolean; function QWordScan(value: qword; buf: pointer; var startoffset: integer): boolean; function SingleScan(minf,maxf: double; buf: pointer; var startoffset: integer): boolean; function DoubleScan(minf,maxf: double; buf: pointer; var startoffset: integer): boolean; function CustomScan(ct: Tcustomtype; value: integer; buf: pointer; var startoffset: integer): boolean; function CustomScanFloat(ct: Tcustomtype; minf, maxf: single; buf: pointer; var startoffset: integer): boolean; function StringScan(st: pchar; buf: Pbytearray; var startoffset: integer): boolean; function WideStringScan(st: pwidechar; buf: Pbytearray; var startoffset: integer): boolean; function testString(buf: PChar; ts: pchar): boolean; function testWideString(buf: PWideChar; ts: pwidechar): boolean; public constructor create(parameters: string; scanner: TScanner); function compareblock(newvalue,oldvalue: pointer): boolean; //Check if the values are at their specific offsets function compareblock_outoforder(newvalue,oldvalue: pointer): boolean; //Scan the blocks for the values //use genericsaveresult for saving the original memory //use a custom address file for property blocksize: integer read fblocksize; property alignsize: integer read fAlignsize; end; Tscanfilewriter=class(tthread) { workerthread: This class is used because it is more efficient to write only one file at a time. It will aquire a critical section when writing a file to acomplish that also, not that it matters much, since you eventually have to wait for the write anyhow, a double buffered result list so it can keep scanning while the list is being saved. } private scancontroller: TScanController; scanner: TScanner; addressfile: TFilestream; memoryfile: TFilestream; // cAddressFile,cMemoryFile: TCompressionStream; addressbuffer: pointer; memorybuffer: pointer; addressSize: dword; memorySize: dword; datawritten: tevent; //event that is set when the thread has finished writing dataavailable:tevent; //event that is set when there is a buffer to save public writeError: boolean; //gets set if an exception happened errorString: string; //exception description procedure execute; override; procedure writeresults(addressbuffer,memorybuffer: pointer; addressSize,memorySize: dword); //writes the results of address and memory procedure flush; constructor create(scanner: TScanner; scancontroller:TScanController; addressfile,memoryfile:TFileStream); destructor destroy; override; end; TScanner=class(tthread) { The scanner class will scan a specified range of memory } private CheckRoutine: TCheckRoutine; StoreResultRoutine: TStoreResultRoutine; FlushRoutine: TFlushRoutine; //pointer to routine used to flush the buffer, generic, string, etc... scanWriter: Tscanfilewriter; savedscanhandler: Tsavedscanhandler; scandir: string; previousmemoryfile: TFilestream; found :dword; maxfound: dword; //the number of entries before flushing is demanded value,value2: int64; svalue,svalue2: single; dvalue,dvalue2: double; minsvalue,maxsvalue: single; mindvalue,maxdvalue: double; floataccuracy: integer; //number of digits after the decimal seperator CurrentAddressBuffer: pointer; CurrentFoundBuffer: pointer; //generic buffer that points to where the memory can be found SecondaryFoundBuffer: pointer; //current gets swapped with these ones after a write SecondaryAddressBuffer: pointer; //binary scan bitmask: int64; //contains the bit string where * is replaced with a 0 andmask: int64; //contains the bit string where 1 and 0 is 1 and * is 0 binaryresults: array [0..7] of boolean; //after a byte is checked this contains which startbits match //array of bytes abs_arraylength: integer; //optimization so no need to call length() abs_arraytofind: TBytes; nibbleSupport: boolean; //multi array of byte. This is for vtByteArrays. vtByteArrays can only be used together with the "OnlyOne" variable. Found in this case means all elements have been found mabs: array of record arraytofind: TBytes; arraylength: integer; foundaddress: ptruint; //0 if not found, anything else if found. end; mabs_arraylength: integer; //optimization //all typesmatch: array [vtByte..vtDouble] of boolean; //will get set depending if that type matches the current address or not customtypesmatch: array of boolean; customtypecount: integer; //some variables to hold what types to scan for all (faster then checking the set) allByte: boolean; allWord: boolean; allDword: boolean; allQword: boolean; allFloat: boolean; allDouble: boolean; allCustom: boolean; floatscanWithoutExponents: boolean; inverseScan: boolean; //groupdata groupdata: TGroupData; //custom data currentAddress: PtrUInt; //check routines: function ByteExact(newvalue,oldvalue: pointer): boolean; function ByteBetween(newvalue,oldvalue: pointer): boolean; function ByteBetweenPercentage(newvalue,oldvalue: pointer): boolean; function ByteBiggerThan(newvalue,oldvalue: pointer): boolean; function ByteSmallerThan(newvalue,oldvalue: pointer): boolean; function ByteIncreasedValue(newvalue,oldvalue: pointer): boolean; function ByteIncreasedValueBy(newvalue,oldvalue: pointer): boolean; function ByteIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function ByteDecreasedValue(newvalue,oldvalue: pointer): boolean; function ByteDecreasedValueBy(newvalue,oldvalue: pointer): boolean; function ByteDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function ByteChanged(newvalue,oldvalue: pointer): boolean; function ByteUnChanged(newvalue,oldvalue: pointer): boolean; function WordExact(newvalue,oldvalue: pointer): boolean; function WordBetween(newvalue,oldvalue: pointer): boolean; function WordBetweenPercentage(newvalue,oldvalue: pointer): boolean; function WordBiggerThan(newvalue,oldvalue: pointer): boolean; function WordSmallerThan(newvalue,oldvalue: pointer): boolean; function WordIncreasedValue(newvalue,oldvalue: pointer): boolean; function WordIncreasedValueBy(newvalue,oldvalue: pointer): boolean; function WordIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function WordDecreasedValue(newvalue,oldvalue: pointer): boolean; function WordDecreasedValueBy(newvalue,oldvalue: pointer): boolean; function WordDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function WordChanged(newvalue,oldvalue: pointer): boolean; function WordUnChanged(newvalue,oldvalue: pointer): boolean; function DWordExact(newvalue,oldvalue: pointer): boolean; function DWordBetween(newvalue,oldvalue: pointer): boolean; function DWordBetweenPercentage(newvalue,oldvalue: pointer): boolean; function DWordBiggerThan(newvalue,oldvalue: pointer): boolean; function DWordSmallerThan(newvalue,oldvalue: pointer): boolean; function DWordIncreasedValue(newvalue,oldvalue: pointer): boolean; function DWordIncreasedValueBy(newvalue,oldvalue: pointer): boolean; function DWordIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function DWordDecreasedValue(newvalue,oldvalue: pointer): boolean; function DWordDecreasedValueBy(newvalue,oldvalue: pointer): boolean; function DWordDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function DwordChanged(newvalue,oldvalue: pointer): boolean; function DwordUnChanged(newvalue,oldvalue: pointer): boolean; function QWordExact(newvalue,oldvalue: pointer): boolean; function QWordBetween(newvalue,oldvalue: pointer): boolean; function QWordBetweenPercentage(newvalue,oldvalue: pointer): boolean; function QWordBiggerThan(newvalue,oldvalue: pointer): boolean; function QWordSmallerThan(newvalue,oldvalue: pointer): boolean; function QWordIncreasedValue(newvalue,oldvalue: pointer): boolean; function QWordIncreasedValueBy(newvalue,oldvalue: pointer): boolean; function QWordIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function QWordDecreasedValue(newvalue,oldvalue: pointer): boolean; function QWordDecreasedValueBy(newvalue,oldvalue: pointer): boolean; function QWordDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function QWordChanged(newvalue,oldvalue: pointer): boolean; function QwordUnChanged(newvalue,oldvalue: pointer): boolean; function SingleExact(newvalue,oldvalue: pointer): boolean; function SingleBetween(newvalue,oldvalue: pointer): boolean; function SingleBetweenPercentage(newvalue,oldvalue: pointer): boolean; function SingleBiggerThan(newvalue,oldvalue: pointer): boolean; function SingleSmallerThan(newvalue,oldvalue: pointer): boolean; function SingleIncreasedValue(newvalue,oldvalue: pointer): boolean; function SingleIncreasedValueBy(newvalue,oldvalue: pointer): boolean; function SingleIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function SingleDecreasedValue(newvalue,oldvalue: pointer): boolean; function SingleDecreasedValueBy(newvalue,oldvalue: pointer): boolean; function SingleDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function SingleChanged(newvalue,oldvalue: pointer): boolean; function singleUnChanged(newvalue,oldvalue: pointer): boolean; function DoubleExact(newvalue,oldvalue: pointer): boolean; function DoubleBetween(newvalue,oldvalue: pointer): boolean; function DoubleBetweenPercentage(newvalue,oldvalue: pointer): boolean; function DoubleBiggerThan(newvalue,oldvalue: pointer): boolean; function DoubleSmallerThan(newvalue,oldvalue: pointer): boolean; function DoubleIncreasedValue(newvalue,oldvalue: pointer): boolean; function DoubleIncreasedValueBy(newvalue,oldvalue: pointer): boolean; function DoubleIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function DoubleDecreasedValue(newvalue,oldvalue: pointer): boolean; function DoubleDecreasedValueBy(newvalue,oldvalue: pointer): boolean; function DoubleDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function DoubleChanged(newvalue,oldvalue: pointer): boolean; function DoubleUnChanged(newvalue,oldvalue: pointer): boolean; function AllExact(newvalue,oldvalue: pointer):boolean; //check byte,word,dword,qword,single and float function AllBetween(newvalue,oldvalue: pointer): boolean; function AllBetweenPercentage(newvalue,oldvalue: pointer): boolean; function AllBiggerThan(newvalue,oldvalue: pointer): boolean; function AllSmallerThan(newvalue,oldvalue: pointer): boolean; function AllIncreasedValue(newvalue,oldvalue: pointer): boolean; function AllIncreasedValueBy(newvalue,oldvalue: pointer): boolean; function AllIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function AllDecreasedValue(newvalue,oldvalue: pointer): boolean; function AllDecreasedValueBy(newvalue,oldvalue: pointer): boolean; function AllDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function AllChanged(newvalue,oldvalue: pointer): boolean; function AllUnchanged(newvalue,oldvalue: pointer): boolean; function CustomExact(newvalue,oldvalue: pointer): boolean; function CustomBetween(newvalue,oldvalue: pointer): boolean; function CustomBetweenPercentage(newvalue,oldvalue: pointer): boolean; function CustomBiggerThan(newvalue,oldvalue: pointer): boolean; function CustomSmallerThan(newvalue,oldvalue: pointer): boolean; function CustomIncreasedValue(newvalue,oldvalue: pointer): boolean; function CustomIncreasedValueBy(newvalue,oldvalue: pointer): boolean; function CustomIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function CustomDecreasedValue(newvalue,oldvalue: pointer): boolean; function CustomDecreasedValueBy(newvalue,oldvalue: pointer): boolean; function CustomDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function CustomChanged(newvalue,oldvalue: pointer): boolean; function CustomUnChanged(newvalue,oldvalue: pointer): boolean; function CustomFloatExact(newvalue,oldvalue: pointer): boolean; function CustomFloatBetween(newvalue,oldvalue: pointer): boolean; function CustomFloatBetweenPercentage(newvalue,oldvalue: pointer): boolean; function CustomFloatBiggerThan(newvalue,oldvalue: pointer): boolean; function CustomFloatSmallerThan(newvalue,oldvalue: pointer): boolean; function CustomFloatIncreasedValue(newvalue,oldvalue: pointer): boolean; function CustomFloatIncreasedValueBy(newvalue,oldvalue: pointer): boolean; function CustomFloatIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function CustomFloatDecreasedValue(newvalue,oldvalue: pointer): boolean; function CustomFloatDecreasedValueBy(newvalue,oldvalue: pointer): boolean; function CustomFloatDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; function CustomFloatChanged(newvalue,oldvalue: pointer): boolean; function CustomFloatUnChanged(newvalue,oldvalue: pointer): boolean; function ArrayOfBytesExact_NibbleWildcardSupport(newvalue: pointer; mabsindex: integer):boolean; function ArrayOfBytesExact(newvalue: pointer; mabsindex: integer):boolean; //following types only have exact: Array of byte, binary and string function ArrayOfByteExact_NibbleWildcardSupport(newvalue,oldvalue: pointer):boolean; function ArrayOfByteExact(newvalue,oldvalue: pointer):boolean; function BinaryExact(newvalue,oldvalue: pointer):boolean; function CaseSensitiveAnsiStringExact(newvalue,oldvalue: pointer):boolean; function CaseInsensitiveAnsiStringExact(newvalue,oldvalue: pointer):boolean; function CaseSensitiveUnicodeStringExact(newvalue,oldvalue: pointer):boolean; function CaseInsensitiveUnicodeStringExact(newvalue,oldvalue: pointer):boolean; //save macthing address routines: procedure GenericSaveResult(address: ptruint; oldvalue: pointer); //only use as last resort : Call to copymemory just to store one entry procedure allSaveResult(address: ptruint; oldvalue: pointer); procedure binarySaveResult(address: ptruint; oldvalue: pointer); procedure groupSaveResult(address: ptruint; oldvalue: pointer); procedure arrayOfByteSaveResult(address: ptruint; oldvalue: pointer); procedure ByteSaveResult(address: ptruint; oldvalue: pointer); procedure WordSaveResult(address: ptruint; oldvalue: pointer); procedure DWordSaveResult(address: ptruint; oldvalue: pointer); procedure QWordSaveResult(address: ptruint; oldvalue: pointer); procedure SingleSaveResult(address: ptruint; oldvalue: pointer); procedure DoubleSaveResult(address: ptruint; oldvalue: pointer); //flush routines procedure genericFlush; //generic routine for flushing the buffer procedure stringFlush; //don't save the memory procedure binaryFlush; //don't save memory AND use foundaddressb for results procedure groupFlush; //don't save memory procedure allFlush; procedure configurescanroutine; //parses scanvalue1,2 according to the VariableType and scanoptions procedure FirstScanmem(base:ptruint; buffer: pointer; size: integer); //routine that gets a buffer and saves all the results it finds in the buffer (For firstscan) procedure FirstNextScanmem(base:ptruint; buffer,oldbuffer: pointer; size: integer); procedure nextnextscanmemall(addresslist: pointer; oldmemory: pointer; chunksize: integer); procedure nextnextscanmembinary(addresslist: pointer; chunksize: integer); procedure nextnextscanmem(addresslist: pointer; oldmemory: pointer; chunksize: integer); procedure firstscan; //copy the given range to the memory region procedure firstnextscan; //routine used when the results list contains nothing but a indicator a unknown scan was done procedure nextnextscan; //routine used when the results list contains addresses public OwningScanController: TScanController; Addressfile: TFilestream; //tempscandir+'Addresses'+ThreadID.TMP' MemoryFile: TFileStream; //tempscandir+'Memory'+ThreadID.TMP' Addressfilename: string; MemoryFilename: string; roundingtype: TRoundingtype; hexadecimal: boolean; binaryStringAsDecimal: boolean; PreviousOffsetCount: integer; //holds the offsecount of the previous scan (for calculating the entry position) unicode: boolean; caseSensitive: boolean; percentage: boolean; fastscanalignsize: integer; fastscanmethod: TFastScanMethod; fastscandigitcount: integer; variablesize: integer; scanvalue1,scanvalue2: string; widescanvalue1: widestring; compareToSavedScan: boolean; savedscanname: string; scanOption: TScanOption; variableType: TVariableType; customType: TCustomType; scanType: TScanType; //defines if it's a firstscan or next scan. (newscan is ignored) useNextNextscan: boolean; //determines to use the nextNextScan or firstNextScan //thread controlling variables: isdone: boolean; //will get set to true when the thread finishes normally haserror: boolean; errorstring: string; //region related scans: //startregion and stopregion _startregion: integer; _stopregion: integer; maxregionsize: dword; //max size of buffer to be allocated when not unknown scan //recreated memory region list for this specific range, can be used to see which regions where only half read memRegions: TMemoryregions; memRegionPos: integer; startaddress: PtrUint; //specific start for this this thread stopaddress: PtrUint; //specific stop for this thread, if not fastscan and another thread continue from here, may add some overlopping bytes //exact address scans: startentry: qword; //index in the address list stopentry: qword; //" " //general: scanned: qword; //total memory/addresses scanned by this routine totalfound: dword; OnlyOne: boolean; AddressFound: PtrUInt; scannernr: integer; procedure execute; override; constructor create(suspended: boolean; scandir: string); destructor destroy; override; end; TScanController=class(tthread) { The ScanController will configure the scanners and wait till they are done, mainly a idle thread } private threadcount: integer; addressFile: TFileStream; memoryFile: TFileStream; savescannerresults: boolean; //tells the epilogue to save the results to addressfile and memoryfile isdoneEvent: TEvent; //gets set when the scan has finished isReallyDoneEvent: TEvent; //gets set when the results have been completely written procedure updategui; procedure errorpopup; procedure firstScan; procedure nextScan; procedure firstnextscan; //rotine used when the results list contains nothing but a indicator a unknown scan was done procedure nextnextscan; //routine used when the results list contains addresses procedure fillVariableAndFastScanAlignSize; //fills in the variablesize and fastscanalignsizevariables, used for firstscan and firstnextscan public OwningMemScan: TMemScan; resultsaveCS: tcriticalsection; //critical section the scanfilewriter objects use to determine if there is another scanthread writing scannersCS: TCriticalSection; //Critican section used aty the end of the scancontroller thread, when the scanners are being destroyed scanners: array of tscanner; totalAddresses: qword; //how many addresses it will have to scan scanWritable: Tscanregionpreference; scanExecutable: Tscanregionpreference; scanCopyOnWrite: Tscanregionpreference; roundingtype: TRoundingType; hexadecimal: boolean; binaryStringAsDecimal: boolean; fastscan: boolean; fastscanalignment: integer; fastscanmethod: TFastscanmethod; fastscandigitcount: integer; unicode: boolean; casesensitive: boolean; percentage: boolean; fastscanalignsize: integer; variablesize: integer; scanvalue1,scanvalue2: string; startaddress: ptruint; //start for the whole scan stopaddress: ptruint; //stop of the whole scan compareToSavedScan: boolean; savedscanname: string; scanOption: TScanOption; variableType: TVariableType; customType: TCustomType; scanType: TScanType; //defines if it's a firstscan or next scan. (newscan is ignored) //memregion info memregion: TMemoryregions; //scanners have access to this, but make sure to NOT WRITE it memRegionPos: Integer; //thread controlling variables: isdone: boolean; //will get set to true when the thread finishes normally haserror: boolean; errorstring: string; //messages notifywindow: thandle; notifymessage: integer; //OnlyOne vars OnlyOne: boolean; FoundSomething: Boolean; AddressFound: ptruint; AddressesFound: TAddresses; //for multi aob scans floatscanWithoutExponents: boolean; inverseScan: boolean; procedure execute; override; constructor create(suspended: boolean); destructor destroy; override; end; TMemScan=class { Configures the gui and related objects and launch TScanner objects with those objects } private {$ifndef lowmemoryusage} previousMemoryBuffer: pointer; {$endif} scanController: TScanController; //thread that configures the scanner threads and wait till they are done {$IFNDEF lowmemoryusage} SaveFirstScanThread: TSaveFirstScanThread; //thread that will save the results of the first scan that can be used for "same as first scan" scans {$ENDIF} memRegion: TMemoryRegions; //after a scan the contents of controller gets copied to here memRegionPos: integer; progressbar: TCustomProgressBar; notifywindow: thandle; notifymessage: integer; currentVariableType: TVariableType; currentCustomType: TCustomType; found: uint64; //first scan init variables startaddress: ptruint; //start for the whole scan stopaddress: ptruint; //stop of the whole scan //fastscan options (only set by firstscan) //Alignment: integer; fastscanalignment: integer; fastscanmethod: TFastscanmethod; fastscandigitcount: integer; //string stuff: stringUnicode: boolean; stringLength: integer; //binary stuff: binaryLength: integer; //array stuff: arrayLength: integer; fLastScanType: TScanType; fLastScanValue: String; fscanresultfolder: string; //the location where all the scanfiles will be stored fCodePage: boolean; fnextscanCount: integer; savedresults: tstringlist; fonlyOne: boolean; fisHexadecimal: boolean; ffloatscanWithoutExponents: boolean; fInverseScan: boolean; fGUIScanner: boolean; procedure DeleteScanfolder; procedure createScanfolder; function DeleteFolder(dir: string) : boolean; protected fOnScanDone: TNotifyEvent; fOnGuiUpdate: TMemScanGuiUpdateRoutine; procedure ScanDone; virtual; //called by the scancontroller public scanWritable: Tscanregionpreference; scanExecutable: Tscanregionpreference; scanCopyOnWrite: Tscanregionpreference; attachedFoundlist: TObject; procedure parseProtectionflags(protectionflags: string); function GetProgress(var totaladdressestoscan:qword; var currentlyscanned: qword; var resultsfound: qword):integer; function GetErrorString: string; function GetFoundCount: uint64; function Getbinarysize: int64; //returns the number of bits of the current type function GetOnlyOneResult(var address: ptruint):boolean; function GetOnlyOneResults(var addresses: Taddresses):boolean; //multi aob version function GetScanFolder: string; procedure TerminateScan(forceTermination: boolean); procedure newscan; //will clean up the memory and files procedure firstscan(scanOption: TScanOption; VariableType: TVariableType; roundingtype: TRoundingType; scanvalue1, scanvalue2: string; startaddress,stopaddress: ptruint; hexadecimal,binaryStringAsDecimal,unicode,casesensitive: boolean; fastscanmethod: TFastScanMethod=fsmNotAligned; fastscanparameter: string=''; customtype: TCustomType=nil); procedure NextScan(scanOption: TScanOption; roundingtype: TRoundingType; scanvalue1, scanvalue2: string; hexadecimal,binaryStringAsDecimal, unicode, casesensitive,percentage,compareToSavedScan: boolean; savedscanname: string); //next scan, determine what kind of scan and give to firstnextscan/nextnextscan function waittilldone(timeout: dword=INFINITE): boolean; function waittillreallydone(timeout: dword=INFINITE): boolean; procedure setScanDoneCallback(notifywindow: thandle; notifymessage: integer); function canUndo: boolean; procedure undoLastScan; constructor create(progressbar: TCustomProgressbar); destructor destroy; override; procedure saveresults(resultname: string); function getsavedresults(r: tstrings): integer; property nextscanCount: integer read fnextscanCount; published property GUIScanner: Boolean read fGUIScanner write fGUIScanner; property inverseScan: boolean read fInverseScan write fInverseScan; property floatscanWithoutExponents: boolean read ffloatscanWithoutExponents write ffloatscanWithoutExponents; property OnlyOne: boolean read fOnlyOne write fOnlyOne; property VarType: TVariableType read currentVariableType; property CustomType: TCustomType read currentCustomType; property codePage: boolean read fCodePage write fCodePage; property isUnicode: boolean read stringUnicode; property isHexadecimal: boolean read fisHexadecimal; //gui property LastScanValue: string read fLastScanValue; property LastScanType: TScanType read FLastScanType; property ScanresultFolder: string read fScanResultFolder; //read only, it's configured during creation property OnScanDone: TNotifyEvent read fOnScanDone write fOnScanDone; property OnGuiUpdate: TMemscanGuiUpdateRoutine read fOnGuiUpdate write fOnGuiUpdate; end; implementation {$ifdef windows} uses formsettingsunit, StrUtils, foundlisthelper, processhandlerunit, parsers,Globals, frmBusyUnit; {$endif} {$ifdef android} uses ProcessHandlerUnit, parsers, Globals; {$endif} resourcestring rsIsNotAValidCharacterInsideABinaryString = '%s is not a valid character inside a binary string'; rsInvalidBinaryNotation = 'Invalid binary notation'; rsThreadSynchronizer = 'Thread synchronizer'; rsUnknown = 'Unknown'; rsPleaseFillSomethingIn = 'Please fill something in'; rsIsNotAValidValue = '%s is not a valid value'; rsIsAnInvalidValue = '%s is an invalid value'; rsIsNotAValidNotation = '%s is an invalid notation'; rsErrorAllocatingBytesForTheOldResults = 'Error allocating %s bytes for the old results. buffersize=%s variablesize=%s'; rsDiskWriteError = 'Disk write error:%s'; rsNoReadableMemoryFound = 'No readable memory found'; rsFailureAllocatingMemoryForCopyTriedAllocatingKB = 'Failure allocating memory for copy. Tried allocating %s KB'; rsErrorWhenWhileLoadingResult = 'Error when while loading result'; rsNotEnoughDiskspaceForTheAddressFile = 'Not enough diskspace for the address file'; rsNotEnoughDiskspaceForTheMemoryFile = 'Not enough diskspace for the memory file'; rsDiskWriteError2 = 'Disk Write Error:%s'; rsFailedSpawningTheSaveFirstScanThread = 'Failed spawning the Save First Scan thread'; rsForSomeReasonTheScanControllerDoesNotExist = 'For some reason the scanController does not exist'; rsAResultSetWithThisNameAlreadyExists = 'A result set with this name(%s) already exists'; rsTMPAndUNDOAreNamesThatMayNotBeUsedTryAnotherName = 'TMP and UNDO are names that may not be used. Try another name'; rsTheTemporaryScanDirectoryDoesNotExistCheckYourScan = 'The temporary scan directory %s does not exist. Check your scan settings'; rsFailureCreatingTheScanDirectory = 'Failure creating the scan directory'; rsMSNothingToScanFor = 'Nothing to scan for'; rsMStupidAlignsize = 'Stupid alignsize'; rsMSCustomTypeIsNil = 'Custom type is nil'; rsMSTheScanWasForcedToTerminateSubsequentScansMayNotFunctionProperlyEtc = 'The scan was forced to terminate. Subsequent scans may not function properly. It''s recommended to restart Cheat Engine'; rsThread = 'thread '; //===============Local functions================// function getBytecountArrayOfByteString(st: string): integer; var bytes: tbytes; begin ConvertStringToBytes(st,true,bytes); result:=length(bytes); end; function getBytecountBinaryString(st:string; scanvalueisdecimal: boolean): integer; var i: integer; begin if scanvalueisdecimal then //first convert to binarystring st:=parsers.inttobin(strtoint(st)); result:=0; for i:=1 to length(st) do begin case st[i] of '0','1','?','*': inc(result); ' ',#8: ; //ignore else raise exception.Create(Format(rsIsNotAValidCharacterInsideABinaryString, [st[i]])); end; end; if result=0 then raise exception.Create(rsInvalidBinaryNotation); if (result>1) then begin if (result mod 8=0) then result:=1+result div 8 else result:=2+(result div 8); end; end; //==================TGroupData===============// constructor TGroupData.create(parameters: string; scanner: TScanner); //todo: convert groupscancommandparser to unix {$ifndef unix} var start, i: integer; p,s: string; gcp: TGroupscanCommandParser; floatsettings: TFormatSettings; {$endif} begin {$ifndef unix} floatsettings:=DefaultFormatSettings; fscanner:=scanner; gcp:=TGroupscanCommandParser.create; try gcp.parse(parameters); fblocksize:=gcp.blocksize; fAlignsize:=gcp.blockalignment; if fblocksize<=0 then raise exception.create(rsMSNothingToScanFor); if fAlignsize<=0 then raise exception.create(rsMStupidAlignsize); outoforder:=gcp.outOfOrder; outoforder_aligned:=gcp.typeAligned; groupdatalength:=length(gcp.elements); setlength(groupdata, groupdatalength); for i:=0 to Length(gcp.elements)-1 do begin groupdata[i].wildcard:=gcp.elements[i].wildcard; groupdata[i].offset:=gcp.elements[i].offset; groupdata[i].vartype:=gcp.elements[i].vartype; groupdata[i].customtype:=gcp.elements[i].customtype; groupdata[i].valuei:=gcp.elements[i].valueint; groupdata[i].valuef:=gcp.elements[i].valuefloat; groupdata[i].floataccuracy:=pos(gcp.FloatSettings.DecimalSeparator,gcp.elements[i].uservalue); if groupdata[i].floataccuracy>0 then groupdata[i].floataccuracy:=length(gcp.elements[i].uservalue)-groupdata[i].floataccuracy; groupdata[i].minfvalue:=groupdata[i].valuef-(1/(power(10,groupdata[i].floataccuracy))); groupdata[i].maxfvalue:=groupdata[i].valuef+(1/(power(10,groupdata[i].floataccuracy))); groupdata[i].value:=uppercase(gcp.elements[i].uservalue); groupdata[i].widevalue:=uppercase(gcp.elements[i].uservalue); groupdata[i].bytesize:=gcp.elements[i].bytesize; end; finally gcp.free; end; {$endif} end; function TGroupData.testString(buf: PChar; ts: pchar): boolean; var i: integer; begin result:=false; for i:=0 to length(ts)-1 do begin if pchar(buf)[i] in ['a'..'z'] then //change to uppercase dec(pbytearray(buf)[i],$20); if ts[i]<>(pchar(buf)[i]) then exit; end; result:=true; end; function TGroupData.testWideString(buf: PWideChar; ts: pwidechar): boolean; var i: integer; begin result:=false; i:=0; for i:=0 to length(ts)-1 do begin if pchar(buf)[i*sizeof(wchar)] in ['a'..'z'] then //change to uppercase dec(pbytearray(buf)[i*sizeof(wchar)],$20); if ts[i]<>(pwidechar(buf)[i]) then exit; end; result:=true; end; function TGroupData.compareblock(newvalue,oldvalue: pointer): boolean; //ordered scan var i: integer; f: single; begin result:=true; for i:=0 to groupdatalength-1 do begin if result=false then exit; case groupdata[i].vartype of vtByte: begin result:=groupdata[i].wildcard or (pbyte(newvalue)^=byte(groupdata[i].valuei)); inc(newvalue, 1); end; vtWord: begin result:=groupdata[i].wildcard or (pword(newvalue)^=word(groupdata[i].valuei)); inc(newvalue, 2); end; vtDWord: begin result:=groupdata[i].wildcard or (pdword(newvalue)^=dword(groupdata[i].valuei)); inc(newvalue, 4); end; vtQWord: begin result:=groupdata[i].wildcard or (pqword(newvalue)^=qword(groupdata[i].valuei)); inc(newvalue, 8); end; vtSingle: begin result:=groupdata[i].wildcard or ((psingle(newvalue)^>groupdata[i].minfvalue) and (psingle(newvalue)^groupdata[i].minfvalue) and (pdouble(newvalue)^groupdata[i].minfvalue) and (fminf) and (psingle(current)^minf) and (pdouble(current)^minf) and (f(pchar(newvalue)[i-1]) then exit; result:=true; end; function TScanner.CaseInsensitiveAnsiStringExact(newvalue,oldvalue: pointer):boolean; var i: integer; begin //scanvalue1 has already been converted to uppercase in config result:=false; for i:=1 to length(scanvalue1) do begin if pchar(newvalue)[(i-1)] in ['a'..'z'] then //change to uppercase dec(pbytearray(newvalue)[(i-1)],$20); if scanvalue1[i]<>(pchar(newvalue)[i-1]) then exit; end; result:=true; //it got here, so a match end; function TScanner.CaseSensitiveUnicodeStringExact(newvalue,oldvalue: pointer):boolean; var i: integer; begin i:=length(widescanvalue1); // result:=false; { for i:=1 to length(scanvalue1) do if widescanvalue1[i]<>(pwidechar(newvalue)[i-1]) then exit; } result:=strlcomp(@widescanvalue1[1], pwidechar(newvalue),i)=0; // result:=true; end; function TScanner.CaseInsensitiveUnicodeStringExact(newvalue,oldvalue: pointer):boolean; var i: integer; begin i:=length(widescanvalue1); result:=true; for i:=1 to length(widescanvalue1) do if system.UpCase(widescanvalue1[i])<>system.UpCase(pwidechar(newvalue)[i-1]) then exit(false); //result:=strlicomp(@widescanvalue1[1], pwidechar(newvalue), i)=0; { i:=0; for i:=1 to length(scanvalue1) do begin if pchar(newvalue)[(i-1)*sizeof(wchar)] in ['a'..'z'] then //change to uppercase dec(pbytearray(newvalue)[(i-1)*sizeof(wchar)],$20); if widescanvalue1[i]<>(pwidechar(newvalue)[i-1]) then exit; end; result:=true; //it got here, so a match } end; function TScanner.ArrayOfByteExact_NibbleWildcardSupport(newvalue,oldvalue: pointer):boolean; //sperate function as support for this will cause it to be slower var i: integer; begin for i:=0 to abs_arraylength-1 do begin if abs_arraytofind[i]=-1 then continue; //full wildcard if abs_arraytofind[i]<0 then begin //it's a nibble wildcard //check if it matches if ((pbytearray(newvalue)[i] and ((abs_arraytofind[i] shr 8) and $ff))<> (abs_arraytofind[i] and $ff) ) then begin result:=false; //no match exit; end; end else if (pbytearray(newvalue)[i]<>abs_arraytofind[i]) then begin result:=false; //no match exit; end; end; result:=true; //still here, so a match end; function TScanner.ArrayOfByteExact(newvalue,oldvalue: pointer):boolean; var i: integer; begin for i:=0 to abs_arraylength-1 do if (abs_arraytofind[i]<>-1) and (pbytearray(newvalue)[i]<>abs_arraytofind[i]) then begin result:=false; //no match exit; end; result:=true; //still here, so a match end; function TScanner.ArrayOfBytesExact_NibbleWildcardSupport(newvalue: pointer; mabsindex: integer):boolean; var i: integer; arraylength: integer; a: PIntegerArray; begin arraylength:=mabs[mabsindex].arraylength; a:=@mabs[mabsindex].arraytofind[0]; for i:=0 to arraylength-1 do begin if a[i]=-1 then continue; //full wildcard if a[i]<0 then begin //it's a nibble wildcard //check if it matches if ((pbytearray(newvalue)[i] and ((a[i] shr 8) and $ff))<> (a[i] and $ff) ) then begin result:=false; //no match exit; end; end else if (pbytearray(newvalue)[i]<>a[i]) then begin result:=false; //no match exit; end; end; result:=true; //still here, so a match end; function TScanner.ArrayOfBytesExact(newvalue: pointer; mabsindex: integer):boolean; var i: integer; arraylength: integer; a: PIntegerArray; begin arraylength:=mabs[mabsindex].arraylength; a:=@mabs[mabsindex].arraytofind[0]; for i:=0 to arraylength-1 do if (a[i]<>-1) and (pbytearray(newvalue)[i]<>a[i]) then begin result:=false; //no match exit; end; result:=true; //still here, so a match end; function TScanner.BinaryExact(newvalue,oldvalue: pointer):boolean; var i: integer; begin for i:=0 to 7 do binaryresults[i]:=((PQWORD(newvalue)^ shr i) and andmask)=bitmask; //no need for a result here, for binary that isn't checked (special case) result:=true; //let the store result routine deal with it end; //byte: function TScanner.ByteExact(newvalue,oldvalue: pointer):boolean; begin result:=pbyte(newvalue)^=byte(value); end; function TScanner.ByteBetween(newvalue,oldvalue: pointer):boolean; begin result:=(pbyte(newvalue)^>=byte(value)) and (pbyte(newvalue)^<=byte(value2)); end; function TScanner.ByteBetweenPercentage(newvalue,oldvalue: pointer):boolean; begin result:=(pbyte(newvalue)^>trunc(pbyte(oldvalue)^*svalue)) and (pbyte(newvalue)^<=trunc(pbyte(oldvalue)^*svalue2)); end; function TScanner.ByteBiggerThan(newvalue,oldvalue: pointer):boolean; begin result:=pbyte(newvalue)^>byte(value); end; function TScanner.ByteSmallerThan(newvalue,oldvalue: pointer):boolean; begin result:=pbyte(newvalue)^pbyte(oldvalue)^; end; function TScanner.ByteIncreasedValueBy(newvalue,oldvalue: pointer):boolean; begin result:=pbyte(newvalue)^=pbyte(oldvalue)^+byte(value); end; function TScanner.ByteIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; begin result:=(pbyte(newvalue)^>trunc(pbyte(oldvalue)^+pbyte(oldvalue)^*svalue)) and (pbyte(newvalue)^trunc(pbyte(oldvalue)^-pbyte(oldvalue)^*svalue2)) and (pbyte(newvalue)^pbyte(oldvalue)^; end; function TScanner.ByteUnchanged(newvalue,oldvalue: pointer):boolean; //also used for same as first begin result:=pbyte(newvalue)^=pbyte(oldvalue)^; end; //word: function TScanner.WordExact(newvalue,oldvalue: pointer): boolean; begin result:=pword(newvalue)^=word(value); end; function TScanner.WordBetween(newvalue,oldvalue: pointer):boolean; begin result:=(pword(newvalue)^>=word(value)) and (pword(newvalue)^<=word(value2)); end; function TScanner.WordBetweenPercentage(newvalue,oldvalue: pointer):boolean; begin result:=(pword(newvalue)^>trunc(pword(oldvalue)^*svalue)) and (pword(newvalue)^<=trunc(pword(oldvalue)^*svalue2)); end; function TScanner.WordBiggerThan(newvalue,oldvalue: pointer):boolean; begin result:=pword(newvalue)^>word(value); end; function TScanner.WordSmallerThan(newvalue,oldvalue: pointer):boolean; begin result:=pword(newvalue)^pword(oldvalue)^; end; function TScanner.WordIncreasedValueBy(newvalue,oldvalue: pointer):boolean; begin result:=pword(newvalue)^=pword(oldvalue)^+word(value); end; function TScanner.WordDecreasedValue(newvalue,oldvalue: pointer):boolean; begin result:=pword(newvalue)^trunc(pword(oldvalue)^+pword(oldvalue)^*svalue)) and (pword(newvalue)^trunc(pword(oldvalue)^-pword(oldvalue)^*svalue2)) and (pword(newvalue)^pword(oldvalue)^; end; function TScanner.WordUnchanged(newvalue,oldvalue: pointer):boolean; begin result:=pword(newvalue)^=pword(oldvalue)^; end; //--------------\/custom\/ function TScanner.CustomExact(newvalue,oldvalue: pointer): boolean; begin {$ifdef customtypeimplemented} result:=customType.ConvertDataToInteger(newvalue, currentAddress)=integer(value); {$ENDIF} end; function TScanner.CustomBetween(newvalue,oldvalue: pointer): boolean; begin {$ifdef customtypeimplemented} result:=(customType.ConvertDataToInteger(newvalue, currentAddress)>=integer(value)) and (customType.ConvertDataToInteger(newvalue, currentAddress)<=integer(value2)); {$ENDIF} end; function TScanner.CustomBetweenPercentage(newvalue,oldvalue: pointer):boolean; begin {$ifdef customtypeimplemented} result:=(customType.ConvertDataToInteger(newvalue, currentAddress)>trunc(customType.ConvertDataToInteger(oldvalue, currentAddress)*svalue)) and (customType.ConvertDataToInteger(newvalue, currentAddress)<=trunc(customType.ConvertDataToInteger(oldvalue, currentAddress)*svalue2)); {$ENDIF} end; function TScanner.CustomBiggerThan(newvalue,oldvalue: pointer): boolean; begin {$ifdef customtypeimplemented} result:=customType.ConvertDataToInteger(newvalue, currentAddress)>integer(value); {$ENDIF} end; function TScanner.CustomSmallerThan(newvalue,oldvalue: pointer): boolean; begin {$ifdef customtypeimplemented} result:=customType.ConvertDataToInteger(newvalue, currentAddress)customType.ConvertDataToInteger(oldvalue, currentAddress); {$ENDIF} end; function TScanner.CustomIncreasedValueBy(newvalue,oldvalue: pointer): boolean; begin {$ifdef customtypeimplemented} result:=customType.ConvertDataToInteger(newvalue, currentAddress)=customType.ConvertDataToInteger(oldvalue, currentAddress)+dword(value); {$ENDIF} end; function TScanner.CustomIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; begin {$ifdef customtypeimplemented} result:=(customType.ConvertDataToInteger(newvalue, currentAddress)>trunc(customType.ConvertDataToInteger(oldvalue, currentAddress)+customType.ConvertDataToInteger(oldvalue, currentAddress)*svalue)) and (customType.ConvertDataToInteger(newvalue,currentAddress)trunc(customType.ConvertDataToInteger(oldvalue, currentAddress)-customType.ConvertDataToInteger(oldvalue, currentAddress)*svalue2)) and (customType.ConvertDataToInteger(newvalue, currentAddress)customType.ConvertDataToInteger(oldvalue, currentAddress); {$ENDIF} end; function TScanner.CustomUnChanged(newvalue,oldvalue: pointer): boolean; begin {$ifdef customtypeimplemented} result:=customType.ConvertDataToInteger(newvalue, currentAddress)=customType.ConvertDataToInteger(oldvalue, currentAddress); {$ENDIF} end; //--------------/\custom/\ //--------------Custom Float------------- function TScanner.CustomFloatExact(newvalue,oldvalue: pointer): boolean; var f: single; begin result:=false; {$ifdef customtypeimplemented} f:=customType.ConvertDataToFloat(newvalue, currentAddress); case roundingtype of rtRounded: result:=(RoundTo(f,-floataccuracy)=svalue); rtExtremerounded: result:=(f>minsvalue) and (f=svalue) and (f=svalue) and (f<=svalue2); {$ENDIF} end; function TScanner.CustomFloatBetweenPercentage(newvalue,oldvalue: pointer): boolean; var new: single; old: single; begin {$ifdef customtypeimplemented} new:=customType.ConvertDataToFloat(newvalue, currentAddress); old:=customType.ConvertDataToFloat(oldvalue, currentAddress); result:=(new>old*svalue) and (new<=old*svalue2); {$ENDIF} end; function TScanner.CustomFloatBiggerThan(newvalue,oldvalue: pointer):boolean; begin {$ifdef customtypeimplemented} result:=customType.ConvertDataToFloat(newvalue, currentAddress)>svalue; {$ENDIF} end; function TScanner.CustomFloatSmallerThan(newvalue,oldvalue: pointer):boolean; begin {$ifdef customtypeimplemented} result:=customType.ConvertDataToFloat(newvalue, currentAddress)customType.ConvertDataToFloat(oldvalue, currentAddress); {$ENDIF} end; function TScanner.CustomFloatIncreasedValueBy(newvalue,oldvalue: pointer):boolean; begin {$ifdef customtypeimplemented} result:=(not CompareMem(newvalue, oldvalue, customtype.bytesize)) and (RoundTo(customType.ConvertDataToFloat(newvalue, currentAddress),-floataccuracy)=RoundTo(customType.ConvertDataToFloat(oldvalue, currentAddress)+svalue,-floataccuracy)); {$ENDIF} end; function TScanner.CustomFloatDecreasedValue(newvalue,oldvalue: pointer):boolean; begin {$ifdef customtypeimplemented} result:=customType.ConvertDataToFloat(newvalue, currentAddress)old+old*svalue) and (newold-old*svalue2) and (newcustomType.ConvertDataToFloat(oldvalue, currentAddress); {$ENDIF} end; function TScanner.CustomFloatUnchanged(newvalue,oldvalue: pointer):boolean; begin {$ifdef customtypeimplemented} result:=customType.ConvertDataToFloat(newvalue, currentAddress)=customType.ConvertDataToFloat(oldvalue, currentAddress); {$ENDIF} end; // ^^^^CustomFloat^^^^ //dword: function TScanner.DWordExact(newvalue,oldvalue: pointer): boolean; begin result:=pdword(newvalue)^=dword(value); end; function TScanner.DWordBetween(newvalue,oldvalue: pointer):boolean; begin result:=(pdword(newvalue)^>=dword(value)) and (pdword(newvalue)^<=dword(value2)); end; function TScanner.DwordBetweenPercentage(newvalue,oldvalue: pointer):boolean; begin result:=(pdword(newvalue)^>trunc(pdword(oldvalue)^*svalue)) and (pdword(newvalue)^<=trunc(pdword(oldvalue)^*svalue2)); end; function TScanner.DWordBiggerThan(newvalue,oldvalue: pointer):boolean; begin result:=pdword(newvalue)^>dword(value); end; function TScanner.DWordSmallerThan(newvalue,oldvalue: pointer):boolean; begin result:=pdword(newvalue)^pdword(oldvalue)^; end; function TScanner.DWordIncreasedValueBy(newvalue,oldvalue: pointer):boolean; begin result:=pdword(newvalue)^=pdword(oldvalue)^+dword(value); end; function TScanner.DWordDecreasedValue(newvalue,oldvalue: pointer):boolean; begin result:=pdword(newvalue)^trunc(pdword(oldvalue)^+pdword(oldvalue)^*svalue)) and (pdword(newvalue)^trunc(pdword(oldvalue)^-pdword(oldvalue)^*svalue2)) and (pdword(newvalue)^pdword(oldvalue)^; end; function TScanner.DWordUnchanged(newvalue,oldvalue: pointer):boolean; begin result:=pdword(newvalue)^=pdword(oldvalue)^; end; //int64 function TScanner.QWordExact(newvalue,oldvalue: pointer): boolean; begin result:=PQWORD(newvalue)^=uint64(value); end; function TScanner.QWordBetween(newvalue,oldvalue: pointer):boolean; begin result:=(PQWORD(newvalue)^>=uint64(value)) and (PQWORD(newvalue)^<=uint64(value2)); end; function TScanner.QwordBetweenPercentage(newvalue,oldvalue: pointer):boolean; begin result:=(PQWORD(newvalue)^>trunc(PQWORD(oldvalue)^*svalue)) and (PQWORD(newvalue)^<=trunc(PQWORD(oldvalue)^*svalue2)); end; function TScanner.QWordBiggerThan(newvalue,oldvalue: pointer):boolean; begin result:=PQWORD(newvalue)^>uint64(value); end; function TScanner.QWordSmallerThan(newvalue,oldvalue: pointer):boolean; begin result:=PQWORD(newvalue)^PQWORD(oldvalue)^; end; function TScanner.QWordIncreasedValueBy(newvalue,oldvalue: pointer):boolean; begin result:=PQWORD(newvalue)^=PQWORD(oldvalue)^+value; end; function TScanner.QWordDecreasedValue(newvalue,oldvalue: pointer):boolean; begin result:=PQWORD(newvalue)^trunc(PQWORD(oldvalue)^+PQWORD(oldvalue)^*svalue)) and (PQWORD(newvalue)^trunc(PQWORD(oldvalue)^-PQWORD(oldvalue)^*svalue2)) and (PQWORD(newvalue)^PQWORD(oldvalue)^; end; function TScanner.QWordUnchanged(newvalue,oldvalue: pointer):boolean; begin result:=PQWORD(newvalue)^=PQWORD(oldvalue)^; end; //single: function TScanner.SingleExact(newvalue,oldvalue: pointer): boolean; begin result:=false; case roundingtype of rtRounded: result:=(RoundTo(psingle(newvalue)^,-floataccuracy)=svalue); rtExtremerounded: result:=(psingle(newvalue)^>minsvalue) and (psingle(newvalue)^=svalue) and (psingle(newvalue)^=svalue) and (psingle(newvalue)^<=svalue2); end; function TScanner.SingleBetweenPercentage(newvalue,oldvalue: pointer): boolean; begin result:=(psingle(newvalue)^>psingle(oldvalue)^*svalue) and (psingle(newvalue)^<=psingle(oldvalue)^*svalue2); end; function TScanner.SingleBiggerThan(newvalue,oldvalue: pointer):boolean; begin result:=psingle(newvalue)^>svalue; end; function TScanner.SingleSmallerThan(newvalue,oldvalue: pointer):boolean; begin result:=psingle(newvalue)^psingle(oldvalue)^; end; function TScanner.SingleIncreasedValueBy(newvalue,oldvalue: pointer):boolean; begin result:=(pdword(newvalue)^<>pdword(oldvalue)^) and (RoundTo(psingle(newvalue)^,-floataccuracy)=RoundTo(psingle(oldvalue)^+svalue,-floataccuracy)); end; function TScanner.SingleDecreasedValue(newvalue,oldvalue: pointer):boolean; begin result:=psingle(newvalue)^pdword(oldvalue)^) and (RoundTo(psingle(newvalue)^,-floataccuracy)=RoundTo(psingle(oldvalue)^-svalue,-floataccuracy)); end; function TScanner.SingleIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; begin result:=(psingle(newvalue)^>psingle(oldvalue)^+psingle(oldvalue)^*svalue) and (psingle(newvalue)^psingle(oldvalue)^-psingle(oldvalue)^*svalue2) and (psingle(newvalue)^pSingle(oldvalue)^; end; function TScanner.SingleUnchanged(newvalue,oldvalue: pointer):boolean; begin result:=pSingle(newvalue)^=pSingle(oldvalue)^; end; //double: function TScanner.DoubleExact(newvalue,oldvalue: pointer): boolean; begin result:=false; case roundingtype of rtRounded: result:=(RoundTo(pdouble(newvalue)^,-floataccuracy)=dvalue); rtExtremerounded: result:=(pdouble(newvalue)^>mindvalue) and (pdouble(newvalue)^=dvalue) and (pdouble(newvalue)^=dvalue) and (pdouble(newvalue)^<=dvalue2); end; function TScanner.DoubleBetweenPercentage(newvalue,oldvalue: pointer): boolean; begin result:=(pdouble(newvalue)^>pdouble(oldvalue)^*dvalue) and (pdouble(newvalue)^<=pdouble(oldvalue)^*dvalue2); end; function TScanner.DoubleBiggerThan(newvalue,oldvalue: pointer):boolean; begin result:=pdouble(newvalue)^>dvalue; end; function TScanner.DoubleSmallerThan(newvalue,oldvalue: pointer):boolean; begin result:=pdouble(newvalue)^pdouble(oldvalue)^; end; function TScanner.DoubleIncreasedValueBy(newvalue,oldvalue: pointer):boolean; begin result:=(pqword(newvalue)^<>pqword(oldvalue)^) and (RoundTo(pdouble(newvalue)^,-floataccuracy)=RoundTo(pdouble(oldvalue)^+svalue,-floataccuracy)); end; function TScanner.DoubleDecreasedValue(newvalue,oldvalue: pointer):boolean; begin result:=pdouble(newvalue)^pqword(oldvalue)^) and (RoundTo(pdouble(newvalue)^,-floataccuracy)=RoundTo(pdouble(oldvalue)^-svalue,-floataccuracy)); end; function TScanner.DoubleIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean; begin result:=(pdouble(newvalue)^>pdouble(oldvalue)^+pdouble(oldvalue)^*dvalue) and (pdouble(newvalue)^pdouble(oldvalue)^-pdouble(oldvalue)^*dvalue2) and (pdouble(newvalue)^pDouble(oldvalue)^; end; function TScanner.DoubleUnchanged(newvalue,oldvalue: pointer):boolean; begin result:=pDouble(newvalue)^=pDouble(oldvalue)^; end; //=============Save result routines===========// procedure TScanner.GenericSaveResult(address: ptruint; oldvalue: pointer); { Generic routine for storing results. Use as last resort. E.g custom scans } var f: single; begin //save varsize {$ifdef customtypeimplemented} if (variableType = vtCustom) and (customtype<>nil) and (customtype.scriptUsesFloat) then begin //check if it's a valid float result f:=customType.ConvertDataToFloat(oldvalue, currentAddress); //get value if isnan(f) or IsInfinite(f) then exit; //check if valid, if not, exit end; {$ENDIF} PPtrUintArray(CurrentAddressBuffer)[found]:=address; copyMemory(pointer(ptruint(CurrentFoundBuffer)+ptruint(variablesize*found)),oldvalue,variablesize); inc(found); if found>=maxfound then flushroutine; end; procedure TScanner.ByteSaveResult(address: ptruint; oldvalue: pointer); begin //save address and current value PPtrUintArray(CurrentAddressBuffer)[found]:=address; pbytearray(CurrentFoundBuffer)[found]:=pbyte(oldvalue)^; inc(found); if found>=maxfound then flushroutine; end; procedure TScanner.WordSaveResult(address: ptruint; oldvalue: pointer); begin PPtrUintArray(CurrentAddressBuffer)[found]:=address; pwordarray(CurrentFoundBuffer)[found]:=pword(oldvalue)^; inc(found); if found>=maxfound then flushroutine; end; procedure TScanner.DWordSaveResult(address: ptruint; oldvalue: pointer); begin PPtrUintArray(CurrentAddressBuffer)[found]:=address; pdwordarray(CurrentFoundBuffer)[found]:=pdword(oldvalue)^; inc(found); if found>=maxfound then flushroutine; end; procedure TScanner.QWordSaveResult(address: ptruint; oldvalue: pointer); begin PPtrUintArray(CurrentAddressBuffer)[found]:=address; puint64array(CurrentFoundBuffer)[found]:=PQWORD(oldvalue)^; inc(found); if found>=maxfound then flushroutine; end; procedure TScanner.SingleSaveResult(address: ptruint; oldvalue: pointer); var exp: integer; begin if not (isnan(psingle(oldvalue)^) or IsInfinite(psingle(oldvalue)^)) then begin if floatscanWithoutExponents and (pdword(oldvalue)^>0) and (abs(127-(pdword(oldvalue)^ shr 23) and $ff)>10) then exit; PPtrUintArray(CurrentAddressBuffer)[found]:=address; psinglearray(CurrentFoundBuffer)[found]:=psingle(oldvalue)^; inc(found); if found>=maxfound then flushroutine; end; end; procedure TScanner.DoubleSaveResult(address: ptruint; oldvalue: pointer); begin if not (isnan(pdouble(oldvalue)^) or IsInfinite(pdouble(oldvalue)^)) then begin if floatscanWithoutExponents and (pqword(oldvalue)^>0) and (abs(integer(1023-(pqword(oldvalue)^ shr 52) and $7ff))>10) then exit; PPtrUintArray(CurrentAddressBuffer)[found]:=address; pdoublearray(CurrentFoundBuffer)[found]:=pdouble(oldvalue)^; inc(found); if found>=maxfound then flushroutine; end; end; procedure TScanner.arrayOfByteSaveResult(address: ptruint; oldvalue: pointer); begin PPtrUintArray(CurrentAddressBuffer)[found]:=address; inc(found); if found>=maxfound then flushroutine; end; procedure TScanner.binarySaveResult(address: ptruint; oldvalue: pointer); var i: integer; begin for i:=0 to 7 do begin if binaryresults[i] then begin PBitAddressArray(CurrentAddressBuffer)[found].address:=address; PBitAddressArray(CurrentAddressBuffer)[found].bit:=i; inc(found); if found>=maxfound then flushroutine; end; end; end; procedure TScanner.groupSaveResult(address: ptruint; oldvalue: pointer); var i: integer; entry: PGroupAddress; begin entry:=PGroupAddress(ptruint(CurrentAddressBuffer)+ found*(sizeof(ptruint)+sizeof(dword)*groupdata.groupdatalength)); entry.address:=address; for i:=0 to groupdata.groupdatalength-1 do entry.offsets[i]:=groupdata.groupdata[i].offset; inc(found); if found>=maxfound then flushroutine; end; procedure TScanner.allSaveResult(address: ptruint; oldvalue: pointer); { note: eventually replace bit with a binary representation of all types that match BUT, don't forget to change the foundlisthelper to handle this (since it'd be multiple addresses in one entry, which isn't handled right now... } var i: TVariabletype; j: integer; begin for i:=vtByte to vtDouble do begin if typesmatch[i] then begin if (i=vtsingle) then begin //filter out NAN and INF if isnan(psingle(oldvalue)^) or IsInfinite(psingle(oldvalue)^) then continue; //skip, don't save end; if (i=vtdouble) then begin if isnan(pdouble(oldvalue)^) or IsInfinite(pdouble(oldvalue)^) then continue; //skip, don't save end; //using the bitaddressarray since it holds a address and a value big enough to hold all types PBitAddressArray(CurrentAddressBuffer)[found].address:=address; PBitAddressArray(CurrentAddressBuffer)[found].bit:=integer(i); //save the current address, max variablesize is x bytes, so just store that copyMemory(pointer(ptruint(CurrentFoundBuffer)+ptruint(variablesize*found)),oldvalue,variablesize); inc(found); if found>=maxfound then flushroutine; end; end; if allCustom then begin for j:=0 to customtypecount-1 do begin if customtypesmatch[j] then begin PBitAddressArray(CurrentAddressBuffer)[found].address:=address; PBitAddressArray(CurrentAddressBuffer)[found].bit:=j+$1000; //+$1000 to distinguish between custom and default types. When I add more than 4095 default types I'll have to change this. copyMemory(pointer(ptruint(CurrentFoundBuffer)+ptruint(variablesize*found)),oldvalue,variablesize); inc(found); if found>=maxfound then flushroutine; end; end; end; end; //=============Flush result routines===========// procedure TScanner.genericFlush; var tempaddress,tempfound: pointer; begin scanwriter.writeresults(CurrentAddressBuffer,currentfoundbuffer,sizeof(ptruint)*found,found*variablesize); //address tempaddress:=CurrentAddressBuffer; tempfound:=CurrentFoundBuffer; CurrentAddressBuffer:=SecondaryAddressBuffer; CurrentFoundBuffer:=SecondaryFoundBuffer; SecondaryAddressBuffer:=tempaddress; SecondaryFoundBuffer:=tempfound; inc(totalfound,found); found:=0; end; procedure TScanner.stringFlush; begin AddressFile.WriteBuffer(CurrentAddressBuffer^,sizeof(ptruint)*found); //address inc(totalfound,found); found:=0; end; procedure TScanner.binaryFlush; begin AddressFile.WriteBuffer(CurrentAddressBuffer^,sizeof(TBitAddress)*found); inc(totalfound,found); found:=0; end; procedure TScanner.groupflush; begin //address, offset,offset,... AddressFile.WriteBuffer(CurrentAddressBuffer^,found*(sizeof(ptruint)+sizeof(dword)*groupdata.groupdatalength)); inc(totalfound,found); found:=0; end; procedure TScanner.allFlush; var tempaddress,tempfound: pointer; begin scanwriter.writeresults(CurrentAddressBuffer,currentfoundbuffer,sizeof(tbitaddress)*found,found*variablesize); tempaddress:=CurrentAddressBuffer; tempfound:=CurrentFoundBuffer; CurrentAddressBuffer:=SecondaryAddressBuffer; CurrentFoundBuffer:=SecondaryFoundBuffer; SecondaryAddressBuffer:=tempaddress; SecondaryFoundBuffer:=tempfound; inc(totalfound,found); found:=0; end; //==================Tscanfilewriter=================// procedure Tscanfilewriter.execute; var part: integer; begin part:=0; if writeError then exit; try repeat dataavailable.WaitFor(infinite); try if terminated then exit; scancontroller.resultsaveCS.Enter; try part:=1; AddressFile.WriteBuffer(addressbuffer^,addressSize); part:=2; MemoryFile.WriteBuffer(memorybuffer^,memorySize); finally scancontroller.resultsaveCS.Leave; end; finally datawritten.SetEvent; //tell the others that you're ready to write again end; until terminated; except on e: exception do begin case part of 0: errorstring:=rsThreadSynchronizer; 1: errorstring:=scanner.Addressfilename; 2: errorstring:=scanner.MemoryFilename; else errorstring:=rsUnknown; end; errorstring:=errorstring+':'+e.message; writeError:=true; dataavailable.SetEvent; datawritten.SetEvent; end; end; end; procedure Tscanfilewriter.writeresults(addressbuffer,memorybuffer: pointer; addressSize,memorySize: dword); { check if the thread is currently saving If yes, wait, if not, start the thread, give it the buffer, and continue } begin datawritten.WaitFor(infinite); //only gets set when the thread is done writing //got past the wait, so it's done writing, so it has no need for the current variables self.addressbuffer:=addressbuffer; self.memorybuffer:=memorybuffer; self.addressSize:=addressSize; self.memorySize:=memorySize; dataavailable.SetEvent; //tell the thread to start saving //and return to the scanner, who should now swap scanbuffers end; procedure Tscanfilewriter.flush; begin if writeerror then exit; datawritten.WaitFor(infinite); datawritten.SetEvent; end; destructor Tscanfilewriter.destroy; begin if not terminated then begin terminate; dataavailable.SetEvent; waitfor; end; if datawritten<>nil then datawritten.Free; if dataavailable<>nil then dataavailable.Free; { if cAddressFile<>nil then cAddressFile.free; if cMemoryFile<>nil then cMemoryFile.free; } inherited destroy; end; constructor Tscanfilewriter.create(scanner: TScanner; scancontroller:TScanController; addressfile,memoryfile:TFileStream); begin self.scancontroller:=scancontroller; self.scanner:=scanner; self.addressfile:=addressfile; self.memoryfile:=memoryfile; //create the events datawritten:=tevent.Create(nil,false,true,''); dataavailable:=tevent.create(nil,false,false,''); inherited create(false); //start it end; //=============TScanner===========// procedure TScanner.FirstScanmem(base:ptruint; buffer: pointer; size: integer); { Scan the given buffer Excludes the previousvalue buffer } var stepsize: integer; lastmem: ptruint; p: pbyte; i: TVariableType; j,k: integer; _fastscan: boolean; dividableby2: boolean; dividableby4: boolean; allfound: boolean; aob_checkroutine: TMultiAOBCheckRoutine; inv: boolean; begin inv:=inverseScan; _fastscan:=fastscanmethod<>fsmNotAligned; p:=buffer; lastmem:=ptruint(p)+(size-variablesize); //optimizes compile to use reg if possible if _fastscan then begin if fastscanmethod=fsmAligned then stepsize:=fastscanalignsize else begin stepsize:=trunc(power(16, fastscandigitcount)); inc(p, fastscanalignsize); end; end else stepsize:=1; case variableType of vtByteArrays: begin //do a ArrayOfBytesExact scan for every aob in the list if nibbleSupport then aob_checkroutine:=ArrayOfBytesExact_NibbleWildcardSupport else aob_checkroutine:=ArrayOfBytesExact; while (ptruint(p)<=lastmem) do begin for j:=0 to mabs_arraylength-1 do begin if (mabs[j].foundaddress=0) and aob_checkroutine(p,j) then //found one begin mabs[j].foundaddress:=base+ptruint(p)-ptruint(buffer); //check if all have been found allfound:=true; for k:=0 to mabs_arraylength-1 do if mabs[k].foundaddress=0 then begin allfound:=false; break; end; if allfound then begin found:=1; exit; end; end; end; inc(p,stepsize); end; end; vtAll: begin //reset typesmatch array for each check while (ptruint(p)<=lastmem) do begin typesmatch[vtByte]:=allByte; if _fastscan then begin dividableby2:=ptruint(p) mod 2=0; dividableby4:=ptruint(p) mod 4=0; typesmatch[vtWord]:=allWord and dividableby2; typesmatch[vtDWord]:=allDword and dividableby4; typesmatch[vtQWord]:=allQword and dividableby4; typesmatch[vtSingle]:=allFloat and dividableby4; typesmatch[vtDouble]:=allDouble and dividableby4; end else begin typesmatch[vtWord]:=allWord; typesmatch[vtDWord]:=allDword; typesmatch[vtQWord]:=allQword; typesmatch[vtSingle]:=allFloat; typesmatch[vtDouble]:=allDouble; end; if allCustom then begin currentaddress:=base+ptruint(p)-ptruint(buffer); for j:=0 to customtypecount-1 do customtypesmatch[j]:=true; end; if checkroutine(p,nil) then //found one StoreResultRoutine(base+ptruint(p)-ptruint(buffer),p); inc(p,stepsize); end; end; vtCustom: begin while (ptruint(p)<=lastmem) do begin currentAddress:=base+ptruint(p)-ptruint(buffer); if checkroutine(p,nil) xor inv then //found one begin StoreResultRoutine(currentAddress,p); if OnlyOne then begin AddressFound:=currentAddress; exit; end; end; inc(p,stepsize); end; end else begin while (ptruint(p)<=lastmem) do begin if checkroutine(p,nil) xor inv then //found one begin StoreResultRoutine(base+ptruint(p)-ptruint(buffer),p); if OnlyOne then begin AddressFound:=base+ptruint(p)-ptruint(buffer); exit; end; end; inc(p,stepsize); end; end; end; end; procedure TScanner.FirstNextScanmem(base:ptruint; buffer,oldbuffer: pointer; size: integer); { Scan the given buffer } var stepsize: integer; lastmem: ptruint; p,oldp: pbyte; valuetype: TVariableType; _fastscan: boolean; dividableby2: boolean; dividableby4: boolean; i: TVariableType; j: integer; inv: boolean; begin inv:=inverseScan; p:=buffer; oldp:=oldbuffer; lastmem:=ptruint(p)+(size-variablesize); //optimizes compile to use reg if possible _fastscan:=fastscanmethod<>fsmNotAligned; if _fastscan then begin if fastscanmethod=fsmAligned then stepsize:=fastscanalignsize else begin stepsize:=trunc(power(16, fastscandigitcount)); inc(p, fastscanalignsize); inc(oldp, fastscanalignsize); end; end else stepsize:=1; if compareToSavedScan then //stupid, but ok... (actually useful for lowmem scans) begin case self.variableType of vtByte: valuetype:=vtbyte; vtWord: valuetype:=vtword; vtDWord: valuetype:=vtdword; vtSingle: valuetype:=vtsingle; vtdouble: valuetype:=vtdouble; vtQword: valuetype:=vtQword; vtAll: valuetype:=vtall; else valuetype:=vtDword; end; if valuetype=vtall then begin while ptruint(p)<=lastmem do begin if _fastscan then begin dividableby2:=ptrUint(p) mod 2=0; dividableby4:=ptrUint(p) mod 4=0; typesmatch[vtByte]:=allbyte; typesmatch[vtWord]:=allWord and dividableby2; typesmatch[vtDWord]:=allDword and dividableby4; typesmatch[vtQWord]:=allQword and dividableby4; typesmatch[vtSingle]:=allFloat and dividableby4; typesmatch[vtDouble]:=allDouble and dividableby4; end else begin typesmatch[vtByte]:=allByte; typesmatch[vtWord]:=allWord; typesmatch[vtDWord]:=allDword; typesmatch[vtQWord]:=allQword; typesmatch[vtSingle]:=allFloat; typesmatch[vtDouble]:=allDouble; end; if allCustom then begin currentaddress:=base+ptruint(p)-ptruint(buffer); for j:=0 to customtypecount-1 do customtypesmatch[j]:=true; end; if checkroutine(p,savedscanhandler.getpointertoaddress(base+ptruint(p)-ptruint(buffer),valuetype,nil )) then //found one StoreResultRoutine(base+ptruint(p)-ptruint(buffer),p); inc(p, stepsize); end; end else begin while ptruint(p)<=lastmem do begin currentaddress:=base+ptrUint(p)-ptrUint(buffer); if checkroutine(p,savedscanhandler.getpointertoaddress(currentaddress,valuetype,customtype )) xor inv then //found one StoreResultRoutine(currentaddress,p); inc(p, stepsize); end; end; end else begin if variableType=vtall then begin while ptruint(p)<=lastmem do begin if _fastscan then begin dividableby2:=ptruint(p) mod 2=0; dividableby4:=ptruint(p) mod 4=0; typesmatch[vtByte]:=allbyte; typesmatch[vtWord]:=allWord and dividableby2; typesmatch[vtDWord]:=allDword and dividableby4; typesmatch[vtQWord]:=allQword and dividableby4; typesmatch[vtSingle]:=allFloat and dividableby4; typesmatch[vtDouble]:=allDouble and dividableby4; end else begin typesmatch[vtByte]:=allByte; typesmatch[vtWord]:=allWord; typesmatch[vtDWord]:=allDword; typesmatch[vtQWord]:=allQword; typesmatch[vtSingle]:=allFloat; typesmatch[vtDouble]:=allDouble; end; if allCustom then for j:=0 to customtypecount-1 do customtypesmatch[j]:=true; if checkroutine(p,oldp) then //found one StoreResultRoutine(base+ptruint(p)-ptruint(buffer),p); inc(p, stepsize); inc(oldp, stepsize); end; end else if variableType=vtCustom then begin while ptruint(p)<=lastmem do begin currentaddress:=base+ptruint(p)-ptruint(buffer); if checkroutine(p,oldp) xor inv then //found one StoreResultRoutine(currentaddress,p); inc(p, stepsize); inc(oldp, stepsize); end; end else begin while ptruint(p)<=lastmem do begin if checkroutine(p,oldp) xor inv then //found one StoreResultRoutine(base+ptruint(p)-ptruint(buffer),p); inc(p, stepsize); inc(oldp, stepsize); end; end; end; end; procedure TScanner.nextnextscanmemAll(addresslist: pointer; oldmemory: pointer; chunksize: integer); var i,j,k: dword; l: TVariableType; m: integer; maxindex: dword; vsize: dword; currentbase: ptruint; newmemory: array [0..4095] of byte; oldmem: pbytearray; alist: pbitaddressarray; actualread: ptrUint; so: Tscanoption; valuetype: TVariableType; phandle: thandle; inv: boolean; begin inv:=inverseScan; i:=0; phandle:=processhandle; so:=scanoption; maxindex:=chunksize-1; vsize:=variablesize; //=8 oldmem:=oldmemory; alist:=addresslist; valuetype:=vtall; while i<=maxindex do begin currentbase:=alist[i].address and qword($FFFFFFFFFFFFF000); if (imaxindex then j:=maxindex; currentbase:=alist[i].address; if readprocessmemory(phandle,pointer(currentbase),@newmemory[0],(alist[j].address-currentbase)+vsize,actualread) then begin if compareToSavedScan then begin //clear typesmatch and set current address for l:=vtByte to vtDouble do typesmatch[l]:=false; if allCustom then for m:=0 to customtypecount-1 do customtypesmatch[m]:=false; currentaddress:=currentbase; for k:=i to j do begin if alist[k].address=currentaddress then begin if alist[k].bit>=$1000 then customtypesmatch[alist[k].bit-$1000]:=false else typesmatch[tvariabletype(alist[k].bit)]:=true; end else begin //new address reached if checkroutine(@newmemory[currentaddress-currentbase],savedscanhandler.getpointertoaddress(currentaddress,valuetype, nil )) then StoreResultRoutine(currentaddress,@newmemory[currentaddress-currentbase]); //clear typesmatch and set current address for l:=vtByte to vtDouble do typesmatch[l]:=false; for m:=0 to customtypecount-1 do customtypesmatch[m]:=false; currentaddress:=alist[k].address; if alist[k].bit>=$1000 then customtypesmatch[alist[k].bit-$1000]:=true else typesmatch[tvariabletype(alist[k].bit)]:=true; end; end; if checkroutine(@newmemory[currentaddress-currentbase],savedscanhandler.getpointertoaddress(currentaddress,valuetype, nil )) then StoreResultRoutine(currentaddress,@newmemory[currentaddress-currentbase]); end else begin //clear typesmatch and set current address for l:=vtByte to vtDouble do typesmatch[l]:=false; if allCustom then for m:=0 to customtypecount-1 do customtypesmatch[m]:=false; currentaddress:=currentbase; for k:=i to j do begin if alist[k].address=currentaddress then begin //add this one to the list if alist[k].bit>=$1000 then customtypesmatch[alist[k].bit-$1000]:=true else typesmatch[tvariabletype(alist[k].bit)]:=true; continue; end else begin //new address //we now have a list of entries with all the same address, k-1 points to the last one if CheckRoutine(@newmemory[currentaddress-currentbase],@oldmem[(k-1)*vsize]) then StoreResultRoutine(currentaddress,@newmemory[currentaddress-currentbase]); //clear typesmatch and set current address for l:=vtByte to vtDouble do typesmatch[l]:=false; for m:=0 to customtypecount-1 do customtypesmatch[m]:=false; currentaddress:=alist[k].address; if alist[k].bit>=$1000 then customtypesmatch[alist[k].bit-$1000]:=true else typesmatch[tvariabletype(alist[k].bit)]:=true; end; end; if CheckRoutine(@newmemory[currentaddress-currentbase],@oldmem[j*vsize]) then StoreResultRoutine(currentaddress,@newmemory[currentaddress-currentbase]); end; end; i:=j+1; end; end; procedure TScanner.nextnextscanmembinary(addresslist: pointer; chunksize: integer); var i,j,k,l: dword; maxindex: dword; vsize: dword; currentbase: ptruint; newmemory: array [0..4095] of byte; alist: PBitAddressArray; actualread: ptrUint; lastaddress: ptruint; scannedbitlist: array [0..7] of boolean; phandle: thandle; begin i:=0; maxindex:=chunksize-1; vsize:=variablesize; alist:=addresslist; currentbase:=0; phandle:=processhandle; while i<=maxindex do begin currentbase:=alist[i].address and qword($FFFFFFFFFFFFF000); if imaxindex then j:=maxindex; currentbase:=alist[i].address; if readprocessmemory(phandle,pointer(currentbase),@newmemory[0],(alist[j].address-currentbase)+vsize,actualread) then begin k:=i; while k<=j do begin if CheckRoutine(@newmemory[alist[k].address-currentbase],nil) then begin for l:=0 to 7 do scannedbitlist[l]:=false; lastaddress:=alist[k].address; while (alist[k].address=lastaddress) and (k<=j) do //nugfix might cause performance loss, check it later begin //add bits to the scanned bitlist scannedbitlist[alist[k].bit]:=true; inc(k); end; dec(k); for l:=0 to 7 do if not scannedbitlist[l] then binaryresults[l]:=false; //if it wasn't scanned, but the result was true, then set it to false StoreResultRoutine(lastaddress,@newmemory[lastaddress-currentbase]); end; inc(k); //next end; end; i:=j+1; end; end; procedure TScanner.nextnextscanmem(addresslist: pointer; oldmemory: pointer; chunksize: integer); { gather addresses in a 4K region and readprocessmemory the difference between (so if only 2 addresses in a 4K block and address 1 is at 0 and address 2 at 2k, then only read 2k) note: Do a test to see how many cpu cycles it costs to read 4K and 4bytes and find out the minimum number for mos toptimal speed done: 4 byte scan takes an average of 4000 cycles (due to caches etc...) 4096 byte scan takes an average of 6500 cycles ... 2048 byte scan takes an average of 5900 cycles ... conclusion: only when there is 1 address in the list, scan 1 address, else scan more } var i,j,k: dword; maxindex: dword; vsize: dword; currentbase: ptruint; newmemory: array [0..4095] of byte; oldmem: pbytearray; alist: PPtrUintArray; actualread: ptrUint; so: Tscanoption; valuetype: TVariableType; phandle: thandle; inv: boolean; begin inv:=inverseScan; i:=0; so:=scanoption; maxindex:=chunksize-1; vsize:=variablesize; oldmem:=oldmemory; alist:=addresslist; phandle:=processhandle; valuetype:=vtdword; case variableType of vtByte: valuetype:=vtbyte; vtWord: valuetype:=vtword; vtDWord: valuetype:=vtdword; vtsingle: valuetype:=vtsingle; vtdouble: valuetype:=vtdouble; vtQword: valuetype:=vtQword; vtAll: valuetype:=vtall; vtCustom: begin case vsize of 1: valuetype:=vtbyte; 2: valuetype:=vtword; 4: valuetype:=vtdword; 8: valuetype:=vtQword; else valuetype:=vtdword; end; end; end; while i<=maxindex do begin currentbase:=alist[i] and qword($FFFFFFFFFFFFF000); if imaxindex then j:=maxindex; currentbase:=alist[i]; if readprocessmemory(phandle,pointer(currentbase),@newmemory[0],(alist[j]-currentbase)+vsize,actualread) then begin if compareToSavedScan then begin for k:=i to j do begin currentaddress:=alist[k]; if checkroutine(@newmemory[alist[k]-currentbase],savedscanhandler.getpointertoaddress(alist[k],valuetype, customType )) xor inv then StoreResultRoutine(alist[k],@newmemory[alist[k]-currentbase]); end; end else begin for k:=i to j do begin currentaddress:=alist[k]; if CheckRoutine(@newmemory[alist[k]-currentbase],@oldmem[k*vsize]) xor inv then StoreResultRoutine(alist[k],@newmemory[alist[k]-currentbase]); end; end; end; i:=j+1; end; end; procedure TScanner.configurescanroutine; { Parse scanvalue1 and scanvalue2 accordingly to the scanoptions and type and fill in class variables that will be used for the scans } var FloatSettings: TFormatSettings; BinaryString,andmask,bitmask: string; i,j: integer; foundbuffersize: integer; td: double; s: string; begin value:=0; dvalue:=0; maxfound:=buffersize; {$ifdef customtypeimplemented} if variableType = vtCustom then begin //possible override if customtype.bytesize>16 then begin maxfound:=(buffersize*16) div customtype.bytesize; //get decent max size but not a redicilous size if maxfound<=0 then maxfound:=1; end; end else if (variableType = vtAll) and (vtCustom in ScanAllTypes) then begin i:=max(8, MaxCustomTypeSize); if i>16 then begin maxfound:=(buffersize*16) div i; if maxfound<=0 then maxfound:=1; end; end; {$ENDIF} OutputDebugString('configurescanroutine'); OutputDebugString('Config 1'); foundbuffersize:=0; //fill FloatSettings with formatting data (e.g difference between , and . for decimal) //GetLocaleFormatSettings(GetThreadLocale, FloatSettings); FloatSettings:=DefaultFormatSettings; if variableType=vtGrouped then begin groupdata:=TGroupData.create(scanvalue1, self); end else if scanOption in [soCustom, soExactValue,soValueBetween,soBiggerThan,soSmallerThan, soDecreasedValueBy, soIncreasedValueBy] then begin //user input is given if scanvalue1='' then raise exception.Create(rsPleaseFillSomethingIn); if variableType in [vtByte,vtWord,vtDWord,vtQword,vtAll,vtCustom] then begin //parse scanvalue1 try if hexadecimal then value:=StrToQWord('$'+scanvalue1) else value:=strtoqwordex(scanvalue1); except if (variableType=vtAll) or (percentage) then begin try dvalue:=strtofloat(scanvalue1,FloatSettings); except if FloatSettings.DecimalSeparator=',' then FloatSettings.DecimalSeparator:='.' else FloatSettings.DecimalSeparator:=','; //try again //todo: Add lua support for unix try dvalue:=strtofloat(scanvalue1,FloatSettings); except //see if lua knows better {$IFNDEF UNIX} try dvalue:=lua_strtofloat(scanvalue1); except {$ENDIF} raise exception.Create(Format(rsIsNotAValidValue, [scanvalue1])); {$IFNDEF UNIX} end; {$ENDIF} end; end; value:=trunc(dvalue); end else begin //not a float type, perhaps lua knows how to handle it {$IFNDEF UNIX} try value:=lua_strtoint(scanvalue1); except {$ENDIF} raise exception.Create(Format(rsIsAnInvalidValue, [scanvalue1])); {$IFNDEF UNIX} end; {$ENDIF} end; end; if scanOption=soValueBetween then begin //also parse scanvalue2 try if hexadecimal then value2:=StrToQWord('$'+scanvalue2) else value2:=StrToQwordEx(scanvalue2); except if (variableType=vtAll) or (percentage) then begin try dvalue:=strtofloat(scanvalue2,FloatSettings); except if FloatSettings.DecimalSeparator=',' then FloatSettings.DecimalSeparator:='.' else FloatSettings.DecimalSeparator:=','; //try again try dvalue:=strtofloat(scanvalue2,FloatSettings); except //see if lua knows better {$IFNDEF UNIX} try dvalue:=lua_strtofloat(scanvalue2); except {$ENDIF} raise exception.Create(Format(rsIsNotAValidValue, [scanvalue2])); {$IFNDEF UNIX} end; {$ENDIF} end; end; value2:=trunc(dvalue); end else begin //perhaps lua knows what it is {$IFNDEF UNIX} try value2:=lua_strtoint(scanvalue2); except {$ENDIF} raise exception.Create(Format(rsIsAnInvalidValue, [scanvalue2])); {$IFNDEF UNIX} end; {$ENDIF} end; end; end; end; if percentage or (variableType in [vtsingle,vtDouble,vtAll, vtCustom]) then begin try if hexadecimal then dvalue:=nan else dvalue:=strtofloat(scanvalue1,FloatSettings); except if FloatSettings.DecimalSeparator=',' then FloatSettings.DecimalSeparator:='.' else FloatSettings.DecimalSeparator:=','; //try again try dvalue:=strtofloat(scanvalue1,FloatSettings); except //try lua {$IFNDEF UNIX} try dvalue:=lua_strtofloat(scanvalue1); except {$ENDIF} raise exception.Create(Format(rsIsNotAValidValue, [scanvalue1])); {$IFNDEF UNIX} end; {$ENDIF} end; end; if percentage or (scanoption=soValueBetween) then begin try if hexadecimal then dvalue2:=nan else dvalue2:=strtofloat(scanvalue2,FloatSettings); except if FloatSettings.DecimalSeparator=',' then FloatSettings.DecimalSeparator:='.' else FloatSettings.DecimalSeparator:=','; //try again try dvalue2:=strtofloat(scanvalue2,FloatSettings); except //and again {$IFNDEF UNIX} try dvalue2:=lua_strtofloat(scanvalue2); except {$ENDIF} raise exception.Create(Format(rsIsNotAValidValue, [scanvalue2])); {$IFNDEF UNIX} end; {$ENDIF} end; end; end; if percentage then begin if dvalue>dvalue2 then begin td:=dvalue; dvalue:=dvalue2; dvalue2:=td; end; dvalue:=dvalue / 100; dvalue2:=dvalue2 / 100; end; svalue:=dvalue; svalue2:=dvalue2; floataccuracy:=pos(FloatSettings.DecimalSeparator,scanvalue1); if floataccuracy>0 then floataccuracy:=length(scanvalue1)-floataccuracy; if not percentage then begin svalue:=RoundTo(svalue,-floataccuracy); svalue2:=RoundTo(svalue2,-floataccuracy); dvalue:=RoundTo(dvalue,-floataccuracy); dvalue2:=RoundTo(dvalue2,-floataccuracy); end; mindvalue:=dvalue-(1/(power(10,floataccuracy))); maxdvalue:=dvalue+(1/(power(10,floataccuracy))); minsvalue:=svalue-(1/(power(10,floataccuracy))); maxsvalue:=svalue+(1/(power(10,floataccuracy))); end; if variableType = vtString then begin widescanvalue1:=UTF8ToUTF16(scanvalue1); end; nibbleSupport:=false; if variabletype = vtByteArray then begin ConvertStringToBytes(trim(scanvalue1),hexadecimal,abs_arraytofind, true); abs_arraylength:=length(abs_arraytofind); for i:=0 to abs_arraylength-1 do if (abs_arraytofind[i]<0) and (abs_arraytofind[i]<>-1) then begin nibbleSupport:=true; break; end; end; if variableType = vtByteArrays then begin //(bytearray1)(bytearray2)(bytearray3)(...)(...)(...)(...)..... i:=WordCount(scanvalue1, ['(',')']); setlength(mabs, i); for i:=0 to length(mabs)-1 do begin s:=ExtractWord(i+1, scanvalue1, ['(',')']); ConvertStringToBytes(trim(s),hexadecimal,mabs[i].arraytofind, true); mabs[i].arraylength:=length(mabs[i].arraytofind); mabs[i].foundaddress:=0; for j:=0 to mabs[i].arraylength-1 do if (mabs[i].arraytofind[j]<0) and (mabs[i].arraytofind[j]<>-1) then begin nibbleSupport:=true; break; end; end; mabs_arraylength:=length(mabs); end; if variableType = vtBinary then begin if binaryStringAsDecimal then begin if hexadecimal then begin scanvalue1:=scanvalue1+'$'; scanvalue2:=scanvalue2+'$'; end; binarystring:=parsers.inttobin(strtoint(trim(scanvalue1))) end else binarystring:=scanvalue1; andmask:=''; bitmask:=''; for i:=1 to length(binarystring) do begin if binarystring[i] in ['?','*'] then begin andmask:=andmask+'0'; bitmask:=bitmask+'0'; end else if binarystring[i] in ['0','1'] then begin andmask:=andmask+'1'; bitmask:=bitmask+binarystring[i]; end else if not (binarystring[i] in [' ',#8]) then raise exception.Create(Format(rsIsNotAValidNotation, [binarystring])); end; self.andmask:=BinToInt(andmask); self.bitmask:=bintoint(bitmask); end; end; OutputDebugString('Config 2'); FlushRoutine:=genericFlush; //change if not so if variableType in [vtbinary,vtall] then begin getmem(CurrentAddressBuffer,maxfound*sizeof(Tbitaddress)); getmem(SecondaryAddressBuffer,maxfound*sizeof(Tbitaddress)); end else if variabletype = vtGrouped then begin //stored as: //Address, offset1, offset2, offset3..... //assuming the rare occasion of a blocksize bigger than 65535 dword offset size is chosen getmem(CurrentAddressBuffer,maxfound*(sizeof(ptruint)+sizeof(dword)*groupdata.groupdatalength)); getmem(SecondaryAddressBuffer,maxfound*(sizeof(ptruint)+sizeof(dword)*1+groupdata.groupdatalength)); end else begin getmem(CurrentAddressBuffer,maxfound*sizeof(ptruint)); getmem(SecondaryAddressBuffer,maxfound*sizeof(ptruint)); end; OutputDebugString('Config 3'); if compareToSavedScan then //create a first scan handler begin OutputDebugString('Compare to saved scan'); savedscanhandler:=Tsavedscanhandler.create(scandir,savedscanname); end; OutputDebugString('Config 3.1'); OutputDebugString('scanOption='+inttostr(integer(scanOption))); if (scanOption in [soIncreasedValueBy, soDecreasedValueBy]) and (value=0) and (dvalue=0) then scanOption:=soUnchanged; case variableType of vtByte: begin //byte config FoundBufferSize:=buffersize*1; StoreResultRoutine:=ByteSaveResult; case scanOption of soExactValue: checkRoutine:=byteExact; soValueBetween: if percentage then checkroutine:=byteBetweenPercentage else checkroutine:=byteBetween; soBiggerThan: checkroutine:=byteBiggerThan; soSmallerThan: checkroutine:=byteSmallerThan; soIncreasedValue: checkroutine:=byteIncreasedValue; soIncreasedValueBy: if percentage then checkroutine:=byteIncreasedValueByPercentage else checkroutine:=byteIncreasedValueBy; soDecreasedValue: checkroutine:=byteDecreasedValue; soDecreasedValueBy: if percentage then checkroutine:=byteDecreasedValueByPercentage else checkroutine:=byteDecreasedValueBy; soChanged: checkroutine:=byteChanged; soUnChanged: checkroutine:=byteUnchanged; end; end; vtWord: begin //word config FoundBufferSize:=buffersize*2; StoreResultRoutine:=WordSaveResult; case scanOption of soExactValue: checkRoutine:=wordExact; soValueBetween: if percentage then checkroutine:=wordBetweenPercentage else checkroutine:=wordBetween; soBiggerThan: checkroutine:=wordBiggerThan; soSmallerThan: checkroutine:=wordSmallerThan; soIncreasedValue: checkroutine:=wordIncreasedValue; soIncreasedValueBy: if percentage then checkroutine:=wordIncreasedValueByPercentage else checkroutine:=wordIncreasedValueBy; soDecreasedValue: checkroutine:=wordDecreasedValue; soDecreasedValueBy: if percentage then checkroutine:=wordDecreasedValueByPercentage else checkroutine:=wordDecreasedValueBy; soChanged: checkroutine:=wordChanged; soUnChanged: checkroutine:=wordUnchanged; end; end; vtDWord: begin //dword config OutputDebugString('Config 4'); FoundBufferSize:=buffersize*4; StoreResultRoutine:=DWordSaveResult; case scanOption of soExactValue: checkRoutine:=dwordExact; soValueBetween: if percentage then checkroutine:=dwordBetweenPercentage else checkroutine:=dwordBetween; soBiggerThan: checkroutine:=dwordBiggerThan; soSmallerThan: checkroutine:=dwordSmallerThan; soIncreasedValue: checkroutine:=dwordIncreasedValue; soIncreasedValueBy: if percentage then checkroutine:=dwordIncreasedValueByPercentage else checkroutine:=dwordIncreasedValueBy; soDecreasedValue: checkroutine:=dwordDecreasedValue; soDecreasedValueBy: if percentage then checkroutine:=dwordDecreasedValueByPercentage else checkroutine:=dwordDecreasedValueBy; soChanged: checkroutine:=dwordChanged; soUnChanged: checkroutine:=dwordUnchanged; end; OutputDebugString('Config 5'); end; vtQWord: begin //qword config FoundBufferSize:=buffersize*8; StoreResultRoutine:=QWordSaveResult; case scanOption of soExactValue: checkRoutine:=qwordExact; soValueBetween: if percentage then checkroutine:=qwordBetweenPercentage else checkroutine:=qwordBetween; soBiggerThan: checkroutine:=qwordBiggerThan; soSmallerThan: checkroutine:=qwordSmallerThan; soIncreasedValue: checkroutine:=qwordIncreasedValue; soIncreasedValueBy: if percentage then checkroutine:=qwordIncreasedValueByPercentage else checkroutine:=qwordIncreasedValueBy; soDecreasedValue: checkroutine:=qwordDecreasedValue; soDecreasedValueBy: if percentage then checkroutine:=qwordDecreasedValueByPercentage else checkroutine:=qwordDecreasedValueBy; soChanged: checkroutine:=qwordChanged; soUnChanged: checkroutine:=qwordUnchanged; end; end; vtSingle: begin //single config FoundBufferSize:=buffersize*4; StoreResultRoutine:=SingleSaveResult; case scanOption of soExactValue: checkRoutine:=singleExact; soValueBetween: if percentage then checkroutine:=singleBetweenPercentage else checkroutine:=singleBetween; soBiggerThan: checkroutine:=singleBiggerThan; soSmallerThan: checkroutine:=singleSmallerThan; soIncreasedValue: checkroutine:=singleIncreasedValue; soIncreasedValueBy: if percentage then checkroutine:=singleIncreasedValueByPercentage else checkroutine:=singleIncreasedValueBy; soDecreasedValue: checkroutine:=singleDecreasedValue; soDecreasedValueBy: if percentage then checkroutine:=singleDecreasedValueByPercentage else checkroutine:=singleDecreasedValueBy; soChanged: checkroutine:=singleChanged; soUnChanged: checkroutine:=singleUnchanged; end; end; vtDouble: begin //double config FoundBufferSize:=buffersize*8; StoreResultRoutine:=doubleSaveResult; case scanOption of soExactValue: checkRoutine:=doubleExact; soValueBetween: if percentage then checkroutine:=DoubleBetweenPercentage else checkroutine:=doubleBetween; soBiggerThan: checkroutine:=doubleBiggerThan; soSmallerThan: checkroutine:=doubleSmallerThan; soIncreasedValue: checkroutine:=doubleIncreasedValue; soIncreasedValueBy: if percentage then checkroutine:=doubleIncreasedValueByPercentage else checkroutine:=doubleIncreasedValueBy; soDecreasedValue: checkroutine:=doubleDecreasedValue; soDecreasedValueBy: if percentage then checkroutine:=doubleDecreasedValueByPercentage else checkroutine:=doubleDecreasedValueBy; soChanged: checkroutine:=doubleChanged; soUnChanged: checkroutine:=doubleUnchanged; end; end; vtByteArray, vtByteArrays: begin if nibbleSupport then CheckRoutine:=ArrayOfByteExact_NibbleWildcardSupport else CheckRoutine:=ArrayOfByteExact; flushroutine:=stringFlush; StoreResultRoutine:=ArrayOfByteSaveResult; FoundBufferSize:=0; end; vtBinary: begin CheckRoutine:=BinaryExact; StoreResultRoutine:=binarySaveResult; flushroutine:=binaryFlush; end; vtString: begin FoundBufferSize:=0; //check if unicode or casesensitive is used if not casesensitive then begin scanvalue1:=uppercase(scanvalue1); widescanvalue1:=uppercase(widescanvalue1); end; flushroutine:=stringFlush; case scanOption of soExactValue: begin if casesensitive and unicode then CheckRoutine:=CaseSensitiveUnicodeStringExact; if casesensitive and not unicode then CheckRoutine:=CaseSensitiveAnsiStringExact; if not casesensitive and unicode then CheckRoutine:=CaseInsensitiveUnicodeStringExact; if not casesensitive and not unicode then CheckRoutine:=CaseInsensitiveAnsiStringExact; end; end; StoreResultRoutine:=arrayOfByteSaveResult; //arrayOfByteSaveResult is compatible since it saves only the address end; vtAll: begin //almost like binary variablesize:=8; //override these variables (8 is big enough for even the double type) {$ifdef customtypeimplemented} if allCustom then //find out the biggest customtype size begin customtypecount:=customtypes.count; setlength(customtypesmatch, customtypecount); for i:=0 to customTypes.count-1 do variablesize:=max(variablesize, TCustomType(customtypes[i]).bytesize); end; {$ENDIF} fastscanalignsize:=1; FoundBufferSize:=maxfound*variablesize; StoreResultRoutine:=allSaveResult; FlushRoutine:=allFlush; case scanOption of soExactValue: checkRoutine:=allExact; soValueBetween: if percentage then checkroutine:=allBetweenPercentage else checkroutine:=allBetween; soBiggerThan: checkroutine:=allBiggerThan; soSmallerThan: checkroutine:=allSmallerThan; soIncreasedValue: checkroutine:=allIncreasedValue; soIncreasedValueBy: if percentage then checkroutine:=allIncreasedValueByPercentage else checkroutine:=allIncreasedValueBy; soDecreasedValue: checkroutine:=allDecreasedValue; soDecreasedValueBy: if percentage then checkroutine:=allDecreasedValueByPercentage else checkroutine:=allDecreasedValueBy; soChanged: checkroutine:=allChanged; soUnChanged: checkroutine:=allUnchanged; end; //the other types have to be filled in by the nextscan routines end; {$ifdef customtypeimplemented} vtCustom: begin //dword config FoundBufferSize:=maxfound*customtype.bytesize; if FoundBufferSize>16*1024*1024 then foundbuffersize:=16*1024*1024; StoreResultRoutine:=GenericSaveResult; if customType.scriptUsesFloat then begin case scanOption of soExactValue: checkRoutine:=customFloatExact; soValueBetween: if percentage then checkroutine:=customFloatBetweenPercentage else checkroutine:=customFloatBetween; soBiggerThan: checkroutine:=customFloatBiggerThan; soSmallerThan: checkroutine:=customFloatSmallerThan; soIncreasedValue: checkroutine:=customFloatIncreasedValue; soIncreasedValueBy: if percentage then checkroutine:=customFloatIncreasedValueByPercentage else checkroutine:=customFloatIncreasedValueBy; soDecreasedValue: checkroutine:=customFloatDecreasedValue; soDecreasedValueBy: if percentage then checkroutine:=customFloatDecreasedValueByPercentage else checkroutine:=customFloatDecreasedValueBy; soChanged: checkroutine:=customFloatChanged; soUnChanged: checkroutine:=customFloatUnchanged; end; end else begin case scanOption of soExactValue: checkRoutine:=customExact; soValueBetween: if percentage then checkroutine:=customBetweenPercentage else checkroutine:=customBetween; soBiggerThan: checkroutine:=customBiggerThan; soSmallerThan: checkroutine:=customSmallerThan; soIncreasedValue: checkroutine:=customIncreasedValue; soIncreasedValueBy: if percentage then checkroutine:=customIncreasedValueByPercentage else checkroutine:=customIncreasedValueBy; soDecreasedValue: checkroutine:=customDecreasedValue; soDecreasedValueBy: if percentage then checkroutine:=customDecreasedValueByPercentage else checkroutine:=customDecreasedValueBy; soChanged: checkroutine:=customChanged; soUnChanged: checkroutine:=customUnchanged; end; end; end; {$ENDIF} vtGrouped: begin if groupdata.outoforder then CheckRoutine:=groupdata.compareblock_outoforder else checkroutine:=groupdata.compareblock; fastscanmethod:=fsmAligned; fastscanalignsize:=groupdata.alignsize; StoreResultRoutine:=groupSaveResult; flushroutine:=groupFlush; FoundBufferSize:=0; variablesize:=groupdata.blocksize; //this is why there is no nextscan data to compare against (varsize of 4096) if scannernr=0 then //write the header for groupdata (after the normal header comes the number of offsets) Addressfile.WriteBuffer(groupdata.groupdatalength, sizeof(groupdata.groupdatalength)); end; end; OutputDebugString('Config 6'); getmem(CurrentFoundBuffer,FoundBufferSize); getmem(SecondaryFoundBuffer,FoundBufferSize); OutputDebugString('configurescanroutine: Normal exit'); end; procedure TScanner.nextNextscan; var oldAddressfile: TFileStream; oldMemoryfile: TFileStream; i: qword; j: integer; stopindex: qword; chunksize: integer; oldaddresses: array of PtrUint; oldaddressesb: array of tbitaddress; oldaddressesGroup: PByteArray; oldmemory: pointer; groupelementsize: integer; begin if startentry>stopentry then //don't bother exit; oldAddressesGroup:=nil; configurescanroutine; oldAddressFile:=nil; oldMemoryFile:=nil; oldmemory:=nil; try oldAddressFile:=TFileStream.Create(scandir+'ADDRESSES.TMP',fmOpenRead or fmShareDenyNone); oldMemoryFile:=TFileStream.Create(scandir+'MEMORY.TMP',fmOpenRead or fmShareDenyNone); //set the current index to startentry stopindex:=stopentry-startentry; //going from 0 to stopindex if self.variableType in [vtBinary,vtall] then begin //addressfile of tbitaddresstype setlength(oldaddressesb,buffersize); oldAddressFile.seek(7+sizeof(TBitAddress)*startentry,soFromBeginning); //header+addresssize*startentry if self.variableType=vtall then begin oldmemory:=virtualAlloc(nil,buffersize*variablesize,MEM_COMMIT or MEM_TOP_DOWN , PAGE_READWRITE); if oldmemory=nil then raise exception.Create(Format(rsErrorAllocatingBytesForTheOldResults, [inttostr(buffersize*variablesize), inttostr(buffersize), inttostr(variablesize)])); end; oldMemoryFile.seek(variablesize*startentry,soFromBeginning); end else if self.variableType = vtGrouped then begin //addressfile of 7+offsetcount+(address+offsetcount)+.... setlength(oldaddresses,buffersize); //normal addresslist, just a special addressfile groupelementsize:=(sizeof(ptruint)+sizeof(dword)*PreviousOffsetCount); getmem(oldaddressesGroup, buffersize*groupelementsize); oldAddressFile.seek(7+sizeof(dword)+startentry*groupelementsize,soFromBeginning); //header+offsetcount+startentry*addressEntrysize (address followed by offsets) end else begin setlength(oldaddresses,buffersize); oldmemory:=virtualAlloc(nil,buffersize*variablesize,MEM_COMMIT or MEM_TOP_DOWN , PAGE_READWRITE); if oldmemory=nil then raise exception.Create(Format(rsErrorAllocatingBytesForTheOldResults, [inttostr(buffersize*variablesize), inttostr(buffersize), inttostr(variablesize)])); oldAddressFile.seek(7+sizeof(ptruint)*startentry,soFromBeginning); //header+addresssize*startentry if not (self.variableType in [vtString,vtByteArray, vtByteArrays]) then oldMemoryFile.seek(variablesize*startentry,soFromBeginning); end; //read in chunks of buffersize i:=0; while i<=stopindex do begin chunksize:=stopindex-i+1; if chunksize>buffersize then chunksize:=buffersize; case variabletype of vtBinary: begin oldAddressFile.ReadBuffer(oldaddressesb[0],chunksize*sizeof(tbitaddress)); nextnextscanmembinary(@oldaddressesb[0],chunksize); end; vtAll: begin oldAddressFile.ReadBuffer(oldaddressesb[0],chunksize*sizeof(tbitaddress)); if not compareToSavedScan then oldMemoryFile.ReadBuffer(oldmemory^,chunksize*variablesize); nextnextscanmemall(@oldaddressesb[0],oldmemory,chunksize); end; else begin if variableType = vtGrouped then begin //load the grouped address list and convert to a regular addresslist oldAddressFile.ReadBuffer(oldaddressesGroup[0],chunksize*groupelementsize); for j:=0 to chunksize-1 do oldAddresses[j]:=PGroupAddress(@OldaddressesGroup[j*groupelementsize]).address; end else //normal addresslist, no need to convert begin oldAddressFile.ReadBuffer(oldaddresses[0],chunksize*sizeof(ptruint)); if not compareToSavedScan then begin if not (self.variableType in [vtString,vtByteArray, vtByteArrays]) then //skip the types with no previous result stored oldMemoryFile.ReadBuffer(oldmemory^,chunksize*variablesize); end; end; nextnextscanmem(@oldaddresses[0],oldmemory,chunksize); end; end; inc(scanned,chunksize); inc(i,chunksize); end; flushroutine; //save all results temporarily stored in memory finally if oldAddressFile<>nil then oldAddressFile.free; if oldMemoryFile<>nil then oldMemoryFile.free; if oldmemory<>nil then virtualfree(oldmemory,0,MEM_RELEASE); if oldaddressesGroup<>nil then begin freemem(oldaddressesGroup); oldaddressesGroup:=nil; end; end; end; procedure TScanner.firstNextscan; var i: integer; size: integer; currentbase: PtrUint; startregion: integer; stopregion: integer; memorybuffer: ^byte; oldbuffer: ^byte; toread: integer; actualread: ptrUint; phandle: thandle; begin phandle:=processhandle; startregion:=_startregion; //using a variable so stack can be used, with possibility of register stopregion:=_stopregion; //allocate a buffer for reading the new memory buffer memorybuffer:=virtualAlloc(nil,maxregionsize+(variablesize-1),MEM_COMMIT or MEM_TOP_DOWN , PAGE_READWRITE); try //configure some variables and function pointers configurescanroutine; for i:=startregion to stopregion do begin if terminated or OwningScanController.Terminated then exit; if i=startregion then begin currentbase:=startaddress; toread:=OwningScanController.memregion[i].MemorySize-(startaddress-OwningScanController.memregion[i].BaseAddress); //set oldbuffer and point to the exact start oldbuffer:=OwningScanController.memregion[i].startaddress; inc(oldbuffer, startaddress-OwningScanController.memregion[i].BaseAddress); end else begin currentbase:=OwningScanController.memregion[i].BaseAddress; toread:=OwningScanController.memregion[i].MemorySize; oldbuffer:=OwningScanController.memregion[i].startaddress; end; if (i=stopregion) and ((currentbase+toread)>stopaddress) then toread:=stopaddress-currentbase; //also try to read the last few bytes and add variablesize if needed if (currentbase+toread)<(OwningScanController.memregion[i].BaseAddress+OwningScanController.memregion[i].MemorySize-variablesize) then inc(toread, variablesize-1); if toread>0 then //temp bugfix to find the real bug (what causes it?) repeat size:=toread; if (size>buffersize) then size:=buffersize; actualread:=0; if sizesoUnknownValue then begin //not unknown initial memorybuffer:=virtualAlloc(nil,maxregionsize+variablesize+16,MEM_COMMIT or MEM_TOP_DOWN , PAGE_READWRITE); configurescanroutine; end else //it is a unknown initial value begin {$ifdef lowmemoryusage} //use the previousmemoryfile //the memory.tmp file must have been generated with the correct size before calling this previousmemoryfile:=Tfilestream.create(scandir+'MEMORY.TMP', fmOpenWrite or fmShareDenyNone); memorybuffer:=virtualAlloc(nil,maxregionsize,MEM_COMMIT or MEM_TOP_DOWN , PAGE_READWRITE); {$else} //use the previousmemorybuffer instead memorybuffer:=pointer(PtrUint(OwningScanController.OwningMemScan.previousMemoryBuffer)+PtrUint(OwningScanController.memregion[startregion].startaddress)+(startaddress-OwningScanController.memregion[startregion].BaseAddress)); {$endif} variablesize:=1; //ignore end; //now save the region between startaddress and stopaddress and create own memregion list setlength(memregions,16); for i:=startregion to stopregion do begin if terminated or OwningScanController.Terminated then exit; if i=startregion then begin currentbase:=startaddress; toread:=OwningScanController.memregion[i].MemorySize-(startaddress-OwningScanController.memregion[i].BaseAddress); end else begin currentbase:=OwningScanController.memregion[i].BaseAddress; toread:=OwningScanController.memregion[i].MemorySize; end; if (i=stopregion) and ((currentbase+toread)>stopaddress) then toread:=stopaddress-currentbase; repeat //05955958 size:=toread; if (size>buffersize) then size:=buffersize; actualread:=0; //variablesize:=0; if size0)) then exit; until terminated or (toread=0); end; if (scanOption<>soUnknownValue) then flushroutine; //save results finally {$ifdef LOWMEMORYUSAGE} if previousmemoryfile<>nil then freeandnil(previousmemoryfile); if memorybuffer<>nil then virtualfree(memorybuffer,0,MEM_RELEASE); {$else} if (scanOption<>soUnknownValue) and (memorybuffer<>nil) then virtualfree(memorybuffer,0,MEM_RELEASE); {$endif} end; end; procedure TScanner.execute; begin (* {$if defined(cpui386) or defined(cpux86_64)} Set8087CW($133f); //disable floating point exceptions in this thread SetSSECSR($1f80); {$endif} *) SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]); try scanwriter:=TScanfilewriter.create(self,self.OwningScanController,addressfile,memoryfile); if scantype=stFirstScan then firstscan; if scantype=stNextScan then begin if useNextNextScan then nextnextscan else firstnextscan; end; //tell scanwriter to stop scanwriter.flush; if scanwriter.writeError then raise exception.Create(Format(rsDiskWriteError, [scanwriter.errorString])); except on e: exception do begin haserror:=true; errorstring:=rsThread+inttostr(scannernr)+':'+e.message; log('Scanner exception:'+errorstring); //tell all siblings to terminate, something messed up //and I can just do this, since the ScanController is waiting for us, and terminate is pretty much atomic //for i:=0 to length(OwningScanController.scanners)-1 do OwningScanController.Terminate; end; end; isdone:=true; end; destructor TScanner.destroy; begin outputdebugstring('Destroying a scanner'); if groupdata<>nil then freeandnil(groupdata); if AddressFile<>nil then //can be made nil by the scancontroller begin freeandnil(Addressfile); DeleteFile(scandir+'ADDRESSES-'+inttostr(ThreadID)+'.TMP'); end; if MemoryFile<>nil then begin freeandnil(MemoryFile); DeleteFile(scandir+'MEMORY-'+inttostr(ThreadID)+'.TMP'); end; if scanwriter<>nil then freeandnil(scanwriter); if CurrentFoundBuffer<>nil then freemem(CurrentFoundBuffer); if SecondaryFoundBuffer<>nil then freemem(SecondaryFoundBuffer); if CurrentAddressBuffer<>nil then freemem(CurrentAddressBuffer); if SecondaryAddressBuffer<>nil then freemem(SecondaryAddressBuffer); CurrentFoundBuffer:=nil; SecondaryFoundBuffer:=nil; CurrentAddressBuffer:=nil; SecondaryAddressBuffer:=nil; if savedscanhandler<>nil then freeandnil(savedscanhandler); inherited destroy; end; constructor TScanner.create(suspended: boolean; scandir: string); begin inherited create(true); //do create the thread, I need the threadid self.scandir:=scandir; AddressFilename:=scandir+'ADDRESSES-'+inttostr(ThreadID)+'.TMP'; MemoryFilename:=scandir+'MEMORY-'+inttostr(ThreadID)+'.TMP'; AddressFile:=TFileStream.Create(AddressFilename,fmCreate or fmSharedenynone); MemoryFile:=TFileStream.Create(MemoryFilename,fmCreate or fmSharedenynone); Priority:=Globals.scanpriority; allByte:=vtByte in ScanAllTypes; allWord:=vtWord in ScanAllTypes; allDword:=vtDword in ScanAllTypes; allQword:=vtQword in ScanAllTypes; allFloat:=vtSingle in ScanAllTypes; allDouble:=vtDouble in ScanAllTypes; allCustom:=vtCustom in ScanAllTypes; if not suspended then start; //would be stupid, but ok... end; //===============TScanController===============// procedure TScanController.updategui; var totaladdressestoscan, currentlyscanned, foundcount: qword; begin //runs in mainthread if OwningMemScan.progressbar<>nil then begin OwningMemScan.progressbar.Position:=OwningMemScan.GetProgress(totaladdressestoscan,currentlyscanned, foundcount); {$ifdef windows} SetProgressValue(OwningMemScan.progressbar.Position, OwningMemScan.progressbar.Max); {$endif} if assigned(owningmemscan.OnGuiUpdate) then owningmemscan.OnGuiUpdate(OwningMemScan, totaladdressestoscan,currentlyscanned, foundcount); end; end; procedure TScanController.errorpopup; begin {$IFNDEF UNIX} messagedlg(errorstring,mtError,[mbok],0); {$ENDIF} end; procedure TScanController.fillVariableAndFastScanAlignSize; var s: string; i,c: integer; begin fastscan:=fastscanmethod<>fsmNotAligned; case variableType of vtByte: begin fastscanalignsize:=1; variablesize:=1; end; vtWord: begin fastscanalignsize:=2; variablesize:=2; end; vtDWord: begin fastscanalignsize:=4; variablesize:=4; end; vtQWord: begin fastscanalignsize:=4; variablesize:=8; end; vtSingle: begin fastscanalignsize:=4; variablesize:=4; end; vtDouble: begin fastscanalignsize:=4; variablesize:=8; end; vtByteArray: begin OutputDebugString('aobscan for '+scanvalue1); if scanoption<>soUnknownValue then variablesize:=getBytecountArrayOfByteString(scanvalue1) else variablesize:=1; fastscanalignsize:=1; self.OwningMemScan.arrayLength:=variablesize; end; vtByteArrays: begin OutputDebugString('maobscan for '+scanvalue1); if scanoption<>soUnknownValue then begin //find the max size of the aob's c:=WordCount(scanvalue1, ['(',')']); variablesize:=1; for i:=0 to c-1 do begin s:=ExtractWord(i+1, scanvalue1, ['(',')']); variablesize:=max(variablesize, getBytecountArrayOfByteString(s)); end; end else variablesize:=1; fastscanalignsize:=1; self.OwningMemScan.arrayLength:=variablesize; end; vtBinary: begin if scanoption<>soUnknownValue then variablesize:=getBytecountBinaryString(scanvalue1,binaryStringAsDecimal) else variablesize:=1; fastscanalignsize:=1; //store some info for lookup if binaryStringAsDecimal then //first convert do binarystring s:=parsers.inttobin(strtoint(scanvalue1)) else s:=trim(scanvalue1); self.OwningMemScan.binaryLength:=length(s); end; vtString: begin if scanoption<>soUnknownValue then begin variablesize:=length(scanvalue1); if unicode then variablesize:=variablesize*2; end else variablesize:=1; fastscanalignsize:=1; self.OwningMemScan.stringUnicode:=unicode; self.OwningMemScan.stringLength:=length(scanvalue1); end; vtAll: begin variablesize:=8; {$ifdef customtypeimplemented} if vtcustom in ScanAllTypes then //find out the biggest customtype size begin for i:=0 to customTypes.count-1 do variablesize:=max(variablesize, TCustomType(customtypes[i]).bytesize); end; {$ENDIF} fastscanalignsize:=1; end; {$ifdef customtypeimplemented} vtCustom: begin if customtype=nil then raise exception.create(rsMSCustomTypeIsNil); variablesize:=customtype.bytesize; fastscanalignsize:=1; end; {$ENDIF} end; if fastscan then //override the alignment if given begin if (fastscanmethod=fsmLastDigits) or (fastscanalignment<>0) then fastscanalignsize:=fastscanalignment; end; OutputDebugString('fillVariableAndFastScanAlignSize:'); OutputDebugString(format('variableType=%d',[integer(variableType)])); OutputDebugString(format('variablesize=%d',[variablesize])); OutputDebugString(format('fastscanalignsize=%d',[fastscanalignsize])); end; procedure TScanController.NextNextScan; { NextNextScan will read results of the previous scan, and pass it off to scanner threads } var AddressFile: TFileStream; blocksize: qword; i: integer; currententry: qword; datatype: string[6]; offsetcount: integer; begin offsetcount:=0; {$ifdef customtypeimplemented} if (variableType=vtCustom) and (customType<>nil) and (customtype.CustomTypeType=cttLuaScript) then threadcount:=1 else {$ENDIF} threadcount:=GetCPUCount; //read the results and split up AddressFile:=TFileStream.Create(OwningMemScan.ScanresultFolder+'ADDRESSES.TMP',fmOpenRead or fmShareDenyNone); try if variableType in [vtbinary,vtall] then //it uses a specific TBitAddress instead of a dword totalAddresses:=(addressfile.size-7) div sizeof(TBitAddress) else if variableType = vtGrouped then begin //read the number of offsets used previously (can be different from the current input) addressFile.Position:=7; addressFile.ReadBuffer(offsetcount, sizeof(offsetcount)); addressfile.Position:=0; //reset position //the addresslist is buildup of address,offset1,offset2,...,offsetcount-1, address, offset1, offset2, ...., offsetcount-1, address,..... totalAddresses:=(addressfile.size-7-sizeof(offsetcount)) div (sizeof(ptruint)+offsetcount*sizeof(dword)); end else totalAddresses:=(addressfile.size-7) div sizeof(ptruint); //split up into blocks //if totaladdresses>0 then begin blocksize:=totaladdresses div threadcount; if (blocksize=0) and (totaladdresses>0) then blocksize:=1; //in case of 4 threads and totaladdresses is 3 or less scannersCS.Enter; //block access by the mainthread on the scanners object, could scanner[14] has not yet been created when doing a progress request try setlength(scanners,threadcount); //setup the scanner threads currententry:=0; for i:=0 to threadcount-1 do begin scanners[i]:=tscanner.Create(true,OwningMemScan.ScanresultFolder); scanners[i].scannernr:=i; scanners[i].OwningScanController:=self; if totalAddresses>0 then begin scanners[i].startentry:=currententry; if i=threadcount-1 then scanners[i].stopentry:=totalAddresses-1 else scanners[i].stopentry:=currententry+blocksize; if scanners[i].stopentry>=totaladdresses then scanners[i].stopentry:=totalAddresses-1; end else begin scanners[i].startentry:=1; scanners[i].stopEntry:=0; //signals the scanner thread to quit end; currententry:=scanners[i].stopentry+1; //next thread will start at the next one scanners[i].compareToSavedScan:=compareToSavedScan; scanners[i].savedscanname:=savedscanname; scanners[i].scanType:=scanType; //stNextScan obviously scanners[i].scanoption:=scanoption; scanners[i].variableType:=VariableType; scanners[i].customType:=CustomType; scanners[i].roundingtype:=roundingtype; scanners[i].scanValue1:=scanvalue1; //usual scanvalue scanners[i].scanValue2:=scanValue2; //2nd value for between scan scanners[i].unicode:=unicode; scanners[i].caseSensitive:=caseSensitive; scanners[i].percentage:=percentage; scanners[i].hexadecimal:=hexadecimal; scanners[i].binaryStringAsDecimal:=binaryStringAsDecimal; scanners[i].fastscanalignsize:=fastscanalignsize; scanners[i].fastscanmethod:=fastscanmethod; scanners[i].fastscandigitcount:=fastscandigitcount; scanners[i].variablesize:=variablesize; scanners[i].useNextNextscan:=true; //address result scan so nextnextscan scanners[i].floatscanWithoutExponents:=floatscanWithoutExponents; scanners[i].inverseScan:=inverseScan; if variableType=vtGrouped then scanners[i].PreviousOffsetCount:=offsetcount; if i=0 then //first thread gets the header part begin datatype:='NORMAL'; scanners[i].AddressFile.WriteBuffer(datatype,sizeof(datatype)); end; end; finally scannersCS.leave; end; for i:=0 to length(scanners)-1 do scanners[i].start; OwningMemScan.found:=0; //and now we wait for i:=0 to threadcount-1 do begin while not (terminated or scanners[i].isdone) do begin {$ifdef android} if not scanners[i].Finished then sleep(25); {$endif} {$ifdef windows} WaitForSingleObject(scanners[i].Handle,25); //25ms, an eternity for a cpu {$endif} if OwningMemScan.progressbar<>nil then synchronize(updategui); end; //If terminated then stop the scanner thread and wait for it to finish if terminated then begin scanners[i].Terminate; scanners[i].WaitFor; end; if scanners[i].haserror then begin haserror:=true; errorstring:=scanners[i].errorstring; break; end; inc(OwningMemScan.found,scanners[i].totalfound); end; if OwningMemScan.progressbar<>nil then synchronize(updategui); if haserror then begin //synchronize(errorpopup); exit; end; end; finally addressfile.free; //release the file so it can be overwritten end; savescannerresults:=true; end; procedure TScanController.FirstNextScan; { FirstNextScan will read first scan regions, and pass it off to scanner threads } var i,j: integer; totalProcessMemorySize: qword; blocksize: dword; leftfromprevious: dword; offsetincurrentregion: PtrUint; currentblocksize: qword; datatype: string[6]; begin threadcount:=GetCPUCount; {$ifdef customtypeimplemented} if (variableType=vtCustom) and (customType<>nil) and (customtype.CustomTypeType=cttLuaScript) then threadcount:=1; {$ENDIF} totalProcessMemorySize:=0; memregion:=OwningMemscan.memRegion; memregionpos:=OwningMemscan.memRegionPos; {$ifdef LOWMEMORYUSAGE} if compareToSavedScan=false then begin compareToSavedScan:=true; savedscanname:='First'; end; {$endif} { read the addressfile and split it up into chunks for the scanner threads important variables: OwningMemScan.previousMemoryBuffer OwningMemscan.memRegion OwningMemscan.memRegionPos } //find total memoryammount for i:=0 to memRegionPos do inc(totalProcessMemorySize, memRegion[i].MemorySize); totalAddresses:=totalProcessMemorySize; //now split up into workloads Blocksize:=totalProcessMemorySize div threadcount; Blocksize:=blocksize-(blocksize mod 4096); //lastblock gets the missing bytes //spawn the thread, but don't run till configured scannersCS.Enter; //block access by the mainthread on the scanners object, could scanner[14] has not yet been created when doing a progress request try setlength(scanners,threadcount); j:=0; //start at memregion 0 leftfromprevious:=0; offsetincurrentregion:=0; for i:=0 to threadcount-1 do begin scanners[i]:=tscanner.Create(true, OwningMemScan.ScanresultFolder); scanners[i].scannernr:=i; scanners[i].OwningScanController:=self; scanners[i]._startregion:=j; scanners[i].startaddress:=memRegion[j].BaseAddress+offsetincurrentregion; scanners[i].maxregionsize:=0; if i=(threadcount-1) then begin //this is the last scanner scanners[i].stopaddress:=stopaddress; //let it go till the end scanners[i]._stopregion:=memregionpos-1; //define maxregionsize while jbuffersize then scanners[i].maxregionsize:=buffersize; //now configure the scanner thread with the same info this thread got, with some extra info scanners[i].compareToSavedScan:=compareToSavedScan; scanners[i].savedscanname:=savedscanname; scanners[i].scanType:=scanType; //stNextScan obviously scanners[i].scanoption:=scanoption; scanners[i].variableType:=VariableType; scanners[i].customType:=CustomType; scanners[i].roundingtype:=roundingtype; scanners[i].scanValue1:=scanvalue1; //usual scanvalue scanners[i].scanValue2:=scanValue2; //2nd value for between scan scanners[i].unicode:=unicode; scanners[i].caseSensitive:=caseSensitive; scanners[i].percentage:=percentage; scanners[i].hexadecimal:=hexadecimal; scanners[i].binaryStringAsDecimal:=binaryStringAsDecimal; scanners[i].fastscanalignsize:=fastscanalignsize; scanners[i].fastscanmethod:=fastscanmethod; scanners[i].fastscandigitcount:=fastscandigitcount; scanners[i].variablesize:=variablesize; scanners[i].useNextNextscan:=false; //region scan so firstnextscan scanners[i].floatscanWithoutExponents:=floatscanWithoutExponents; scanners[i].inverseScan:=inverseScan; if i=0 then //first thread gets the header part begin datatype:='NORMAL'; scanners[i].AddressFile.WriteBuffer(datatype,sizeof(datatype)); end; end; finally scannersCS.Leave; end; //all threads are created, so start them for i:=0 to length(scanners)-1 do scanners[i].start; savescannerresults:=true; OwningMemScan.found:=0; //and now we wait for i:=0 to threadcount-1 do begin while not (terminated or scanners[i].isdone) do begin {$IFDEF WINDOWS} WaitForSingleObject(scanners[i].Handle,25); //25ms, an eternity for a cpu if OwningMemScan.progressbar<>nil then synchronize(updategui); {$else} sleep(25); {$ENDIF} end; //If terminated then stop the scanner thread and wait for it to finish if terminated then begin scanners[i].Terminate; scanners[i].WaitFor; end; //scanners[i].WaitFor; //if the mainthread has to cancel, it has to tell the child scanners to terminate instead if scanners[i].haserror then begin haserror:=true; errorstring:=scanners[i].errorstring; break; end; inc(OwningMemScan.found,scanners[i].totalfound); end; if OwningMemScan.progressbar<>nil then synchronize(updategui); if haserror then begin synchronize(errorpopup); exit; end; //scan is successfull. //now clean up some mem, it's not needed anymore {$ifndef LOWMEMORYUSAGE} if OwningMemScan.previousMemoryBuffer<>nil then begin virtualfree(OwningMemScan.previousMemoryBuffer,0,MEM_RELEASE); OwningMemscan.previousMemoryBuffer:=nil; end; {$endif} end; procedure TScanController.nextScan; var AddressFile: TFilestream; datatype: string[6]; begin //open the address file and determine if it's a region scan or result scan AddressFile:=TFileStream.Create(OwningMemScan.ScanresultFolder+'ADDRESSES.TMP',fmOpenRead or fmSharedenynone); try Addressfile.ReadBuffer(datatype,sizeof(datatype)); finally addressFile.free; end; if datatype='REGION' then FirstNextScan else NextNextScan; end; procedure TScanController.firstScan; { first scan will gather the memory regions, open the files, and spawn scanners } var currentBaseAddress: PtrUint; mbi : TMemoryBasicInformation; i,j: integer; Blocksize: qword; currentblocksize: qword; totalProcessMemorySize: qword; leftfromprevious: dword; offsetincurrentregion: qword; isWritable, isExecutable, isCopyOnWrite: boolean; validRegion: boolean; datatype: string[6]; f: TFilestream; vqecacheflag: dword; begin OutputDebugString('TScanController.firstScan'); if OnlyOne then threadcount:=1 else threadcount:=GetCPUCount; //if it's a custom scan with luascript as type just use one cpu so there is less overhead {$ifdef customtypeimplemented} if (variableType=vtCustom) and (customType<>nil) and (customtype.CustomTypeType=cttLuaScript) then threadcount:=1; {$ENDIF} totalProcessMemorySize:=0; OutputDebugString(format('threadcount=%d',[threadcount])); { ScanController plan: spawn idle scanner threads , ammount=maxthreadcount in settings enumerate all regions and split up into jobs for the threads if scanoption=soUnknownValue make a buffer big enough to hold all the memory, and give the threads the startbase where in the buffer their first region will start start scanner threads } //determine the size in bytes for this variable. If one is provided. //also fill in the fastscan alignment as well //else it's ignored and never used setlength(memRegion,16); memRegionPos:=0; if OnlyOne then //don't go back, but forward begin if (startaddress mod 8)>0 then //align on a 8 byte base startaddress:=startaddress-(startaddress mod 8)+8; end else begin if (startaddress mod 8)>0 then //align on a 8 byte base startaddress:=startaddress-(startaddress mod 8); end; OutputDebugString('processhandle='+inttostr(processhandle)); OutputDebugString(format('startaddress=%x',[startaddress])); OutputDebugString(format('stopaddress=%x',[stopaddress])); OutputDebugString('Finding out memory size'); currentBaseAddress:=startaddress; ZeroMemory(@mbi,sizeof(mbi)); OutputDebugString('scanWritable='+inttostr(integer(scanWritable))); OutputDebugString('scanExecutable='+inttostr(integer(scanExecutable))); OutputDebugString('scanCopyOnWrite='+inttostr(integer(scanCopyOnWrite))); vqecacheflag:=0; if not Scan_MEM_MAPPED then vqecacheflag:=vqecacheflag or VQE_NOSHARED; if scan_pagedonly then vqecacheflag:=vqecacheflag or VQE_PAGEDONLY; if scan_dirtyonly and (scanWritable=scanInclude) then vqecacheflag:=vqecacheflag or VQE_DIRTYONLY; VirtualQueryEx_StartCache(processhandle, vqecacheflag); while (Virtualqueryex(processhandle,pointer(currentBaseAddress),mbi,sizeof(mbi))<>0) and (currentBaseAddresscurrentBaseAddress) do //last check is done to see if it wasn't a 64-bit overflow. begin // if (not (not scan_mem_private and (mbi._type=mem_private))) and (not (not scan_mem_image and (mbi._type=mem_image))) and (not (not scan_mem_mapped and (mbi._type=mem_mapped))) and (mbi.State=mem_commit) and ((mbi.Protect and page_guard)=0) and ((mbi.protect and page_noaccess)=0) then //look if it is commited begin if PtrUint(mbi.BaseAddress)=stopaddress then mbi.RegionSize:=stopaddress-PtrUint(mbi.BaseAddress); validRegion:=(mbi.State=mem_commit); validRegion:=validregion and (PtrUint(mbi.BaseAddress)0) or ((mbi.protect and PAGE_WRITECOPY)>0) or //writecopy IS writable ((mbi.protect and PAGE_EXECUTE_READWRITE)>0) or ((mbi.protect and PAGE_EXECUTE_WRITECOPY)>0); isExecutable:=((mbi.protect and PAGE_EXECUTE)>0) or ((mbi.protect and PAGE_EXECUTE_READ)>0) or ((mbi.protect and PAGE_EXECUTE_READWRITE)>0) or ((mbi.protect and PAGE_EXECUTE_WRITECOPY)>0); isCopyOnWrite:=((mbi.protect and PAGE_WRITECOPY)>0) or ((mbi.protect and PAGE_EXECUTE_WRITECOPY)>0); case scanWritable of scanInclude: validregion:=validregion and isWritable; scanExclude: validregion:=validregion and (not isWritable); end; case scanExecutable of scanInclude: validregion:=validregion and isExecutable; scanExclude: validregion:=validregion and (not isExecutable); end; case scanCopyOnWrite of scanInclude: validregion:=validregion and isCopyOnWrite; scanExclude: validregion:=validregion and (not isCopyOnWrite); end; end; if not validregion then begin //next currentBaseAddress:=PtrUint(mbi.BaseAddress)+mbi.RegionSize; continue; end; //still here, so valid try if memRegionPos>0 then begin //check if it can be appended to the previous region if memRegion[memRegionPos-1].BaseAddress+memRegion[memRegionPos].MemorySize=PtrUint(mbi.baseaddress) then //yes, append begin //yes, so append memRegion[memRegionPos-1].MemorySize:=memRegion[memRegionPos-1].MemorySize+mbi.RegionSize; continue; end; end; //still here, so a new region memRegion[memRegionPos].BaseAddress:=PtrUint(mbi.baseaddress); //just remember this location memRegion[memRegionPos].MemorySize:=mbi.RegionSize; memRegion[memRegionPos].startaddress:=pointer(ptrUint(totalProcessMemorySize)); //starts from 0, for unknown scans inc(memRegionPos); if (memRegionPos mod 16)=0 then //add another 16 to it setlength(memRegion,length(memRegion)+16); finally inc(totalProcessMemorySize,mbi.RegionSize); //add this size to the total end; end; currentBaseAddress:=PtrUint(mbi.baseaddress)+mbi.RegionSize; end; VirtualQueryEx_EndCache(processhandle); OutputDebugString(format('memRegionPos=%d',[memRegionPos])); for i:=0 to memRegionPos-1 do OutputDebugString(format('i: %d B=%x S=%x SA=%p',[i, memRegion[i].BaseAddress, memRegion[i].MemorySize, memRegion[i].startaddress])); OutputDebugString(format('totalProcessMemorySize=%x (%d)',[totalProcessMemorySize, totalProcessMemorySize])); totalAddresses:=totalProcessMemorySize; if memRegionPos=0 then raise exception.Create(rsNoReadableMemoryFound); //if soUnknown, make a buffer where it can store all the 'previous' memory if scanOption=soUnknownValue then begin OutputDebugString('scanOption=soUnknownValue'); {$ifdef lowmemoryusage} //create a file to store the previous memory in OutputDebugString(format('Creating a memory.tmp file : %dKB',[totalProcessMemorySize div 1024])); f:=Tfilestream.Create(OwningMemScan.ScanresultFolder+'MEMORY.TMP', fmCreate); f.Size:=totalProcessMemorySize; f.free; {$else} //extra check to make sure the previous scan was cleared if OwningMemScan.previousMemoryBuffer<>nil then virtualfree(OwningMemScan.previousMemoryBuffer,0,MEM_RELEASE); OutputDebugString(format('Allocating %dKB for previousMemoryBuffer',[totalProcessMemorySize div 1024])); OwningMemScan.previousMemoryBuffer:=VirtualAlloc(nil,totalProcessMemorySize, MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE); //top down to try to prevent memory fragmentation if OwningMemScan.previousMemoryBuffer=nil then raise exception.Create(Format(rsFailureAllocatingMemoryForCopyTriedAllocatingKB, [inttostr(totalProcessMemorySize div 1024)])); OutputDebugString(format('Allocated at %p',[OwningMemScan.previousMemoryBuffer])); {$endif} end; //split up into separate workloads OutputDebugString(format('Splitting up the workload between %d threads',[threadcount])); Blocksize:=totalProcessMemorySize div threadcount; if (Blocksize mod 4096) > 0 then Blocksize:=blocksize-(blocksize mod 4096); //lastblock gets the missing bytes OutputDebugString(format('Blocksize = %x',[Blocksize])); scannersCS.Enter; //block access by the mainthread on the scanners object, could scanner[14] has not yet been created when doing a progress request try setlength(scanners,threadcount); j:=0; //start at memregion 0 leftfromprevious:=0; offsetincurrentregion:=0; for i:=0 to threadcount-1 do begin OutputDebugString(format('Creating scanner %d',[i])); scanners[i]:=tscanner.Create(true, OwningMemScan.ScanresultFolder); scanners[i].scannernr:=i; scanners[i].OwningScanController:=self; scanners[i]._startregion:=j; scanners[i].startaddress:=memRegion[j].BaseAddress+offsetincurrentregion; scanners[i].maxregionsize:=0; if i=(threadcount-1) then begin //if it's the last thread, just give it what's left scanners[i].stopaddress:=stopaddress; scanners[i]._stopregion:=memregionpos-1; //define maxregionsize , go from current till end (since it'll scan everything that's left) while jsoUnknownValue then //not a unknown initial value scan, so it doesn't need overlap begin if scanners[i].maxregionsizebuffersize then scanners[i].maxregionsize:=buffersize; OutputDebugString(format('maxregionsize = %x',[scanners[i].maxregionsize])); //now configure the scanner thread with the same info this thread got, with some extra info scanners[i].compareToSavedScan:=compareToSavedScan; scanners[i].savedscanname:=savedscanname; scanners[i].scanType:=scanType; //stFirstScan obviously scanners[i].scanoption:=scanoption; scanners[i].variableType:=VariableType; scanners[i].customType:=CustomType; scanners[i].roundingtype:=roundingtype; scanners[i].scanValue1:=scanvalue1; //usual scanvalue scanners[i].scanValue2:=scanValue2; //2nd value for between scan scanners[i].unicode:=unicode; scanners[i].OnlyOne:=OnlyOne; scanners[i].caseSensitive:=caseSensitive; scanners[i].percentage:=percentage; scanners[i].hexadecimal:=hexadecimal; scanners[i].binaryStringAsDecimal:=binaryStringAsDecimal; scanners[i].fastscanalignsize:=fastscanalignsize; scanners[i].fastscanmethod:=fastscanmethod; scanners[i].fastscandigitcount:=fastscandigitcount; scanners[i].variablesize:=variablesize; scanners[i].floatscanWithoutExponents:=floatscanWithoutExponents; scanners[i].inverseScan:=inverseScan; if i=0 then //first thread gets the header part begin if scanoption=soUnknownValue then datatype:='REGION' else datatype:='NORMAL'; scanners[i].AddressFile.WriteBuffer(datatype,sizeof(datatype)); end; end; finally scannersCS.Leave; end; //all threads are created, so start them for i:=0 to length(scanners)-1 do scanners[i].start; //prepare the result files try OwningMemScan.found:=0; //and now we wait for i:=0 to threadcount-1 do begin while not (terminated or scanners[i].isdone) do begin {$ifdef windows} WaitForSingleObject(scanners[i].Handle,25); //25ms, an eternity for a cpu if OwningMemScan.progressbar<>nil then synchronize(updategui); {$else} sleep(25) {$endif} end; //If terminated then stop the scanner thread and wait for it to finish if terminated then begin scanners[i].Terminate; scanners[i].WaitFor; end; if scanners[i].haserror then begin OwningMemScan.found:=0; haserror:=true; errorstring:=scanners[i].errorstring; break; end; inc(OwningMemScan.found,scanners[i].totalfound); end; if OnlyOne then begin if (scanners[0].found) or (scanners[0].totalfound)>0 then begin FoundSomething:=true; AddressFound:=scanners[0].AddressFound; end; //even fill in the data if nothing was found setlength(AddressesFound, length(scanners[0].mabs)); for j:=0 to length(scanners[0].mabs)-1 do AddressesFound[j]:=scanners[0].mabs[j].foundaddress; end; if OwningMemScan.progressbar<>nil then synchronize(updategui); if haserror then begin OwningMemScan.found:=0; //synchronize(errorpopup); exit; end; //all threads are done //combine all results and write them too the AddressFile and MemoryFile if scanOption=soUnknownValue then begin //read the scanner memregions and adapt this memregion to it //first find out how many regions it got memRegionPos:=0; for i:=0 to threadcount-1 do inc(memRegionPos,scanners[i].memRegionPos); setlength(OwningMemScan.memRegion,memRegionPos); //setting the size accordingly (max, can end up smaller due to appending) OwningMemScan.memRegionPos:=0; for i:=0 to threadcount-1 do begin for j:=0 to scanners[i].memRegionPos-1 do begin inc(OwningMemScan.found,scanners[i].memregions[j].MemorySize); if OwningMemScan.memregionpos>0 then begin if OwningMemScan.memregion[OwningMemScan.memregionpos-1].BaseAddress+OwningMemScan.memregion[OwningMemScan.memregionpos-1].MemorySize=scanners[i].memregions[j].BaseAddress then begin //append inc(OwningMemScan.memregion[OwningMemScan.memregionpos-1].MemorySize,scanners[i].memregions[j].MemorySize); continue; end; end; //new one OwningMemScan.memregion[OwningMemScan.memregionpos]:=scanners[i].memregions[j]; inc(OwningMemScan.memregionpos); end; end; if fastscan then //divide by alignsize of fastscan begin if fastscanmethod=fsmAligned then OwningMemScan.found:=OwningMemScan.found div fastscanalignsize else begin OwningMemScan.found:=OwningMemScan.found div trunc(power(16, fastscandigitcount)); end; end; savescannerresults:=true; end else begin savescannerresults:=true; end; finally OutputDebugString('Scan ended'); end; end; procedure TScanController.execute; var err: dword; i: integer; oldpos,oldmempos: qword; wantsize: qword; haserror2: boolean; datatype: string[6]; begin OutputDebugString('TScanController.execute'); try //check what it is, and call first/next/nextnext- scan err:=0; errorstring:=''; try fillVariableAndFastScanAlignSize; if scantype=stFirstScan then firstscan; if scantype=stNextScan then nextscan; OutputDebugString('No exception on controller'); except on e: exception do begin OutputDebugString(pchar('controller exception happened during the scan:'+e.message)); haserror:=true; errorstring:='controller:'+e.message; end; end; if OnlyOne then savescannerresults:=false; //DO NOT INTERFERE {$ifdef LOWMEMORYUSAGE} if scanOption=soUnknownValue then begin if scanners[0].Addressfile<>nil then freeandnil(scanners[0].Addressfile); AddressFile:=TFileStream.Create(scanners[0].Addressfilename,fmCreate or fmShareDenyNone); datatype:='REGION'; AddressFile.WriteBuffer(datatype,sizeof(datatype)); for i:=0 to OwningMemScan.memregionpos do AddressFile.WriteBuffer(OwningMemScan.memregion[i], sizeof(OwningMemScan.memRegion[i])); freeandnil(addressfile); //make the state compatible with the original code (double rename, but whatever) for i:=0 to length(scanners)-1 do if scanners[i].MemoryFile<>nil then freeandnil(scanners[i].MemoryFile); deletefile(scanners[0].Memoryfilename); renamefile(OwningMemScan.ScanresultFolder+'MEMORY.TMP', scanners[0].Memoryfilename); end; {$endif} if savescannerresults then //prepare saving. Set the filesize begin try OutputDebugString('ScanController: creating undo files'); if scanners[0].Addressfile<>nil then freeandnil(scanners[0].Addressfile); if scanners[0].Memoryfile<>nil then freeandnil(scanners[0].Memoryfile); deletefile(OwningMemScan.ScanresultFolder+'ADDRESSES.UNDO'); renamefile(OwningMemScan.ScanresultFolder+'ADDRESSES.TMP',OwningMemScan.ScanresultFolder+'ADDRESSES.UNDO'); renamefile(scanners[0].Addressfilename, OwningMemScan.ScanresultFolder+'ADDRESSES.TMP'); //memory deletefile(OwningMemScan.ScanresultFolder+'MEMORY.UNDO'); renamefile(OwningMemScan.ScanresultFolder+'MEMORY.TMP',OwningMemScan.ScanresultFolder+'MEMORY.UNDO'); renamefile(scanners[0].Memoryfilename, OwningMemScan.ScanresultFolder+'MEMORY.TMP'); try AddressFile:=TFileStream.Create(OwningMemScan.ScanresultFolder+'ADDRESSES.TMP',fmOpenWrite or fmShareDenyNone); MemoryFile:=TFileStream.Create(OwningMemScan.ScanresultFolder+'MEMORY.TMP',fmOpenWrite or fmsharedenynone); except raise exception.create(rsErrorWhenWhileLoadingResult); end; oldpos:=addressfile.Size; oldmempos:=memoryfile.size; wantsize:=AddressFile.size; for i:=1 to threadcount-1 do wantsize:=wantsize+scanners[i].Addressfile.Size; addressfile.size:=wantsize; if addressfile.size<>wantsize then raise exception.create(rsNotEnoughDiskspaceForTheAddressFile); wantsize:=memoryfile.size; for i:=1 to threadcount-1 do wantsize:=wantsize+scanners[i].MemoryFile.size; memoryfile.size:=wantsize; if memoryfile.size<>wantsize then raise exception.create(rsNotEnoughDiskspaceForTheMemoryFile); outputdebugstring(format('ScanController: Have set AddressFile.size to %d',[AddressFile.size])); outputdebugstring(format('ScanController: Have set MemoryFile.size to %d',[memoryFile.size])); except on e: exception do begin OutputDebugString(pchar(Format(rsDiskWriteError2, [e.message]))); haserror:=true; errorstring:='controller:Cleanup:ResultsPrepare:'+e.message; end; end; end; //send message saying it's done if haserror then err:=1; isdone:=true; {$ifdef windows} if notifywindow<>0 then postMessage(notifywindow,notifymessage,err,0); {$else} //todo: notify the caller the scan is done OutputDebugString('It actually finished'); {$endif} isdoneevent.setevent; haserror2:=false; //todo: instead of appending the results, link to the result files instead if scanOption<>soUnknownValue then begin try if savescannerresults and (addressfile<>nil) then //now actually save the scanner results begin //AddressFile should already have been created with the correct datatype and opened as denynone AddressFile.Seek(oldpos,soFromBeginning); Memoryfile.seek(oldmempos,soFromBeginning); //save the exact results, and copy it to the AddressesFirst.tmp and Memoryfirst.tmp files for i:=1 to length(scanners)-1 do begin outputdebugstring(format('ScanController: Writing results from scanner %d',[i])); if (scanners[i].Addressfile<>nil) and (scanners[i].MemoryFile<>nil) then begin addressfile.CopyFrom(scanners[i].Addressfile,0); Memoryfile.CopyFrom(scanners[i].MemoryFile,0); end; end; end; except on e: exception do begin OutputDebugString(pchar('Disk Write Error:'+e.message)); haserror2:=true; errorstring:='controller:Cleanup:ResultsWrite:'+e.message; end; end; end; isreallydoneevent.setEvent; //clean up secondary scanner threads, their destructor will close and delete their files outputdebugstring('ScanController: Destroying scanner threads'); scannersCS.enter; try outputdebugstring('ScanController: Critical section "scannersCS" aquired'); for i:=0 to length(scanners)-1 do begin outputdebugstring(format('ScanController: Freeing scanner %d',[i])); freeandnil(scanners[i]); end; setlength(scanners,0); finally scannersCS.leave; outputdebugstring('ScanController: Critical section "scannersCS" released'); end; //cleanup the files if addressfile<>nil then addressfile.Free; if MemoryFile<>nil then Memoryfile.Free; //save the first scan results if needed try if scantype=stFirstScan then begin if not OnlyOne then begin outputdebugstring('ScanController: This was a first scan, so saving the First Scan results'); outputdebugstring('to:'+OwningMemScan.ScanresultFolder+'ADDRESSES.First'); {$IFDEF LOWMEMORYUSAGE} copyfile(OwningMemScan.ScanresultFolder+'ADDRESSES.TMP', OwningMemScan.ScanresultFolder+'ADDRESSES.First'); copyfile(OwningMemScan.ScanresultFolder+'MEMORY.TMP', OwningMemScan.ScanresultFolder+'MEMORY.First') {$else} OwningMemScan.SaveFirstScanThread:=TSaveFirstScanThread.create(OwningMemScan.ScanresultFolder, false,@OwningMemScan.memregion,@OwningMemScan.memregionpos, OwningMemScan.previousMemoryBuffer); {$ENDIF} end else OutputDebugString('This was an single result scan only. No need to save the first scan state'); end; except on e: exception do begin OutputDebugString(pchar('First Scan Create:'+e.message)); haserror2:=true; errorstring:='controller:Cleanup:'+rsFailedSpawningTheSaveFirstScanThread+':'+e.message; end; end; except on e: exception do begin OutputDebugString(pchar('controller exception happened:Unknown!'+e.message)); haserror2:=true; errorstring:='controller:Unknown!'+e.message; end; end; {$IFNDEF UNIX} if haserror2 then MessageBox(0, pchar(errorstring),'Scancontroller cleanup error', MB_ICONERROR or mb_ok); {$ENDIF} outputdebugstring('end of scancontroller reached'); isreallydoneevent.setEvent; //just set it again if it wasn't set {$IFNDEF UNIX} if assigned(OwningMemScan.OnScanDone) then {$endif} begin outputdebugstring('Queue OwningMemScan.ScanDone'); Queue(OwningMemScan.ScanDone); end; end; constructor TScanController.create(suspended: boolean); begin {$IFDEF WINDOWS} SetProgressstate(tbpsNormal); {$ENDIF} isdoneevent:=TEvent.create(nil,true,false,''); isreallydoneevent:=TEvent.create(nil,true,false,''); scannersCS:=TCriticalSection.Create; resultsaveCS:=TCriticalsection.create; inherited create(suspended); end; destructor TScancontroller.destroy; var i: integer; begin terminate; WaitFor; scannersCS.Enter; try for i:=0 to length(scanners)-1 do scanners[i].Free; setlength(scanners,0); finally scannersCS.leave; end; scannersCS.free; resultsaveCS.free; inherited destroy; end; //----------------memscan--------------// procedure TMemscan.TerminateScan(forceTermination: boolean); var i: integer; lastwait: TWaitResult; begin if scancontroller<>nil then begin if not forceTermination then begin scanController.Terminate end else begin //scancontroller.forceTerminate:=true; for i:=0 to length(scancontroller.scanners)-1 do begin scanController.scanners[i].Terminate; scanController.scanners[i].isdone:=true; end; scancontroller.Terminate; for i:=0 to 100 do begin lastwait:=scancontroller.isdoneEvent.WaitFor(50); if lastwait<>wrTimeout then break else begin if GetCurrentThreadID=MainThreadID then CheckSynchronize; end; end; if lastwait=wrTimeout then begin {$IFNDEF UNIX} TerminateThread(scancontroller.Handle, $dead); messagedlg(rsMSTheScanWasForcedToTerminateSubsequentScansMayNotFunctionProperlyEtc, mtWarning, [mbok], 0); {$else} KillThread(scancontroller.handle); {$ENDIF} scanController.isDoneEvent.SetEvent; {$IFNDEF UNIX} if notifywindow<>0 then PostMessage(notifywindow, notifymessage,0,0); {$ENDIF} end; end; end else raise exception.Create(rsForSomeReasonTheScanControllerDoesNotExist); //and now the caller has to wait end; function TMemscan.GetErrorString: string; begin result:=''; if scancontroller<>nil then begin scancontroller.WaitFor; result:=scancontroller.errorstring; end; end; function TMemscan.canUndo: boolean; var u: TFileStream; begin result:=false; try u:=tfilestream.create(fScanResultFolder+'MEMORY.UNDO', fmopenread or fmShareDenyNone); try result:=u.Size>0; finally u.free; end; except end; // LastScanType; end; function TMemscan.getsavedresults(r: tstrings): integer; begin r.clear; if savedresults=nil then begin r.add('First'); result:=1; end else r.AddStrings(savedresults); result:=r.count; end; procedure TMemscan.saveresults(resultname: string); var fname: string; begin //check if it already exists or if it's a 'special' name fname:=uppercase(resultname); if (fname='TMP') or (fname='UNDO') then raise exception.create(rsTMPAndUNDOAreNamesThatMayNotBeUsedTryAnotherName); if savedresults=nil then begin savedresults:=tstringlist.create; savedresults.CaseSensitive:=false; savedresults.Duplicates:=dupError; savedresults.Add('First'); end; if savedresults.IndexOf(resultname)<>-1 then raise exception.create(Format(rsAResultSetWithThisNameAlreadyExists, [resultname])); //everything looks ok waittilldone; //copy the current scanresults to memory.savedscan and addresses.savedscan CopyFile(pchar(fScanResultFolder+'MEMORY.TMP'), pchar(fScanResultFolder+'MEMORY.'+resultname), false); CopyFile(pchar(fScanResultFolder+'ADDRESSES.TMP'), pchar(fScanResultFolder+'ADDRESSES.'+resultname), false); savedresults.Add(resultname); end; procedure TMemscan.undoLastScan; begin {$IFNDEF UNIX} if attachedFoundlist<>nil then TFoundList(Attachedfoundlist).Deinitialize; {$ENDIF} if canUndo then begin deletefile(fScanResultFolder+'MEMORY.TMP'); deletefile(fScanResultFolder+'ADDRESSES.TMP'); renamefile(fScanResultFolder+'MEMORY.UNDO',fScanResultFolder+'MEMORY.TMP'); renamefile(fScanResultFolder+'ADDRESSES.UNDO',fScanResultFolder+'ADDRESSES.TMP'); end; end; function TMemscan.waittilldone(timeout: DWORD=INFINITE): boolean; begin result:=true; if scancontroller<>nil then result:=scancontroller.isdoneEvent.WaitFor(timeout)<>wrTimeout; end; function TMemscan.waittillreallydone(timeout: DWORD=INFINITE): boolean; begin result:=true; if scancontroller<>nil then result:=scancontroller.isreallydoneEvent.WaitFor(timeout)<>wrTimeout; end; procedure TMemscan.ScanDone; //called by the scancontroller when the scan has finished begin if assigned(fOnScanDone) then fOnScanDone(self); end; function TMemscan.GetOnlyOneResults(var addresses: Taddresses):boolean; begin if self.scanController<>nil then begin result:=scancontroller.FoundSomething; addresses:=scancontroller.AddressesFound; end else result:=false; end; function TMemscan.GetOnlyOneResult(var address: ptruint):boolean; begin if self.scanController<>nil then begin result:=scancontroller.FoundSomething; address:=scancontroller.AddressFound; end else result:=false; end; function TMemscan.GetScanFolder: string; begin result:=ScanresultFolder; end; function TMemscan.GetProgress(var totaladdressestoscan:qword; var currentlyscanned: qword; var resultsfound: qword):integer; {returns a value between 1 and 1000 representing how far the scan is} var i: integer; begin result:=0; totaladdressestoscan:=0; currentlyscanned:=0; resultsfound:=0; //Take care of memory if self.scanController<>nil then begin totaladdressestoscan:=self.scanController.totalAddresses; currentlyscanned:=0; scanController.scannersCS.enter; try for i:=0 to length(self.scanController.scanners)-1 do begin inc(currentlyscanned,self.scanController.scanners[i].scanned); inc(resultsfound, self.scanController.scanners[i].totalfound+self.scanController.scanners[i].found); end; finally scanController.scannersCS.Leave; end; if totaladdressestoscan>0 then result:=trunc((currentlyscanned / totaladdressestoscan) * 1000); end; end; function TMemscan.GetFoundCount: uint64; begin result:=found; end; function TMemscan.Getbinarysize: int64; var i: integer; begin case self.currentVariableType of vtByte: result:=8; vtWord: result:=16; vtDWord: result:=32; vtQWord: result:=64; vtSingle: result:=32; vtDouble: result:=64; vtAll: begin result:=8; //bytes {$ifdef customtypeimplemented} if vtcustom in ScanAllTypes then for i:=0 to customTypes.count-1 do result:=max(result, TCustomType(customtypes[i]).bytesize); {$ENDIF} result:=result*8; //get the binary size end; vtString: if stringUnicode then result:=16*stringLength else result:=8*stringLength; vtBinary: result:=binaryLength; vtByteArray: result:=arrayLength*8; {$ifdef customtypeimplemented} vtCustom: result:=currentCustomType.bytesize*8; {$ENDIF} else result:=8; end; end; procedure TMemscan.newscan; begin {$IFNDEF UNIX} if attachedFoundlist<>nil then TFoundList(Attachedfoundlist).Deinitialize; {$ENDIF} if scanController<>nil then begin scanController.terminate; scanController.WaitFor; FreeAndNil(scanController); end; {$IFNDEF LOWMEMORYUSAGE} if SaveFirstScanThread<>nil then begin SaveFirstScanThread.Terminate; SaveFirstScanThread.WaitFor; //wait till it's done freeandnil(SaveFirstScanThread); end; if previousMemoryBuffer<>nil then virtualfree(previousMemoryBuffer,0,MEM_RELEASE); {$endif} fLastscantype:=stNewScan; fLastScanValue:=''; deletescanfolder; createscanfolder; fnextscanCount:=0; end; procedure TMemscan.NextScan(scanOption: TScanOption; roundingtype: TRoundingType; scanvalue1, scanvalue2: string; hexadecimal,binaryStringAsDecimal, unicode, casesensitive,percentage,compareToSavedScan: boolean; savedscanname: string); begin fisHexadecimal:=hexadecimal; {$IFNDEF UNIX} if attachedFoundlist<>nil then TFoundList(Attachedfoundlist).Deinitialize; {$ENDIF} inc(fnextscanCount); if scanController<>nil then begin {$ifdef windows} if GUIScanner and (WaitForSingleObject(scancontroller.handle, 500)<>WAIT_OBJECT_0) then begin if frmBusy=nil then begin frmBusy:=TfrmBusy.create(nil); frmBusy.WaitForHandle:=scancontroller.handle; frmBusy.Showmodal; end; end; {$endif} scancontroller.WaitFor; //could be it's still saving the results of the previous scan freeandnil(scanController); end; {$IFNDEF LOWMEMORYUSAGE} if SaveFirstScanThread<>nil then begin if GUIScanner and (WaitForSingleObject(SaveFirstScanThread.handle, 500)<>WAIT_OBJECT_0) then begin if frmBusy=nil then begin frmBusy:=TfrmBusy.create(nil); frmBusy.WaitForHandle:=SaveFirstScanThread.handle; frmBusy.Showmodal; end; end; SaveFirstScanThread.WaitFor; //wait till it's done freeandnil(SaveFirstScanThread); end; {$ENDIF} scanController:=TscanController.Create(true); scanController.OwningMemScan:=self; scanController.scantype:=stNextScan; scanController.scanOption:=scanOption; scanController.compareToSavedScan:=compareToSavedScan; scanController.savedscanname:=savedscanname; scanController.variableType:=CurrentVariableType; scancontroller.customType:=customtype; scanController.roundingtype:=roundingtype; scanController.fastscanalignment:=fastscanalignment; scanController.fastscanmethod:=fastscanmethod; scancontroller.fastscandigitcount:=fastscandigitcount; if codepage then begin scanvalue1:=UTF8ToWinCP(scanvalue1); scanValue2:=UTF8ToWinCP(scanvalue1); end; scanController.scanValue1:=scanvalue1; //usual scanvalue scanController.scanValue2:=scanValue2; //2nd value for between scan scanController.startaddress:=self.startaddress; scanController.stopaddress:=self.stopaddress; scancontroller.hexadecimal:=hexadecimal; scancontroller.binaryStringAsDecimal:=binaryStringAsDecimal; scancontroller.unicode:=unicode; scancontroller.casesensitive:=casesensitive; scancontroller.floatscanWithoutExponents:=floatscanWithoutExponents; scancontroller.inverseScan:=inverseScan; scancontroller.percentage:=percentage; scancontroller.notifywindow:=notifywindow; scancontroller.notifymessage:=notifymessage; fLastscantype:=stNextScan; fLastScanValue:=scanvalue1; scanController.start; end; procedure TMemscan.firstscan(scanOption: TScanOption; VariableType: TVariableType; roundingtype: TRoundingType; scanvalue1, scanvalue2: string; startaddress,stopaddress: ptruint; hexadecimal,binaryStringAsDecimal,unicode,casesensitive: boolean; fastscanmethod: TFastScanMethod=fsmNotAligned; fastscanparameter: string=''; customtype: TCustomType=nil); { Spawn the controller thread and fill it with the required data Popup the wait window, or not ? } begin fisHexadecimal:=hexadecimal; if (variableType=vtCustom) and (customtype=nil) then raise exception.create('customType=nil'); {$IFNDEF UNIX} if attachedFoundlist<>nil then TFoundList(Attachedfoundlist).Deinitialize; {$ENDIF} if scanController<>nil then freeandnil(scanController); {$IFNDEF LOWMEMORYUSAGE} if SaveFirstScanThread<>nil then begin SaveFirstScanThread.Terminate; //it should quit, saving took to long and the user already started a new one SaveFirstScanThread.WaitFor; //wait till it has fully terminated freeandnil(SaveFirstScanThread); end; {$ENDIF} currentVariableType:=VariableType; currentCustomType:=customtype; if fastscanparameter<>'' then self.fastscanalignment:=strtoint('$'+fastscanparameter) else self.fastscanalignment:=1; self.fastscanmethod:=fastscanmethod; self.fastscandigitcount:=length(fastscanparameter); self.startaddress:=startaddress; self.stopaddress:=stopaddress; //OutputDebugString('Vartype='+inttostr(integer(VariableType))); scanController:=TscanController.Create(true); scanController.OwningMemScan:=self; scanController.scantype:=stFirstScan; scanController.scanOption:=scanOption; scanController.variableType:=VariableType; scancontroller.customType:=customtype; scancontroller.scanWritable:=scanWritable; scancontroller.scanExecutable:=scanExecutable; scancontroller.scanCopyOnWrite:=scanCopyOnWrite; scanController.roundingtype:=roundingtype; scanController.fastscanalignment:=fastscanalignment; scanController.fastscanmethod:=fastscanmethod; scancontroller.fastscandigitcount:=fastscandigitcount; if codepage then begin scanvalue1:=UTF8ToWinCP(scanvalue1); scanValue2:=UTF8ToWinCP(scanvalue1); end; scanController.scanValue1:=scanvalue1; //usual scanvalue scanController.scanValue2:=scanValue2; //2nd value for between scan scanController.startaddress:=startaddress; scanController.stopaddress:=stopaddress; scancontroller.hexadecimal:=hexadecimal; scancontroller.binaryStringAsDecimal:=binaryStringAsDecimal; scancontroller.unicode:=unicode; scancontroller.casesensitive:=casesensitive; scancontroller.floatscanWithoutExponents:=floatscanWithoutExponents; scancontroller.inverseScan:=InverseScan; scancontroller.percentage:=false; //first scan does not have a percentage scan scancontroller.notifywindow:=notifywindow; scancontroller.notifymessage:=notifymessage; scanController.OnlyOne:=onlyone; fLastscantype:=stFirstScan; fLastScanValue:=scanValue1; scanController.start; end; procedure TMemscan.setScanDoneCallback(notifywindow: thandle; notifymessage: integer); begin self.notifywindow:=notifywindow; self.notifymessage:=notifymessage; end; procedure TMemScan.parseProtectionflags(protectionflags: string); var i: integer; currentstate: Tscanregionpreference; begin //parse the protectionflags string and set scanWritable, scanExecutable and scanCopyOnWrite; scanWritable:=scanDontCare; scanCopyOnWrite:=scanDontCare; scanExecutable:=scanDontCare; protectionflags:=uppercase(protectionflags); currentstate:=scanDontCare; for i:=1 to length(protectionflags) do begin case protectionflags[i] of '-': currentState:=scanExclude; '+': currentState:=scanInclude; '*': currentstate:=scanDontCare; 'W': begin scanWritable:=currentState; currentState:=scanDontCare; end; 'C': begin scanCopyOnWrite:=currentState; currentState:=scanDontCare; end; 'X': begin scanExecutable:=currentState; currentState:=scanDontCare; end; end; end; end; constructor TMemScan.create(progressbar: TCustomProgressbar); begin self.progressbar:=progressbar; //setup the location of the scan results CreateScanfolder; end; procedure TMemscan.CreateScanfolder; var guid: TGUID; usedtempdir: string; utf8: boolean; begin OutputDebugString('CreateScanfolder'); CreateGUID(guid); if (length(trim(tempdiralternative))>2) and dontusetempdir then usedtempdir:=trim(tempdiralternative) else usedtempdir:=GetTempDir; usedtempdir:=IncludeTrailingPathDelimiter(usedtempdir); fScanResultFolder:=usedtempdir+'Cheat Engine'+pathdelim; OutputDebugString('fScanResultFolder='+fScanResultFolder); if DirectoryExistsUTF8(usedtempdir) then utf8:=true else if DirectoryExists(usedtempdir) then utf8:=false else raise exception.create(Format(rsTheTemporaryScanDirectoryDoesNotExistCheckYourScan, [usedtempdir])); if (utf8 and (not DirectoryExistsUTF8(fScanResultFolder))) or ((not utf8) and (not DirectoryExists(fScanResultFolder))) then begin if (utf8 and (not CreateDirUTF8(fScanResultFolder))) or ((not utf8) and (not CreateDir(fScanResultFolder))) then begin //failure in creating the dir {$IFNDEF UNIX} MakePathAccessible(fScanResultFolder); {$ENDIF} if (utf8 and (not CreateDirUTF8(fScanResultFolder))) or ((not utf8) and (not CreateDirUTF8(fScanResultFolder))) then raise exception.create(rsFailureCreatingTheScanDirectory); end; end; {$IFNDEF UNIX} MakePathAccessible(fScanResultFolder); {$ENDIF} fScanResultFolder:=fScanResultFolder+GUIDToString(guid)+pathdelim; CreateDir(fScanResultFolder); end; procedure TMemscan.DeleteScanfolder; var usedtempdir: string; Info : TSearchRec; f: string; age: longint; currenttime: longint; begin if fScanResultFolder<>'' then begin try if DeleteFolder(fScanResultFolder) then outputdebugstring('deleted the scanresults') else outputdebugstring('Failure deleting the scanresults'); //check if there are folders that are older than 2 days if (length(tempdiralternative)>2) and dontusetempdir then begin usedtempdir:=tempdiralternative; tempdiralternative:=trim(tempdiralternative); if tempdiralternative[length(tempdiralternative)]<>pathdelim then tempdiralternative:=tempdiralternative+pathdelim; end else usedtempdir:=GetTempDir; if FindFirst(usedtempdir+'Cheat Engine\{*}', faDirectory , info)=0 then begin repeat if (info.Attr and faDirectory) = faDirectory then begin if length(info.Name)>5 then begin //if found, delete them if older than 2 days f:=usedtempdir+'Cheat Engine\'+info.name; age:=info.time; //FileAge('"'+f+'"'); if age>0 then begin currenttime:=DateTimeToFileDate(now); if (currenttime-age) > 60*60*24*2 then //if older than 2 days then deletefolder(f); end; end; end; until FindNext(info)<>0; FindClose(info); end; except outputdebugstring('Fatal error while trying to delete the scanresults'); end; end; end; function TMemScan.DeleteFolder(dir: string) : boolean; var DirInfo: TSearchRec; r : Integer; begin ZeroMemory(@DirInfo,sizeof(TSearchRec)); result := true; while dir[length(dir)]=pathdelim do //cut of \ dir:=copy(dir,1,length(dir)-1); outputdebugstring('Deleting '+dir); r := FindFirst(dir + pathdelim+'*.*', FaAnyfile, DirInfo); while (r = 0) and result do begin if (DirInfo.Attr and FaVolumeId <> FaVolumeID) then begin if ((DirInfo.Attr and FaDirectory) <> FaDirectory) then result := DeleteFile(dir + pathdelim + DirInfo.Name); end; r := FindNext(DirInfo); end; FindClose(DirInfo); if Result then result := RemoveDir(dir); end; destructor TMemScan.destroy; begin {$IFNDEF LOWMEMORYUSAGE} if SaveFirstScanThread<>nil then SaveFirstScanThread.Free; if previousMemoryBuffer<>nil then virtualfree(previousMemoryBuffer,0,MEM_RELEASE); {$endif} if scanController<>nil then freeandnil(scancontroller); DeleteScanfolder; inherited Destroy; end; end.