Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Machine code binary buffer searching regardless of NULL


  • Please log in to reply
52 replies to this topic
Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

\Q...\E does not help

That is only true if your program is so poorly designed without fault tolerance and control. This is often a cause of bugs and security holes which AutoHotkey's uniquely simplistic syntax aims to prevent in the first place. It's ironic how you find the need to replace a single \E so mundane and 'ugly' knowing the overheads of a machine code function.

It is not about replacing a single \E, but possibly many of them. If you have a function, which accepts a binary search string S, in where you have to replace each "\E" with something like "\E\E\Q", you face serious difficulties coding in AHK, because S might contain \0's, RegEx control sequences, AHK escape sequences, %...% look-like references, etc. The resulting function should work with any combination of these special cases, which could make it long, complicated and "ugly". It has nothing to do with "if your program is so poorly designed without fault tolerance and control": but working correctly with all possible input. A "\E" might not cause errors, but faulty results.

I never expected that you would be so adamant to suppress alternatives techniques. Raw buffer searching and regex have their trade-offs and either are suited for different applications.

I don't want to suppress alternatives, but they have to work, or at least their limitations should be documented. This is what I try to do. So far we discovered

1. Workarounds for passing binary HayStack to RegExMatch with the proper length information

2. Similar workarounds for setting the length of a binary Needle surrounded by \Q...\E

3. One restriction for the search string (no "\E" inside)

It is not clear that there are no other restrictions, so we have to keep on looking.

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012

their limitations should be documented

Everything is documented on the pcre site. It's also open source, no confusing and ambiguous bytecode to run.

The only real limitation of regex is having to escape \E when using raw binary patterns. It's a bug in AutoHotkey that prevents the correct length being passed to the functions; if you post a bug report I'm sure Chris will take it into consideration and make amends. Despite this you can do tremendously more than primitive searching and replacing, with remarkable efficiency. In my opinion the benefit to cost ratio turns out in overwhelming favour of regex.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

Everything is documented on the pcre site. It's also open source, no confusing and ambiguous bytecode to run.

The only real limitation of regex is having to escape \E when using raw binary patterns.

This is an important statement. It would be too much for an average AHK user to read the full PCRE documentation and the source code to find it out.

It's a bug in AutoHotkey that prevents the correct length being passed to the functions

It is not a bug, but the basic design of AHK, based on \0 terminated strings. Function parameters passing, string expressions involve the string copy function of Windows, which terminates at the first \0. Handling binary data (also Unicode) has been on the wish list for a long time, but there seems to be no more AHK development (Chris has no posts since Nov 22), so we have to live with what is already available.

Knowing the AHK design, it is obvious that complicated workarounds are necessary to work with binary data.

Despite this you can do tremendously more than primitive searching and replacing, with remarkable efficiency.

No argument about that. However, to get a failsafe binary RegEx we need wOxxOm's original binary search-and-replace function to first remove the \E limitation, than some tricks to set the right StrLen. These are beyond the skills of regular AHK users, so someone ought to write a wrapper.

Is there a strong need for binary RegEx? I often needed simple binary replace functionality (used a hex editor), and complex RegExReplace on text strings, but never on binary data. Do you know a good application where the full RegEx power is needed on binary data?

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012

It would be too much for an average AHK user to read the full PCRE documentation and the source code to find it out.

There are numerous resources on the internet not to mention AutoHotkey's quick reference, which give an overview on control chars and escape sequences in regex. One could argue that using functions and checking processor architecture for compatibility is also too much to ask from the average user.

It is not a bug, but the basic design of AHK, based on \0 terminated strings. Function parameters passing, string expressions involve the string copy function of Windows, which terminates at the first \0.

Why does it work with RtlMoveMemory? VarSetCapacity with -1 should update the internal length yet it makes no difference.

we need wOxxOm's original binary search-and-replace function to first remove the \E limitation

You could also use WinAPI functions to convert the binary data to a hex stream as shown in RegionWaitChange. After regex compiles and caches the expression it will under most circumstances work faster than this machine code.

Do you know a good application where the full RegEx power is needed on binary data?

Virtually anywhere you need to search and replace with complex clauses, such as inspecting binary markups (already used in AHK for bencoded/torrent files), parsing magic identifiers, etc.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
AHK has another strange, but very useful peculiarity: it copies over the full result of RegExReplace to an AHK variable, regardless of any nulls inside. Because of this undocumented behavior RegEx search for a binary needle (~search string) in a binary haystack (~buffer) is not that complicated, after all.

We have to make the needle a quoted RegEx literal: enclose it between \Q and \E, and replace all internal "\E" (0x5c, 0x45) with "\E\E\Q". We can use RegExReplace also in this preparation phase. Care must be taken never to allow AHK to apply its string manipulations (function parameters, concatenation...) on these binary data, because they will truncate them at the first null.
;;;;; Initialization of 2 machine code functions, for setting input and showing results
MCode(Hex2Bin,"568b74240c8a164684d2743b578b7c240c538ac2c0e806b109f6e98ac802cac0e10"
. "4880f8a164684d2741a8ac2c0e806b309f6eb80e20f02c20ac188078a16474684d275cd5b5f5ec3")
MCode(Bin2Hex,"8B54240C85D2568B7424087E3A53578B7C24148A07478AC8C0E90480F9090F97C3F"
. "6DB80E30702D980C330240F881E463C090F97C1F6D980E10702C880C130880E464A75CE5F5BC606005EC3")

;;;;; Example 1: make binary buffer a quoted RegEx literal
hex2bin(A,"005C455C45000000005C4500000000") ; 3 "\E" and many \0's
QtBuf(Q,QLen, A,15)                         ; Make RegEx quote
MsgBox % "New length = " . QLen . "`nQuoted RegEx literal in hex: " . Bin2Hex(&Q,QLen)

;;;;; Example 2: find binary data in a binary buffer (read from ahk.exe)
BinRead(A_AhkPath, F)                       ; Binary haystack: ahk.exe
hex2bin(H,"0E1FBA0E00B409CD21B8014CCD21546869732070") ; binary search string
QtBuf(Q,QLen, H,20)                         ; Make RegEx quote
MsgBox % "Binary search string of length " . QLen . "`nIn HEX form: " . Bin2Hex(&Q,QLen)
MsgBox % "Binary needle found at offset " RegExMatch(F, Q) - 1


QtBuf(ByRef Q, ByRef QLen, ByRef B, BLen) { ; replace each \E with \E\E\Q, enclose in \E..\Q
   RegExReplace(B, "\\E","",n)
   QLen := BLen+4*n+4
   VarSetCapacity(Q,QLen,1)
   If (n > 0) {
      VarSetCapacity(C,QLen-4,1)
      C := RegExReplace(B, "\\E", "\E\E\Q") ; AHK copies result over \0's
      Q := RegExReplace(C, ".*", "\Q$0\E")  ; enclose result in \E..\Q
   } Else
      Q := RegExReplace(B, ".*", "\Q$0\E")
}

BinRead(file, ByRef data, n=0, offset=0)  {  ; n = #bytes/all; neg offset from end
   h := DllCall("CreateFile",Str,file, UInt,0x80000000, UInt,3, UInt,0, UInt,3, UInt,0, UInt,0)
   m := 2*(offset < 0)                       ; offset >= 0 : m = 0; offset < 0: m = 2
   DllCall("SetFilePointerEx",UInt,h, Int64,offset, UIntP,U, Int,m)
   m := DllCall("GetFileSize",UInt,h, Int64P,r)
   If n not between 1 and %m%
      n = %m%
   VarSetCapacity(data, n, 1)
   DllCall("ReadFile",UInt,h, UInt,&data, UInt,n, UIntP,r, UInt,0)
   DllCall("CloseHandle", UInt,h)
   Return r
}

Bin2Hex(addr,len) {       ; convert binary data at addr to hex stream
   Local hex
   VarSetCapacity(hex,2*len+1)
   dllcall(&Bin2Hex, "uint",&hex, "uint",addr, "uint",len, "cdecl")
   VarSetCapacity(hex,-1) ; update StrLen
   Return hex
}

Hex2Bin(ByRef buf, hex) { ; convert hex stream to binary data
   Global Hex2Bin
   VarSetCapacity(buf,(StrLen(hex)+1)//2,1)
   dllcall(&Hex2Bin, "uint",&buf, "uint",&hex, "cdecl")
}

MCode(ByRef code, hex) {  ; allocate memory and write Machine Code there
   VarSetCapacity(code,StrLen(hex)//2)
   Loop % StrLen(hex)//2
      NumPut("0x" . SubStr(hex,2*A_Index-1,2), code, A_Index-1, "Char")
}


polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
You use a lot of redundant code and much of it can be simplified to:

; binary needle (as hex stream) which contains "\E":
exp_txt = 10AC1FDF0D3680BC9A0254ACE702A754A65C45BF85112214B49AE50C17C8A308
StringToBinary(exp, exp_txt, false) ; use WinAPI to convert to real binary

; hack for reading binary file into a variable which internally stores its true length:
FileRead, raw_src, *c %A_AhkPath%
VarSetCapacity(src, sz := VarSetCapacity(raw_src), 0xff)
DllCall("RtlMoveMemory", "UInt", &src, "UInt", &raw_src, "UInt", sz)

; escape all instances of \E and prepend \Q:
pos := RegExMatch(src, "\Q" . RegExReplace(exp, "\\E", "\E\\E\Q"))

MsgBox, Found at: %pos%

; helper function for the purpose of this demo:
StringToBinary(ByRef bin, ByRef str, base64 = true, sz = -1) {
	cp := VarSetCapacity(bin, StrLen(str) / (base64 ? 1.42 : 2), fb)
	DllCall("Crypt32.dll\CryptStringToBinaryA", "UInt", &str
		, "UInt", sz > 0 ? sz : StrLen(str), "UInt", base64 ? 7 : 8
		, "UInt", &bin, "UInt", &cp, "UInt", 0, "UInt", 0), VarSetCapacity(bin, -1)
}

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


wOxxOm
  • Members
  • 371 posts
  • Last active: Feb 20 2015 12:10 PM
  • Joined: 09 Feb 2006
this is nice, guys, except for reference to "confusing and ambiguous bytecode" from Titan :-D
Actually it would be nice if Chris has some binary-in-binary search function incorporated and if my simple asm looks unreliable then, hehe, then maybe there are more authoritative sources. This would eliminate DllCall overhead, and for simple byte-search it is more than enough.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

You use a lot of redundant code

Where?

- I intentionally avoided the use of Crypt32.dll, because it is not present in earlier Windows versions.
- As discussed here, the result of CryptBinaryToStringA depends on the dll version, so you don't get consistent results.
- I described the FileRead *c / RtlMoveMemory trick before, but it uses double the memory and longer running time than BinRead, which was presented here as an alternative.
- Your code does not work:
pos := RegExMatch(src, "\Q" . RegExReplace(exp, "\\E", "\E\\E\Q"))
There are many errors in this single line. E.g. the "." concatenation truncates the resulting string at the first null. The replacement string does not need double "" in front of the second E. The terminating \E is missing, which is a problem if you want to follow the quoted literal with other RegEx constructs (otherwise you have just plain search capabilities). The length of the search string is not set correctly if "exp" contains nulls...

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

You could also use WinAPI functions to convert the binary data to a hex stream as shown in RegionWaitChange

It is well known, as it was already discussed in months earlier posts, possibly in other places, too.

wOxxOm
  • Members
  • 371 posts
  • Last active: Feb 20 2015 12:10 PM
  • Joined: 09 Feb 2006
btw, earlier I somewhat praised CreateFileMapping file contents searching approach, and since this function seems to be ignored generally by programmers, here's a little example of searching using my InBuf from the topic's first post, works with files *larger than 4GB*, accepts StartOffset that may be also larger than 4GB.

InFile( fileName, needleAddr, needleLen, StartOffset=0 )
{
	lRet=-1
	IfEqual,needleLen,0, return lRet
	IfEqual,needleAddr,0, return lRet
	hFile:=DllCall("CreateFile", "str", fileName,"uint",0x80000000 ;GENERIC_READ
				,"uint", 1 ;FILE_SHARE_READ
				,"uint", 0, "uint",3 ;OPEN_EXISTING
				,"uint",0x2000000 ;FILE_FLAG_BACKUP_SEMANTICS
				,"uint", 0)
	ifEqual,hFile,-1, return lRet

	VarSetCapacity( lBufLen, 8, 0 )
	NumPut( DllCall("GetFileSize","uint",hFile,"uint",&lBufLen+4), lBufLen )
	DllCall( "RtlMoveMemory", "int64 *",lBufLen64, "uint",&lBufLen, "uint",8 )
	lBufLen64 -= StartOffset
	If( lBufLen64>=0 )
	{	hMap:=DllCall("CreateFileMapping", "uint",hFile, "uint",0, "uint",2 ;PAGE_READONLY
					,"uint",0,"uint",0,"uint",0)
		if( hMap )
		{	lMax32b=0xFFFFFFFF
			lMaxView=0x40000000 ;1GB
			VarSetCapacity( SI, 36, 0 )
			DllCall("GetSystemInfo","uint",&SI)
			memAllocGranularity:=NumGet( SI, 28 )
			loop
			{	FileOffs:=(StartOffset//memAllocGranularity)*memAllocGranularity
				delta:=StartOffset-FileOffs
				lBufLenLo:=(lBufLen64+delta > lMaxView) ? lMaxView : lBufLen64+delta
				hView:=DllCall("MapViewOfFile", "uint",hMap, "uint", 4 ;FILE_MAP_READ
							,"uint",FileOffs>>32,"uint",FileOffs & lMax32b,"uint",lBufLenLo)
				ifEqual,hView,0, break
				lRet:=InBuf( hView, needleAddr, lBufLenLo, needleLen, delta )
				DllCall("UnmapViewOfFile","uint",hView)
				if( lRet!=-1 )
				{	lRet += FileOffs
					break
				}
				StartOffset += lBufLenLo-needleLen
				lBufLen64 -= lBufLenLo-needleLen
				IfLessOrEqual,lBufLen64,0, break
			}
			DllCall("CloseHandle","uint",hMap)
		}
	}
	DllCall("CloseHandle","uint",hFile)
	return lRet
}
This is possible because InBuf accepts memory address instead of string variable name. The advantage of CreateFileMapping may be obvious or not, which depends on whether you want to see it :-D In short - you don't need to maintain chunked reading of file bothering about chunk cross boundary checks in case your file is under 1GB (that is if you care to bother about it at all :-D ), and you don't need to read the whole contents of file into memory, the OS does it for you automatically when actual memory access occurs (in InBuf code). The disadvantage is probably also huge - given current implementation of InFile and InBuf there is no way to stop the process or show feedback...

P.S. wouldn't it be nice if someone shows how to use such memory address with RegExMatch ?

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

CreateFileMapping file contents searching approach

Very nice! Thanks for figuring this out. Your code works lightning fast in my XP laptop.

I guess it fools applications by mapping SW pointers (memory addresses) to a few physical memory blocks, filled with the appropriate file content on demand. With complicated regular expressions the performance could be horrible, though, because the buffer is accessed in a highly non-sequential manner, requiring frequent re-read of file blocks.

Nevertheless, it should work with RegExMatch/Replace, too, if they are dllcall-ed. The built in version expects the buffer to be an AHK variable with the attached length information, which is quite complicated to hack. Maybe, one could recompile AutoHotKey.exe with the RegEx functions exported, making a dllcall("AutoHotkey.exe\RegExMatch",... possible. With explicit length parameters it would also simplify handling binary AHK buffers.

wOxxOm
  • Members
  • 371 posts
  • Last active: Feb 20 2015 12:10 PM
  • Joined: 09 Feb 2006

Nevertheless, it should work with RegExMatch/Replace, too, if they are dllcall-ed

wow! wowwowowowow!!!!!!!

skrommel
  • Members
  • 193 posts
  • Last active: Jun 07 2010 08:30 AM
  • Joined: 30 Jul 2004
:) I need a case INsensitive search function, and InFile is very fast.

Any chance of an extended version, wOxxOm?

Skrommel

tinku99
  • Members
  • 560 posts
  • Last active: Feb 08 2015 12:54 AM
  • Joined: 03 Aug 2007
I am getting wrong results frequently with the regexp versions of binary search posted here. The mcode by wOxxOm works correctly everytime
example:
searching for 28001100 hex string should fail. Atleast according to my search using hexl-mode in emacs, but the regexp versions give wrong answer.
Titan's version gives wrong results also.

* 28001100 is a binary tag used in dicom images that i am doing some imageprocessing with.

testBinarySearch:
initBinarySearch()

;;;;; Example 2: find binary data in a binary buffer (read from ahk.exe)
file := A_AhkPath
BinRead(file, F)                       ; Binary haystack: ahk.exe
FileGetSize, sizeF, %file%
hex2bin(H,"28001100") ; binary search string
Msgbox % InBuf(&F, &H, sizeF, 4, StartOffset=0)
QtBuf( Q, QLen, H, 4)                         ; Make RegEx quote
MsgBox % "Binary needle found at offset " RegExMatch(F, Q) - 1
return

;; #Includes
#Include BinarySearch.ahk
;; MCode(ByRef code, hex)
MCode(ByRef code, hex) { ; allocate memory and write Machine Code there
   VarSetCapacity(code,StrLen(hex)//2)
   Loop % StrLen(hex)//2
      NumPut("0x" . SubStr(hex,2*A_Index-1,2), code, A_Index-1, "Char")
}


InBuf(haystackAddr, needleAddr, haystackSize, needleSize, StartOffset=0)
{   Static fun
   IfEqual,fun,
   {
      h=
      ( LTrim join
         5589E583EC0C53515256579C8B5D1483FB000F8EC20000008B4D108B451829C129D9410F8E
         B10000008B7D0801C78B750C31C0FCAC4B742A4B742D4B74364B74144B753F93AD93F2AE0F
         858B000000391F75F4EB754EADF2AE757F3947FF75F7EB68F2AE7574EB628A26F2AE756C38
         2775F8EB569366AD93F2AE755E66391F75F7EB474E43AD8975FC89DAC1EB02895DF483E203
         8955F887DF87D187FB87CAF2AE75373947FF75F789FB89CA83C7038B75FC8B4DF485C97404
         F3A775DE8B4DF885C97404F3A675D389DF4F89F82B45089D5F5E5A595BC9C2140031C0F7D0EBF0
      )
      VarSetCapacity(fun,StrLen(h)//2)
      Loop % StrLen(h)//2
         NumPut("0x" . SubStr(h,2*A_Index-1,2), fun, A_Index-1, "Char")
   }
   Return DllCall(&fun
      , "uint",haystackAddr, "uint",needleAddr
      , "uint",haystackSize, "uint",needleSize
      , "uint",StartOffset)
}

initBinarySearch()
{
Global Hex2Bin, Bin2Hex
;;;;; Initialization of 2 machine code functions, for setting input and showing results
MCode(Hex2Bin,"568b74240c8a164684d2743b578b7c240c538ac2c0e806b109f6e98ac802cac0e10"
. "4880f8a164684d2741a8ac2c0e806b309f6eb80e20f02c20ac188078a16474684d275cd5b5f5ec3")
MCode(Bin2Hex,"8B54240C85D2568B7424087E3A53578B7C24148A07478AC8C0E90480F9090F97C3F"
. "6DB80E30702D980C330240F881E463C090F97C1F6D980E10702C880C130880E464A75CE5F5BC606005EC3")
}



widow
  • Guests
  • Last active:
  • Joined: --
Hi, nice work! :D Here are some slightly modified functions from Hutch's Masm package ( http://www.masm32.com ) Some works right away, some dont..
In the lib folders of masm32 is alot of more machine code to play with. I tried to debug with Olly but when using dllcall-method to execute machine code it doesnt work so good. Anyone has a solution? I will post some functions here if/when i get them done.

SetBatchLines, -1
MCode(SZTRIM,"558BEC56578B75088B7D0833C983EE0183C601803E2074F8803E0974F3803E00750733C0C60"
      . "700EB1E8A043188043983C10184C074083C2172EF8BD1EBEBC6043A008BC28B4D085F5EC9C20400")
MCode(SZREV,"558BEC56578B75088B7D0C33C08A143088143883C00184D275F38B750C8B7D0C83E8018D7C38"
                 . "FFD1E8F7D82BF08A0C308A17881430880F83EF0183C00175EE8B450C5F5EC9C20800")
MCode(XORDATA,"558BEC83C4F45356578B45108945F80345148945FC8B75108A068845F78B4D0C8B75088B7D"
            . "088A06463245F75056FF45F88B75F88A06468B5DFC395DF8750C8B55108955F88B75F88A06"
            . "468845F75E588807474983F90075CD5F5E5BC9C210")
MCode(SZLEFT,"8B54240C8B4424048B4C240803C203CAF7DA53900FB61C02881C0A83C20175F4C6040A005BC"
             . "20C00")
MCode(WordCount ,"558BEC81C4F8FBFFFF535657C785F8FBFFFF000000008B5D1883FB017F0AB8FEFFFFFFE"
               . "9A20000008B750C0375102BF38BD6B9000100008BC38DBDFCFBFFFFF3AB8BCB498B7514"
               . "8DBDFCFBFFFF33C08A0646890C874975F78BCB49894DFC8B750C8B7D14037508EB1503C"
               . "12B45FC7905B80100000003F08B4DFC3BD67C4633C08A04313A043974118B8485FCFBFF"
               . "FF3BD875E28D743101EBE14933C08A04313A0439750F4979F5FF85F8FBFFFF8B4DFC46E"
               . "BCB8B8485FCFBFFFF3BD875AB8D743101EBB68B85F8FBFFFF5F5E5BC9C21400")
MCode(PebBase ,"64A130000000C20400")
MCode(K32Base,"558BECEB04000000008B4C240433D249668B513C66F7C200F875F23B4C0A3475EC6681394D"
            . "5A75E58908C9C20400")
MCode(IsAlpha,"807C2404417216807C24047A770F807C24045A770DB801000000C2040033C0C20400807C24"
            . "046172F4B802000000C20400")

;char := d
;retval := dllcall(&IsAlpha, "int",&char,"cdecl int") 
;msgbox %retval%
;return



string  := "THIS IS A STRING",   NrBytes   := dllcall(&SZTRIM, "int",&string ,"cdecl int")
szOrg := string
VarSetCapacity(tst,2048)       ; because ahk



memADDR    :=    dllcall(&SZLEFT, "int",&string ,"int",&tst,"int",5,"cdecl int") 
szLeft :=       readit(memADDR ,132,pid) 

;sztrim for counting byte's is not needed i think:
memADDR    :=    dllcall(&SZREV, "int",&string ,"int",&string,"cdecl int") 
szRev :=readit(memADDR ,NrBytes,pid) 
k32base :=       dllcall(&K32Base,int,0,"cdecl int")
pebBase :=       dllcall(&PebBase ,int,0,"cdecl int")

msgbox %szLeft%`n%szRev%`n%szOrg%`nk32:%k32base%`nPeb:%pebBase%
return


; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
readit(mem ,count,pid)
{
   ProcessHandle := DllCall("OpenProcess", "Int", 24, "Char", 0, "UInt", pid, "UInt") 
   VarSetCapacity(MVALUE,count) 
   DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",mem ,"Str",MVALUE,"UInt",count
                              ,"UInt *",0)
   return MVALUE
}

MCode(ByRef code, hex) {
   VarSetCapacity(code,StrLen(hex)//2) 
   Loop % StrLen(hex)//2 
      NumPut("0x" . SubStr(hex,2*A_Index-1,2), code, A_Index-1, "Char") 
} 
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
/*   
XORDATA    proc lpSource:DWORD,ln:DWORD,lpKey:DWORD,lnKey:DWORD  ; doesnt work atm
WordCount proc startpos:DWORD,lpSource:DWORD,srcLngth:DWORD, lpSubStr:DWORD,subLngth:DWORD   ; not this either
SZLEFT    proc src:DWORD,dst:DWORD,ln:DWORD      ; cant handle source=dest
SZREV   proc src:DWORD,dst:DWORD
ISALPHA proc char:byte                            ;returns TRUE on empty char (could be called "isnotnumeric")
*/

2009-11-30 [ Moderator!: Long Hex strings were shortened to avoid horizontal scroll bar. ]