It takes hours and hours to figure out all of this by yourself.
That's why I'll make a Tutorial here.
That all of you can easily understand what MCode is and what it is used for.
First of all I'm going to talk about what MCode is and why it is being used.
After that we'll have a look at some practical examples.
While doing this I'll tell you about the Problems.
1. Introduction:What is MCode ?
MCode is compiled code. For example if you make a C++ program it'll result in this kind of code.
AHK Scripts form the opposite of compiled code. In an AHK script the interpreter looks at the script text and then executes the corresponding code.
In an compiled language (e.g. C++) the compiler creates such Code directly.
The CPU is directly able to read such code.
MCode is such compiled code that can be used inside an AHK script.
We are able to execute this code via DllCall.
For Example the example MCode of the compiler I use is:
This is a Function that simply return 42 (The answer to all questions and everything.).2,x86:aipYww==,x64:uCoAAADD
1. Introduction:What is MCode good for?
MCode is one of the various ways to increase the speed of your Script/Program.
Especially if you're calculating with a huge amount of Data.
For example you can make functions that can change a GDI+ picture.
MCode also gives you the possibility to execute code on a very low level (Assembly).
1. Introduction:Sounds nice. What is needed?
1. Knowledge about C++ and DllCalls:
If you don't think you're good enough in DllCalls forget it.
You can find an super nice C++ here:
http://www.cplusplus.com/doc/tutorial/
2. A compiler:
The compiler I use can be found at:
http://ahkscript.org/boards/viewtopic.php?f=6&t=4642
You need to install GCC
http://www.autohotkey.com/board/topic/55162-mcodegen-easily-transform-cc-code-into-mcode/
You need to install Visual C++
I'll add any other suggestions here.
3. an MCode Function:
I use this one:
Code: Select all
MCode(mcode)
{
static e := {1:4, 2:1}, c := (A_PtrSize=8) ? "x64" : "x86"
if (!regexmatch(mcode, "^([0-9]+),(" c ":|.*?," c ":)([^,]+)", m))
return
if (!DllCall("crypt32\CryptStringToBinary", "str", m3, "uint", 0, "uint", e[m1], "ptr", 0, "uint*", s, "ptr", 0, "ptr", 0))
return
p := DllCall("GlobalAlloc", "uint", 0, "ptr", s, "ptr")
if (c="x64")
DllCall("VirtualProtect", "ptr", p, "ptr", s, "uint", 0x40, "uint*", op)
if (DllCall("crypt32\CryptStringToBinary", "str", m3, "uint", 0, "uint", e[m1], "ptr", p, "uint*", s, "ptr", 0, "ptr", 0))
return p
DllCall("GlobalFree", "ptr", p)
}
After you've added the function to your stdli directory you are ready to program your own functions.
The example code, of the compiler I mentioned above is this one:
Code: Select all
int MyFunction()
{
return 42;
}
When you press the Create Machine Code button, the online compiler will create this code.
Code: Select all
MyFunction := MCode("2,x86:uCoAAADD,x64:uCoAAADD")
Your Script should look like this.
Code: Select all
MyFunction := MCode("2,x86:uCoAAADD,x64:uCoAAADD")
Msgbox % DllCall(MyFunction,"cdecl")
But now a more complex function.
It searches for the end of an string: The 0-Char:
Code: Select all
int stringlen(char *str)
{
int i=0;
for (; str[i]!=0; i++);
return i;
}
Code: Select all
stringlen := MCode("2,x86:i0wkBDPAOAF0B0CAPAgAdfnD,x64:M8A4AXQKSP/B/8CAOQB19vPD")
MsgBox, % DllCall(stringlen, "astr", "test","cdecl")
Code: Select all
unsigned int MyFunction(unsigned int a,unsigned int b)
{
if (a>0)
return MyFunction(a-1,b*a);
else
return b;
}
In order to do so it calls itself.
Now the AHK code.
Code: Select all
MyFunction := MCode("2,x86:i0wkBItEJAiFyXQKjWQkAA+vwUl1+sM=,x64:hcl0Bw+v0f/JdfmLwsM=")
Msgbox % DllCall(MyFunction,"int",3,"int",1,"cdecl")
But you have to write too much.(Yes I'm lazy)
Actually you dont need this part: ,"int",1,"cdecl") We could create a caller function that does the thing we want.
In order to use the stdcall convention(Wich is the standard for the DllCall function) you have to put a _stdcall before the function.
Your C++ code will look like this:
Code: Select all
unsigned int MyFunction(unsigned int a,unsigned int b)
{
if (a>0)
return MyFunction(a-1,b*a);
else
return b;
}
unsigned int _stdcall MyFunctionCaller(unsigned int a)
{
return MyFunction(a,1);
}
Code: Select all
MyFunction := MCode("2,x86:i0wkBItEJAiFyXQKjWQkAA+vwUl1+sM=,x64:hcl0Bw+v0f/JdfmLwsM=")
MyFunctionCaller := MCode("
(LTrim Join
2,x86:i0QkBIXAdA5QSFDoAAAAAIPECMIEALgBAAAAwgQA,x64:i9GFyXQH/8npAAAAALgBAAAAww==
)")
Msgbox % DllCall(MyFunctionCaller,"uint",3)
No we didn't make a mistake. The Problems name is MCode:
OK Lets imagine you have a AHK script, but you have to call every function by its position: (e.g. call Char nr. 144.).
This is where your function starts at Char 144.
Now lets imagine we put some new code in front of your function.
Your function will be moved backwards e.g. 20 Chars.
If you don't change the starting nr. of your function you wont be able to call it properly.
In Machine Code it is exactly like this:
A function is called by calling the start address of the function.
But the function is added after the AHK stuff.
That's why your function is calling something else. This leads to errors.
The compiler is able to tell you the starting address it expected:
(Note: Not for 64 bit PCs)
Code: Select all
/*
unsigned int MyFunction(unsigned int a,unsigned int b)
{
if (a>0)
return MyFunction(a-1,b*a);
else
return b;
}
unsigned int MyFunctionaddress()
{
return (unsigned int)(&MyFunction);
}
*/
MyFunction := MCode("2,x86:i0wkBItEJAiFyXQKjWQkAA+vwUl1+sM=,x64:hcl0Bw+v0f/JdfmLwsM=")
MyFunctionaddress := MCode("2,x86:uAAAAADD,x64:SI0FAAAAAMM=")
Msgbox % "The expected Address of the function is :" DllCall(MyFunctionaddress)
Msgbox % "The actual address of the function is :" MyFunction
So what do we do now
The answer is function pointers. ...
Functionpointers are documented here:
http://www.cplusplus.com/doc/tutorial/pointers/
Please read through the site mentioned before reading further.
To call our function in MCode we have to give it the pointer to our function.
Code: Select all
unsigned int MyFunction(unsigned int a,unsigned int b)
{
if (a>0)
return MyFunction(a-1,b*a);
else
return b;
}
unsigned int _stdcall MyFunctionCaller(unsigned int a,unsigned int(*MyFunction)(unsigned int,unsigned int) )
{
return (*MyFunction)(a,1);
}
Code: Select all
MyFunction := MCode("2,x86:i0wkBItEJAiFyXQKjWQkAA+vwUl1+sM=,x64:hcl0Bw+v0f/JdfmLwsM=")
MyFunctionCaller := MCode("2,x86:i0QkBGoBUP9UJBCDxAjCCAA=,x64:SIvCugEAAABI/+A=")
When we call the new function we also have to add the Function pointer.
Code: Select all
MyFunction := MCode("2,x86:i0wkBItEJAiFyXQKjWQkAA+vwUl1+sM=,x64:hcl0Bw+v0f/JdfmLwsM=")
MyFunctionCaller := MCode("2,x86:i0QkBGoBUP9UJBCDxAjCCAA=,x64:SIvCugEAAABI/+A=")
Msgbox % DllCall(MyFunctionCaller,"uint", 3,"UPtr",MyFunction)
There are also other things that wont work in autohotkey:
- Global&Static Variables
- Objects(thiscall konvention)
- Sometimes even calling the own function
- Preset Floats can cause difficulties cause compiler rather sores them at a specific address than in a code.
Also feel free to share any MCode function you've created here.
I might need some good examples for further chapters.