There are times that you have a binary string inside a (Plus!)
DataBloc.
Now, there is a problem if you want to output that binary data for example in the debug window of your script, or if you want to manipulate it further in your script as being a normal string.
This because the
ReadString() method will only read up until the first Null character ("\0"); and a binary string can of course contain much more data after a Null character.
You also can't use the
ReadBSTR() method directly, because that binary string isn't a BSTR string (a
BSTR is a unicode string prefixed with its length, thus, allowing for Null characters). Even if you would add the known length of that binary string in front of it, you still would need to convert it to 'pseudo' unicode first to be able to read it using
ReadBSTR().
'pseudo' because all you need to do is add a Null byte (0x0) after each byte in the binary string. It isn't really unicode unicode in that way, but it can be read using unicode read functions like
ReadBSTR().
You could use some Unicode conversation APIs for that (eg:
MultiByteToWideChar API). But the problem with that is that those APIs will actually change some of the bytes into another byte + not a Null character; they actaully
map the characters according to a 'code page', they do not simply append a Null byte after each byte. Aka they have the very real potential to mangle up the real data from the binary string and thus producing the wrong output.
In the end, what you probably end up with is something like this:
js code:
// BinaryData is a DataBloc containing bytes
// OutputString is going to be a BSTR, so we can output it using ReadBSTR()
var OutputString = Interop.Allocate(BinaryData.Size * 2 + 4 + 2);
// *2 because it contains a unicode string
// +4 to accomodate for the DWORD length
// +2 for the unicode termination Null character
// Add the size of the string into the first DWORD of the BSTR
OutputString.WriteDWORD(0, BinaryData.Size * 2);
>>>// Convert the binary DataBloc into a pseudo unicode string (aka append a Null character after each byte)<<<
>>>for (var i=0; i < BinaryData.Size; i++) {<<<
>>> OutputString.SetAt(i * 2 + 4, BinaryData.GetAt(i));<<<
>>>}<<<
// Read out the BSTR string and output it
Debug.Trace(OutputString.ReadBSTR(0));
Now, this will work, but it will be dead slow. So, in many cases it is realy useless. For example, think about outputting a memory dump of 10000 bytes with the above code. But what if we can replace that slow loop function with assembler code (you can't go any faster than that)? Wouldn't that be cool? Well, you can:
js code:
// BinaryData is a DataBloc containing bytes
// OutputString is going to be a BSTR, so we can output it using ReadBSTR()
var OutputString = Interop.Allocate(BinaryData.Size * 2 + 4 + 2);
// *2 because it contains a unicode string
// +4 to accomodate for the DWORD length
// +2 for the unicode termination Null character
// Add the size of the string into the first DWORD of the BSTR
OutputString.WriteDWORD(0, BinaryData.Size * 2);
>>>// Create our ASM routine<<<
>>>// It takes the memory pointer to a byte array as input and copies the individual bytes to the memory offset given in the second paramater,<<<
>>>// but each time increasing the memory offset by two (instead of by one). Thus essentially doubling its size. eg: \1\2\3 is copied as \1\0\2\0\3\0.<<<
>>>var sASM = "\u8B55\u244C\u8B10\u2454\u8B0C\u246C\u6608\uB60F\u0045\u8966\u4502\u4242\uF3E2\uC25D\u0010";<<<
>>>var oASM = Interop.Allocate(sASM.length * 2 + 2);<<<
>>>oASM.WriteString(0, sASM);<<<
>>>// Call the mighty ASM routine<<<
>>>// Here the 'magic' happens; we (ab)use the CallWindowProc API.<<<
>>>Interop.Call("User32", "CallWindowProcW", oASM.DataPtr, BinaryData.DataPtr, OutputString.DataPtr + 4, BinaryData.Size, null);<<<
// Read out the BSTR string and output it
Debug.Trace(OutputString.ReadBSTR(0));
This code will run as good as instantly because of the used ASM:
js code:
/*
HEX ASM
0000 55 push ebp ; save old frame pointer
0001 8B 4C 24 10 mov ecx, [esp+10h] ; copy value of parameter 3 (=length of input string) into counter register (CX)
0005 8B 54 24 OC mov edx, [esp+0Ch] ; copy value of parameter 2 (=mem adress of output string) into data register (DX)
0009 8B 6C 24 08 mov ebp, [esp+8] ; copy value of parameter 1 (=mem adress of input string) into base pointer register (BP)
'start':
000D 66 0F B6 45 00 movzx ax, byte ptr [ebp+0] ; copy byte value from mem adress stored in EBP to AX
0012 66 89 02 mov [edx], ax ; copy AX to mem adress stored in EDX
0015 45 inc ebp ; increment EBP
0016 42 inc edx ; increment EDX
0017 42 inc edx ; increment EDX
0018 E2 F3 loop 'start' ; decrement ECX and jump to 'start' if CX!=0
001A 5D pop ebp ; restore old frame pointer
001B C2 10 retn 10h ; end
*/
---------
EDIT:
Apparently there was already another nice example of using ASM in scripting, created by Mnjul some time ago
here (note: thread is in beta testing section, so only beta testers can access it) and Matty used it to create
this script.
The principle is just the same: write the ASM code and use the opcodes (=the hexadecimal values) to make a binary data string and place that in memory.
His script uses the
WriteProcessMemory API to inject (asm) code into the process memory (but not yet run it!), rerouting the default call to the
WinProc procedure to this code instead, and calling the 'old'
WinProc procedure after the new injected code has finished.