Δευτέρα 7 Νοεμβρίου 2022

Call external function from machine code

From revision 15,  version 11 we can get the address of loaded functions (from external libraries)

We use for this example the PathAddBackslash function from Shlwapi.dll (version 4.71 or later):

https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathaddbackslashw

Description copied from link above:

Adds a backslash to the end of a string to create the correct syntax for a path. If the source path already has a trailing backslash, no backslash will be added.

There are two versions, the A (Ansi) and W (Unicode Utf16Le). We use the W one, so we place the UTF16LE at buffer BinaryData at offset 0. The PathAddBackSlash get a LPTSTR as parameter which is a long pointer to a string. This string must written with 2 zero bytes (2 for W version). So we make the buffer using clause Clear to fill the buffer with 0.

We read at the link above this: [in, out] pszPath which say that we get to same pointer the output 

Also this function has a Return value. This value is the LPSTR (which we provide pushing this in stack), or NULL (zero)  if the max_path (internal constant) not allow to add a character blackslash (2 bytes because we have the W version).

Using the function from M2000 code (not the machine code), we place the space of zeros to the string with the path (expanded to 250 characters, 500 bytes). The M2000 strings are BSTR type so they have two zero at the end (not included in Len() - also you know that M2000 Len() return number of words, two bytes, and works for ANSI strings returning +0.5 for the last byte when we have odd length in bytes). The most interesting part of BSTR is that normally they are immutable, so a A$=A$+"1" or A$+="1" produce a new BSTR string from other two.  But the memory of actual bytes of string (if string isn't Null length, which  Null length means there is no memory for string) can be changed and this is the way for the call to PathAddBackSlash() do (as M2000 function), pass the address of the pointer of actual string data. From the machine code example we pass the BinaryData(0), the real address of the 0 offset at buffer BinaryData, where the input string has the first character.

What about the length of string? The PathAddBackSlashW expect to find the two trailing zero bytes ,which is character 0 in Utf16LE, so this character can't be in start or middle position. The call from M2000 pass the string pointer to BSTR which at -4 offset has the length of string, but the PathAddBackSlashW has no clue that this pointer is for a BSTR string. The designers of BSTR type allow character 0 in any position inside the string, because the length handled by the length field. Also they have one character with code 0 (2 zeros for the W version of function), for using it for counting the length using the pointer of BSTR as LPSTR pointer. 
Function leftPart$(a$, 0) get the left part of string, from code 0 character (chr$(0) and chrcode$(0) produce this zero code character). LeftPart$() can work with a character  number or a string as second parameter. Here we get 123 and 123 4
a$="123 456"
Print leftpart$(a$, 32), leftpart$(a$, "5")


Run m2000.exe, write Edit A, then copy the code bellow, press Esc to exit editor, write A and press enter to execute the module A. Write Save machinecode1 and press enter. Write New press enter. Write load machinecode1 and press enter. Write edit A to edit the code.


//	from Version 11 Revision 15
// All declared external functions can return the address using the name only as read only variable.
Declare PathAddBackslash Lib "Shlwapi.PathAddBackslashW" { &Path$ }
Hex "Address of PathAddBackslash: ";PathAddBackslash
// Setup Buffers for Machine Code
Buffer Clear BinaryData as byte*1024
Return BinaryData, 0:="C:"
// Execution buffer change to READ ONLY at code execution.
Buffer code alfa as byte*1024
// Simple program:
// push BinaryData(0)
// call PathAddBackslash
// ; xor eax, eax  ; clear eax ; optional
// ret
Pc=0
//* example of __stdcall */ push argN : push arg2 : push arg1 and then Call function
OpLong(0x68, BinaryData(0)) ' parameter the real address of BinaryData at offset 0.
OpLong(0xE8, PathAddBackslash-pc-5-alfa(0)) ' rel32 (relative addres 32bit)
// OpByte(0x31, 0xC0) ' now eax=0  ' without this we get non zero in
Ret() ' return
// PathAddBackslash
Try Ok {
Execute Code alfa, 0
// if eax<>0 then Execute Code raise Error with Error number = eax
Print "eax=0"  // not displayed
}
Print "RetValue in eax:";Uint(Error)
Print LeftPart$(eval$(BinaryData, 0, 255),0)


// Call the function PathAddBackslash() (the former way)
Test1()


End
Sub Test1()
Local P$ = "C:"+String$(Chr$(0), 250)
Local A= PathAddBackslash(&P$)
Print LeftPart$(P$,0)
End Sub
Sub Ret()
Return alfa, pc:=0xC3
pc++
End Sub
Sub OpByte()
Return alfa, pc:=number, pc+1:=number
pc+=2
End Sub
Sub OpLong()
Return alfa, pc:=number, pc+1:=number as long
pc+=5
End Sub


Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου

You can feel free to write any suggestion, or idea on the subject.