[V2] Assembly to Machine-code using Keystone.dll

Post your working scripts, libraries and tools.
HakitoJin
Posts: 17
Joined: 02 Oct 2016, 17:20

[V2] Assembly to Machine-code using Keystone.dll

08 May 2019, 17:49

This open-source assembler seems to have an ok license, so I made a function for it, expect assembly of small code to take around 0.3ms.
You will need a dll (step1-2), my function (step3), and the knowledge of how to use said function (step4-5).

Let me know if anything doesn't work, I will make sure to immediately question my decision to continue learning programming given all these unpredictable errors that make me feel like I can't even evaluate 1+1 without some god damn computer out there throwing an error, and then possibly try to fix the issue.



Steps:

1. Go to www.keystone-engine.org/download/ and download the WIN-32 package (64 might work too).

2. Locate keystone.dll in the archive, take it and place it anywhere you want, but don't lose it, we'll need the path to this dll in just a sec.

3. Place this function in your code:

Code: Select all

;If second parameter is 1, then we return a space delimited hex string, otherwise we return an array that contains an index for each byte
Keystone(Code,Readable:=0) ;by Ħakito - uses a public domain LICENSE: choosealicense.com/licenses/unlicense
{
	Local
	Static DLLPath,DLLHandle,KS_OPEN,KS_ASM,KS_FREE,KS_CLOSE,kspointer , KS_ERR_OK:=0 , KS_ARCH_X86:=4 , KS_MODE_32:=4
	iF not DLLPath ;Locate the dll, afterwards the function can be called normally, and the dll is also loaded on the first normal run
	{
		iF FileExist(Code) ;Using contents of Code as DLLPath
		{
			DLLPath:=Code
			return Readable?"DLL HAS BEEN FOUND":1
		}
		else return Readable?"DLL NOT FOUND":0
	}
	else iF not DLLHandle ;We locate the DLL above, but we don't load it, we only do that when we are required to assemble some code.
	{
		iF FileExist(DLLPath)
		{
			DLLHandle:=DllCall("LoadLibrary", "Str",DLLPath, "Ptr")
			KS_OPEN:=DllCall("GetProcAddress", "Ptr",DLLHandle, "AStr","ks_open", "Ptr")
			KS_ASM:=DllCall("GetProcAddress", "Ptr",DLLHandle, "AStr","ks_asm", "Ptr")
			KS_FREE:=DllCall("GetProcAddress", "Ptr",DLLHandle, "AStr","ks_free", "Ptr")
			KS_CLOSE:=DllCall("GetProcAddress", "Ptr",DLLHandle, "AStr","ks_close", "Ptr")
			
			VarSetCapacity(ks,8,0)
			iF KS_ERR_OK != DllCall(KS_OPEN, "UInt",KS_ARCH_X86, "UInt",KS_MODE_32, "UPtr",&ks, "CDecl Int")
				return Readable?"ERROR AT KS_OPEN":0
			kspointer:=NumGet(&ks,0,"UInt")
		}
		else	return Readable?"DLL NOT FOUND":0
	}
	
	VarSetCapacity(Count,4,0) , VarSetCapacity(Size,4,0) , VarSetCapacity(Encode,8,0)
	Try ;This thing can crash autohotkey!
	{
		iF KS_ERR_OK != DllCall(KS_ASM, "UPtr",kspointer, "AStr",Code, "UInt64",0, "UPtr",&Encode, "UInt",&Size, "UInt",&Count, "CDecl Int")
			return Readable?"ERROR AT KS_ASM":""
	}
	Catch
	{
		return Readable?"FATAL ERROR AT KS_ASM":""
	}
	
	Encodepointer:=NumGet(&Encode,0,"UInt")
	Size:=NumGet(&Size,0,"UInt")
	Count:=NumGet(&Count,0,"UInt")
	iF Readable ;Creates a human readable hex string
	{
		Loop Size
			Output.=(A_Index!=1?" ":"") Format("{1:02X}", NumGet(Encodepointer,A_Index-1,"UChar"))
	}
	else
	{
		;Old discarded method :   Output:=[StrGet(Encodepointer,Size,"CP0"),Size]
		Output:=[]
		Loop Size
			Output.Push(NumGet(Encodepointer,A_Index-1,"UChar"))
	}
		
	DllCall(KS_FREE, "UPtr",Encodepointer, "CDecl Int")
	;DllCall(KS_CLOSE, "UPtr",kspointer, "CDecl Int") ;No need to close it, we can just keep reusing the same handle for future calls
	return Output
}

4. Call the function like this in the auto-execute area of your script, this tells the function where the dll is located for when it needs it.

Code: Select all

Keystone("C:\Users\Hakito\Desktop\keystone.dll")

5. Now you can start converting Assembly to Machine-code! Here are some examples.

Code: Select all

;Return Machine-code as an array
Arr := Keystone("ADD EAX,0x2; CMP EAX,0x2; JG label; SUB EAX,0x1; label:; RET")
Msgbox Arr[1] " " Arr[2] " " Arr[3] " " Arr[4] ; etc...

;Return as a readable string
Msgbox Keystone("ADD EAX,0x2; CMP EAX,0x2; JG label; SUB EAX,0x1; label:; RET",True)
;Expected output: 83 C0 02 83 F8 02 7F 03 83 E8 01 C3

;Another readable string
Msgbox Keystone("
(
VAR1 = 0x05
CMP EAX,VAR1
JE label
CMP EAX,0x04
JE label
CMP EAX,0x03
JE label
MOV [ECX+EAX*0x4+0x000000C0],EDX
label:
RET
)",True)
;Expected output: "83 F8 05 74 11 83 F8 04 74 0C 83 F8 03 74 07 89 94 81 C0 00 00 00 C3"
Assembly code has to be delimited using ; or `n, and you must write numbers in hex to avoid errors.

(Written for AHKv2-a100)
User avatar
elModo7
Posts: 217
Joined: 01 Sep 2017, 02:38
Location: Spain
Contact:

Re: [V2] Assembly to Machine-code using Keystone.dll

13 May 2019, 03:00

Is this exclussive for ahk v2 or is there a possibility to have it in v1?
(V1 throws "ERROR AT KS_ASM")

Cheers and thanks for sharing!
HakitoJin
Posts: 17
Joined: 02 Oct 2016, 17:20

Re: [V2] Assembly to Machine-code using Keystone.dll

13 May 2019, 08:25

elModo7 wrote:
13 May 2019, 03:00
Is this exclussive for ahk v2 or is there a possibility to have it in v1?
(V1 throws "ERROR AT KS_ASM")

Cheers and thanks for sharing!
Here is some code that seems to work in v1, also, ew v1...

Code: Select all

Keystone("C:\Users\Hakito\Desktop\keystone.dll")
Msgbox % Keystone("ADD EAX,0x2; CMP EAX,0x2; JG label; SUB EAX,0x1; label:; RET",True)
Msgbox % Keystone("
(
VAR1 = 0x05
CMP EAX,VAR1
JE label
CMP EAX,0x04
JE label
CMP EAX,0x03
JE label
MOV [ECX+EAX*0x4+0x000000C0],EDX
label:
RET
)",True)
return


;If second parameter is 1, then we return a space delimited hex string, otherwise we return an array that contains an index for each byte
Keystone(Code,Readable:=0) ;For AHKv1 - by Hakito - uses a public domain LICENSE: choosealicense.com/licenses/unlicense
{
	Local
	Static DLLPath,DLLHandle,KS_OPEN,KS_ASM,KS_FREE,KS_CLOSE,kspointer , KS_ERR_OK:=0 , KS_ARCH_X86:=4 , KS_MODE_32:=4
	iF not DLLPath ;Locate the dll, afterwards the function can be called normally, and the dll is also loaded on the first normal run
	{
		iF FileExist(Code) ;Using contents of Code as DLLPath
		{
			DLLPath:=Code
			return Readable?"DLL HAS BEEN FOUND":1
		}
		else return Readable?"DLL NOT FOUND":0
	}
	else iF not DLLHandle ;We locate the DLL above, but we don't load it, we only do that when we are required to assemble some code.
	{
		iF FileExist(DLLPath)
		{
			DLLHandle:=DllCall("LoadLibrary", "Str",DLLPath, "Ptr")
			KS_OPEN:=DllCall("GetProcAddress", "Ptr",DLLHandle, "AStr","ks_open", "Ptr")
			KS_ASM:=DllCall("GetProcAddress", "Ptr",DLLHandle, "AStr","ks_asm", "Ptr")
			KS_FREE:=DllCall("GetProcAddress", "Ptr",DLLHandle, "AStr","ks_free", "Ptr")
			KS_CLOSE:=DllCall("GetProcAddress", "Ptr",DLLHandle, "AStr","ks_close", "Ptr")
			
			VarSetCapacity(ks,8,0)
			iF (KS_ERR_OK != DllCall(KS_OPEN, "UInt",KS_ARCH_X86, "UInt",KS_MODE_32, "UPtr",&ks, "CDecl Int"))
				return Readable?"ERROR AT KS_OPEN":0
			kspointer:=NumGet(&ks,0,"UInt")
		}
		else	return Readable?"DLL NOT FOUND":0
	}

	VarSetCapacity(Count,4,0) , VarSetCapacity(Size,4,0) , VarSetCapacity(Encode,8,0)
	Try ;This thing can crash autohotkey!
	{
		iF (KS_ERR_OK != DllCall(KS_ASM, "UPtr",kspointer, "AStr",Code, "UInt64",0, "UPtr",&Encode, "UInt",&Size, "UInt",&Count, "CDecl Int"))
			return Readable?"ERROR AT KS_ASM":""
	}
	Catch
	{
		return Readable?"FATAL ERROR AT KS_ASM":""
	}
	
	Encodepointer:=NumGet(&Encode,0,"UInt")
	Size:=NumGet(&Size,0,"UInt")
	Count:=NumGet(&Count,0,"UInt")
	iF (Readable) ;Creates a human readable hex string
	{
		Loop % Size
			Output.=(A_Index!=1?" ":"") Format("{1:02X}", NumGet(Encodepointer+0,A_Index-1,"UChar"))
	}
	else
	{
		;Old discarded method :   Output:=[StrGet(Encodepointer,Size,"CP0"),Size]
		Output:=[]
		Loop % Size
			Output.Push(NumGet(Encodepointer+0,A_Index-1,"UChar"))
	}
		
	DllCall(KS_FREE, "UPtr",Encodepointer, "CDecl Int")
	;DllCall(KS_CLOSE, "UPtr",kspointer, "CDecl Int") ;No need to close it, we can just keep reusing the same handle for future calls
	return Output
}

Return to “Scripts and Functions (v2)”

Who is online

Users browsing this forum: No registered users and 6 guests