DTeCH Newbie cheater
Reputation: 0
Joined: 19 Jul 2013 Posts: 23 Location: Cayman Islands
|
Posted: Tue Oct 08, 2013 6:53 am Post subject: [VB.NET] Search for array of bytes, & return Address |
|
|
Hey guys
I have been trying alot of different projects found online, but have not managed to get a workable project built.
All of the online projects tend to be done with the core reason for making it being an overwhelming one sided assumption that all users will use pointers, or some type of base address which is not correct to think. Not all programs can be targeted with base pointers.
In my case, the locations change every time the game is loaded, so I require the need to scan for the location first... THEN continue from there.
I have tried BlackMagic-1.1, & it seems to allow the locating of addresses, & it does so rather quickly (suprisingly fast! ), however, I am not familiar whith what functions are usually used to complete my desired goals.
I have found that with BlackMagic1.1, you pass a string value with a hex string (with spaces - like "00 B8 00 00 F1 86 29 C7...") along with a mask string (like: "?x??xxxx") to the FindPattern function like this (With just a multiline textbox, & a button on a new project form):
Code: | Imports System.Collections.Generic
imports System.Diagnostics
imports System.Text
Imports Magic
Public Class Form1
Private Const find_PATTERN As String = "54 68 69 73 49 73 54 65 73 74 41 00 00 00 00" ' ThisIsTestA
Private Const find_MASK As String = "xxxxxxxxxxx????"
Private WithEvents Timer1 As System.Timers.Timer
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim ThisIsTestB_Bytes() As Byte = {&H54, &H68, &H69, &H73, &H49, &H73, &H54, &H65, &H73, &H74, &H42, &H00, &H00, &H00, &H00} 'ThisIsTestB in Bytes
Dim dwCodeLoc As UInt32
Dim bmGame As BlackMagic = New BlackMagic()
If (bmGame.OpenProcessAndThread(SProcess.GetProcessFromProcessName("plugin-container"))) Then 'Firefox
TextBox1.Text = bmGame.GetModuleFilePath() & vbCrLf
Dim dt As DateTime = DateTime.Now
dwCodeLoc = bmGame.FindPattern(find_PATTERN, find_MASK)
Dim dwCodeLocStr As String = "&H" & dwCodeLoc '<==== &H & the UInt number, or should it be &H & the HEX version of the UInt number "&H" & Hex(dwCodeLoc), or even just Hex Hex(dwCodeLoc), or UInt version with no &H???
' I've experimented with different ways of reading from that location (address)
TextBox1.Text += bmGame.ReadASCIIString(dwCodeLocStr, 15) & vbCrLf
Dim tmpByte() As Byte = MemMod.ReadMemory(dwCodeLocStr, 15)
TextBox1.Text += Encoding.ASCII.GetString(tmpByte) & vbCrLf
TextBox1.Text += bmGame.ReadBytes(dwCodeLocStr, 15) & vbCrLf ' Encoding.ASCII.GetString(MemMod.ReadMemory(dwCodeLoc, "e_rare_rubber_duck_ticket".Length)) & vbCrLf
TextBox1.Text += "Pattern found in " & DateTime.Now.Subtract(dt).TotalMilliseconds & "ms" & vbCrLf
TextBox1.Text += "Code location: " & dwCodeLocStr & vbCrLf
TextBox1.Text += "find_PATTERN: " & bmGame.ReadUInt(dwCodeLocStr + &H16) & vbCrLf
TextBox1.Text += "CURMGR_OFFSET: " & bmGame.ReadUInt(dwCodeLocStr + &H1C) & vbCrLf
TextBox1.Text += Encoding.ASCII.GetString(tmpByte) & vbCrLf
' I've experimented with different ways of writing to that location (address)
bmGame.WriteASCIIString(dwCodeLoc, "ThisIsTestB")
'bmGame.WriteBytes(dwCodeLoc, ThisIsTestB_Bytes, 15) ', RubberDucks)
'MemMod.WriteMemory(dwCodeLocStr, "ThisIsTestB" + vbNullChar, True)
'MemMod.WriteMemory(dwCodeLocStr, ThisIsTestB_Bytes)
dwCodeLoc = 0
' Check to see if ThisIsTestA is till at the first detected location
dwCodeLoc = bmGame.FindPattern(find_PATTERN, find_MASK) 'Finds ThisIsTestA at the same location as before even though I attempted to overwrite it with ThisIsTestB string already.
TextBox1.Text += dwCodeLocStr & vbCrLf
TextBox1.Text += Encoding.ASCII.GetString(MemMod.ReadMemory(dwCodeLocStr, 15)) & vbCrLf
TextBox1.Text += bmGame.ReadASCIIString(dwCodeLocStr, 15) & vbCrLf
TextBox1.Text += Encoding.ASCII.GetString(MemMod.ReadMemory(dwCodeLoc, "ThisIsTestB".Length)) & vbCrLf
Else
TextBox1.Text = "Game could not be opened for read/write." & vbCrLf
End If
TextBox1.Text += "Test Done."
End Sub
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Timer1 = New System.Timers.Timer
Timer1.Enabled = True
Timer1.Interval = 1000
Timer1.Start() ' = True
GC.Collect()
End Sub
Private Sub Timer1_Elapsed(sender As Object, e As System.Timers.ElapsedEventArgs) Handles Timer1.Elapsed
If MemMod.UpdateProcessHandlePublic() Then ' Check if the game is running
picBrowser1.Image = ImageList1.Images.Item(1)
picBrowser1.Invalidate()
picBrowser1.Refresh()
Else
picBrowser1.Image = ImageList1.Images.Item(0)
picBrowser1.Invalidate()
picBrowser1.Refresh()
End If
End Sub
End Class |
...with this module added for the MemMod functions:
Code: | Option Strict On
Imports System.Runtime.InteropServices
Imports System.Text
Module MemMod
<DllImport("kernel32.dll")> _
Private Function OpenProcess(ByVal dwDesiredAccess As UInteger, <MarshalAs(UnmanagedType.Bool)> ByVal bInheritHandle As Boolean, ByVal dwProcessId As Integer) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True)> _
Private Function WriteProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByVal lpBuffer As Byte(), ByVal nSize As IntPtr, <Out()> ByRef lpNumberOfBytesWritten As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True)> _
Private Function ReadProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, <Out()> ByVal lpBuffer() As Byte, ByVal dwSize As IntPtr, ByRef lpNumberOfBytesRead As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True)>
Private Function CloseHandle(ByVal hObject As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Private Const PROCESS_VM_WRITE As UInteger = &H20
Private Const PROCESS_VM_READ As UInteger = &H10
Private Const PROCESS_VM_OPERATION As UInteger = &H8
Public TargetProcess As String = "plugin-container" 'Firefox
Private ProcessHandle As IntPtr = IntPtr.Zero
Private LastKnownPID As Integer = -1
Public Function ReadMemory(Of T)(ByVal address As Integer) As T
Return ReadMemory(Of T)(address, 0, False)
End Function
Public Function ReadMemory(ByVal address As Integer, ByVal length As Integer) As Byte()
Return ReadMemory(Of Byte())(address, length, False)
End Function
Private Function ProcessIDExists(ByVal pID As Integer) As Boolean
For Each p As Process In Process.GetProcessesByName(TargetProcess)
If p.ID = pID Then Return True
Next
Return False
End Function
Public Function UpdateProcessHandlePublic() As Boolean
If LastKnownPID = -1 OrElse Not ProcessIDExists(LastKnownPID) Then
If ProcessHandle <> IntPtr.Zero Then CloseHandle(ProcessHandle)
Dim p() As Process = Process.GetProcessesByName(TargetProcess)
If p.Length = 0 Then Return False
LastKnownPID = p(0).Id
ProcessHandle = OpenProcess(PROCESS_VM_READ Or PROCESS_VM_WRITE Or PROCESS_VM_OPERATION, False, p(0).Id)
If ProcessHandle = IntPtr.Zero Then Return False
End If
Return True
End Function
Private Function UpdateProcessHandle() As Boolean
If LastKnownPID = -1 OrElse Not ProcessIDExists(LastKnownPID) Then
If ProcessHandle <> IntPtr.Zero Then CloseHandle(ProcessHandle)
Dim p() As Process = Process.GetProcessesByName(TargetProcess)
If p.Length = 0 Then Return False
LastKnownPID = p(0).Id
ProcessHandle = OpenProcess(PROCESS_VM_READ Or PROCESS_VM_WRITE Or PROCESS_VM_OPERATION, False, p(0).Id)
If ProcessHandle = IntPtr.Zero Then Return False
End If
Return True
End Function
Public Function ReadMemory(Of T)(ByVal address As Integer, ByVal length As Integer, ByVal unicodeString As Boolean) As T
Dim buffer() As Byte
If GetType(T) Is GetType(String) Then
If unicodeString Then buffer = New Byte(length * 2 - 1) {} Else buffer = New Byte(length - 1) {}
ElseIf GetType(T) Is GetType(Byte()) Then
buffer = New Byte(length - 1) {}
Else
buffer = New Byte(Marshal.SizeOf(GetType(T)) - 1) {}
End If
If Not UpdateProcessHandle() Then Return Nothing
Dim success As Boolean = ReadProcessMemory(ProcessHandle, New IntPtr(address), buffer, New IntPtr(buffer.Length), IntPtr.Zero)
If Not success Then Return Nothing
If GetType(T) Is GetType(Byte()) Then Return CType(CType(buffer, Object), T)
If GetType(T) Is GetType(String) Then
If unicodeString Then Return CType(CType(Encoding.Unicode.GetString(buffer), Object), T)
Return CType(CType(Encoding.ASCII.GetString(buffer), Object), T)
End If
Dim gcHandle As GCHandle = gcHandle.Alloc(buffer, GCHandleType.Pinned)
Dim returnObject As T
returnObject = CType(Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject, GetType(T)), T)
gcHandle.Free()
Return returnObject
End Function
Private Function GetObjectBytes(ByVal value As Object) As Byte()
If value.GetType() Is GetType(Byte()) Then Return CType(value, Byte())
Dim buffer(Marshal.SizeOf(value) - 1) As Byte
Dim ptr As IntPtr = Marshal.AllocHGlobal(buffer.Length)
Marshal.StructureToPtr(value, ptr, True)
Marshal.Copy(ptr, buffer, 0, buffer.Length)
Marshal.FreeHGlobal(ptr)
Return buffer
End Function
Public Function WriteMemory(ByVal address As Integer, ByVal value As Object) As Boolean
Return WriteMemory(address, value, False)
End Function
Public Function WriteMemory(ByVal address As Integer, ByVal value As Object, ByVal unicode As Boolean) As Boolean
If Not UpdateProcessHandle() Then Return False
Dim buffer() As Byte
If TypeOf value Is String Then
If unicode Then buffer = Encoding.Unicode.GetBytes(value.ToString()) Else buffer = Encoding.ASCII.GetBytes(value.ToString())
Else
buffer = GetObjectBytes(value)
End If
Dim result As Boolean = WriteProcessMemory(ProcessHandle, New IntPtr(address), buffer, New IntPtr(buffer.Length), IntPtr.Zero)
Return result
End Function
End Module |
One of the configuration was able to return a string of crap though, but right now I can't remeber how it was that produced results. I assumed that the address format was wrong, & caused the crap to be returned instead, but as I said, I do not remember the configuration that did it.
I commented out all the above except MemMod module (used for my Game process checks by the timer), & removed the references to BlackMagic... replacing it with ManagedWinapi.dll from GitHub, & tested another function:
Code: |
Imports System.Collections.Generic
imports System.Diagnostics
imports System.Text
Public Class Form1
Private Const find_PATTERN As String = "54 68 69 73 49 73 54 65 73 74 41 00 00 00 00" ' ThisIsTestA
Private Const find_MASK As String = "xxxxxxxxxxx????"
Private WithEvents Timer1 As System.Timers.Timer
Private Shared Function GetMemoryAddressOfString(searchedBytes As Byte(), p As Process) As Integer
'List<int> addrList = new List<int>();
Dim addr As Integer = 0
Dim speed As Integer = 1024 * 64
Dim j As Integer = &H400000
While j < &H7FFFFFFF
Dim mem As ManagedWinapi.ProcessMemoryChunk = New ManagedWinapi.ProcessMemoryChunk(p, j, speed + searchedBytes.Length)
Dim bigMem As Byte() = mem.Read()
For k As Integer = 0 To bigMem.Length - searchedBytes.Length - 1
Dim found As Boolean = True
For l As Integer = 0 To searchedBytes.Length - 1
If bigMem(k + l) <> searchedBytes(l) Then
found = False
Exit For
End If
Next
If found Then
addr = k + j
Exit For
End If
Next
If addr <> 0 Then
'addrList.Add(addr);
'addr = 0;
Exit While
End If
j += speed
End While
'return addrList;
Return addr
End Function
Private Sub Timer1_Elapsed(sender As Object, e As System.Timers.ElapsedEventArgs) Handles Timer1.Elapsed
If MemMod.UpdateProcessHandlePublic() Then ' Check if the game is running
picBrowser1.Image = ImageList1.Images.Item(1)
picBrowser1.Invalidate()
picBrowser1.Refresh()
Else
picBrowser1.Image = ImageList1.Images.Item(0)
picBrowser1.Invalidate()
picBrowser1.Refresh()
End If
End Sub
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Timer1 = New System.Timers.Timer
Timer1.Enabled = True
Timer1.Interval = 1000
Timer1.Start() ' = True
GC.Collect()
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim tmpByte() As Byte = Encoding.ASCII.GetBytes("ThisIsTestA")
Dim p As Process() = Process.GetProcessesByName("plugin-container")
TextBox1.Text = GetMemoryAddressOfString(tmpByte, p(0))
End Sub
End Class |
Just like the other tests, all I seem to get in return is either "T", or "MZ?"... except for the one time I got a string of crap in return.
Can anyone check the code, see where I might have went wrong? I used BmlackMagic's test project as a template, & that's why you see:
Code: | TextBox1.Text += "find_PATTERN: " & bmGame.ReadUInt(dwCodeLocStr + &H16) & vbCrLf
TextBox1.Text += "CURMGR_OFFSET: " & bmGame.ReadUInt(dwCodeLocStr + &H1C) & vbCrLf |
&H16, & &H1C must be something specific to the WoW game (I don't play that crap ), & seems to be the set offset for whatever the test project was targeting, or something like that.
Can someone with experience, & familiar with reading process memory with VS.NET show me what the obvious errors I've overlooked are?
ps: I know the Address/Location is probably the most obvious place to look, & I have tried different ways of setting it. Could just be that I just haven't set it to the right combination of settings yet to see results the way I expect.
Thanks guys.
UPDATE:
The following is what I have so far, & it works for most of what I need in my .NET trainers (Except autoassembler functionality )
Imports:
Code: | Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Text
Imports ManagedWinapi '<===== From GitHub (ManagedWinapi.dll)
Imports System.ComponentModel |
Form:
Code: | Public Class Form1
Private WithEvents Timer1 As System.Timers.Timer
Private WithEvents bgWorker As New BackgroundWorker
Private Shared Function GetAddressOfString(searchedBytes As Byte(), p As Process, start As UInt32) As Integer
Dim addr As Integer = 0
Dim speed As Integer = 1024 * 64
Dim j As UInt32 = start '&H400000
While j < &H7FFFFFFF
Dim mem As ManagedWinapi.ProcessMemoryChunk = New ManagedWinapi.ProcessMemoryChunk(p, j, speed + searchedBytes.Length)
Dim bigMem As Byte() = mem.Read()
For k As Integer = 0 To bigMem.Length - searchedBytes.Length - 1
Dim found As Boolean = True
For l As Integer = 0 To searchedBytes.Length - 1
If bigMem(k + l) <> searchedBytes(l) Then
found = False
Exit For
End If
Next
If found Then
addr = k + j
Exit For
End If
Next
If addr <> 0 Then
Exit While
End If
j += speed
End While
Return addr
End Function
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
bgWorker.WorkerReportsProgress = True
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Button1.Text = "Working..."
Button1.Enabled = False
bgWorker.RunWorkerAsync()
End Sub
Private Sub bgWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bgWorker.DoWork
Dim tmpByte() As Byte = Encoding.ASCII.GetBytes("Hi... I'm a test string. :)")
Dim p As Process() = Process.GetProcessesByName("plugin-container")
Dim tmpLastAddr As String = "&H400000"
Dim tmpAddressStr As String = ""
WorkMessage = Nothing
bgWorker.ReportProgress(0)
Do Until tmpAddressStr = "&H0"
tmpAddressStr = "&H" & Hex(GetAddressOfString(tmpByte, p(0), tmpLastAddr))
If tmpAddressStr = "&H0" Then
WorkMessage = "Test Done."
bgWorker.ReportProgress(0)
Exit Sub
Else
WorkMessage = tmpAddressStr & vbCrLf
bgWorker.ReportProgress(0)
Application.DoEvents()
MemMod.WriteMemory(tmpAddressStr, "Hi... I'm a test string too! :)" + vbNullChar, False)
tmpLastAddr = tmpAddressStr
End If
Loop
WorkMessage = "Test Done."
bgWorker.ReportProgress(0)
WorkMessage = "EnableButton"
bgWorker.ReportProgress(0)
End Sub
Private Sub bgWorker_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles bgWorker.ProgressChanged
If WorkMessage = "EnableButton" then
WorkMessage = ""
Button1.Text = "Cheat Active"
Button1.Enabled = True
Else
If WorkMessage = Nothing Then
TextBox1.Text = ""
Else
TextBox1.Text += WorkMessage
End If
Application.DoEvents()
End If
End Sub
End Class |
Module:
Code: | Option Strict On
Imports System.Runtime.InteropServices
Imports System.Text
Module MemMod
Public WorkMessage As String = Nothing
<DllImport("kernel32.dll")> _
Private Function OpenProcess(ByVal dwDesiredAccess As UInteger, <MarshalAs(UnmanagedType.Bool)> ByVal bInheritHandle As Boolean, ByVal dwProcessId As Integer) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True)> _
Private Function WriteProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByVal lpBuffer As Byte(), ByVal nSize As IntPtr, <Out()> ByRef lpNumberOfBytesWritten As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True)> _
Private Function ReadProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, <Out()> ByVal lpBuffer() As Byte, ByVal dwSize As IntPtr, ByRef lpNumberOfBytesRead As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True)>
Private Function CloseHandle(ByVal hObject As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Private Const PROCESS_VM_WRITE As UInteger = &H20
Private Const PROCESS_VM_READ As UInteger = &H10
Private Const PROCESS_VM_OPERATION As UInteger = &H8
Public TargetProcess As String = "plugin-container"
Private ProcessHandle As IntPtr = IntPtr.Zero
Private LastKnownPID As Integer = -1
Public Function ReadMemory(Of T)(ByVal address As Integer) As T
Return ReadMemory(Of T)(address, 0, False)
End Function
Public Function ReadMemory(ByVal address As Integer, ByVal length As Integer) As Byte()
Return ReadMemory(Of Byte())(address, length, False)
End Function
Private Function ProcessIDExists(ByVal pID As Integer) As Boolean
For Each p As Process In Process.GetProcessesByName(TargetProcess)
If p.Id = pID Then Return True
Next
Return False
End Function
Public Function UpdateProcessHandlePublic() As Boolean
If LastKnownPID = -1 OrElse Not ProcessIDExists(LastKnownPID) Then
If ProcessHandle <> IntPtr.Zero Then CloseHandle(ProcessHandle)
Dim p() As Process = Process.GetProcessesByName(TargetProcess)
If p.Length = 0 Then Return False
LastKnownPID = p(0).Id
ProcessHandle = OpenProcess(PROCESS_VM_READ Or PROCESS_VM_WRITE Or PROCESS_VM_OPERATION, False, p(0).Id)
If ProcessHandle = IntPtr.Zero Then Return False
End If
Return True
End Function
Private Function UpdateProcessHandle() As Boolean
If LastKnownPID = -1 OrElse Not ProcessIDExists(LastKnownPID) Then
If ProcessHandle <> IntPtr.Zero Then CloseHandle(ProcessHandle)
Dim p() As Process = Process.GetProcessesByName(TargetProcess)
If p.Length = 0 Then Return False
LastKnownPID = p(0).Id
ProcessHandle = OpenProcess(PROCESS_VM_READ Or PROCESS_VM_WRITE Or PROCESS_VM_OPERATION, False, p(0).Id)
If ProcessHandle = IntPtr.Zero Then Return False
End If
Return True
End Function
Public Function ReadMemory(Of T)(ByVal address As Integer, ByVal length As Integer, ByVal unicodeString As Boolean) As T
Dim buffer() As Byte
If GetType(T) Is GetType(String) Then
If unicodeString Then buffer = New Byte(length * 2 - 1) {} Else buffer = New Byte(length - 1) {}
ElseIf GetType(T) Is GetType(Byte()) Then
buffer = New Byte(length - 1) {}
Else
buffer = New Byte(Marshal.SizeOf(GetType(T)) - 1) {}
End If
If Not UpdateProcessHandle() Then Return Nothing
Dim success As Boolean = ReadProcessMemory(ProcessHandle, New IntPtr(address), buffer, New IntPtr(buffer.Length), IntPtr.Zero)
If Not success Then Return Nothing
If GetType(T) Is GetType(Byte()) Then Return CType(CType(buffer, Object), T)
If GetType(T) Is GetType(String) Then
If unicodeString Then Return CType(CType(Encoding.Unicode.GetString(buffer), Object), T)
Return CType(CType(Encoding.ASCII.GetString(buffer), Object), T)
End If
Dim gcHandle As GCHandle = gcHandle.Alloc(buffer, GCHandleType.Pinned)
Dim returnObject As T
returnObject = CType(Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject, GetType(T)), T)
gcHandle.Free()
Return returnObject
End Function
Private Function GetObjectBytes(ByVal value As Object) As Byte()
If value.GetType() Is GetType(Byte()) Then Return CType(value, Byte())
Dim buffer(Marshal.SizeOf(value) - 1) As Byte
Dim ptr As IntPtr = Marshal.AllocHGlobal(buffer.Length)
Marshal.StructureToPtr(value, ptr, True)
Marshal.Copy(ptr, buffer, 0, buffer.Length)
Marshal.FreeHGlobal(ptr)
Return buffer
End Function
Public Function WriteMemory(ByVal address As Integer, ByVal value As Object) As Boolean
Return WriteMemory(address, value, False)
End Function
Public Function WriteMemory(ByVal address As Integer, ByVal value As Object, ByVal unicode As Boolean) As Boolean
If Not UpdateProcessHandle() Then Return False
Dim buffer() As Byte
If TypeOf value Is String Then
If unicode Then buffer = Encoding.Unicode.GetBytes(value.ToString()) Else buffer = Encoding.ASCII.GetBytes(value.ToString())
Else
buffer = GetObjectBytes(value)
End If
Dim result As Boolean = WriteProcessMemory(ProcessHandle, New IntPtr(address), buffer, New IntPtr(buffer.Length), IntPtr.Zero)
Return result
End Function
End Module |
It's not as fast as BlackMagic-1.1, but it does the job!
ps: Put together with bits of code from across the net. Will add credits when I remember where I got all the bits from.
_________________
Hitler... When all else fails, you'll be in the right state of mind. Jesus Saves. |
|