Need help converting 'trainer' to PureBasic

Just starting out? Need help? Post your questions and find answers here.
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Need help converting 'trainer' to PureBasic

Post by PB »

This is a small app that opens Calc.exe and then replaces the 'Backspace'
button text with 'VB-O-Matic' (like a trainer). I've tried converting this to
PureBasic without success. Is anyone able to please help? (The VB code
works fine on Win 2000, so I know the routine definitely works).

Code: Select all

Private Type OSVERSIONINFO
    dwOSVersionInfoSize As Long
    dwMajorVersion As Long
    dwMinorVersion As Long
    dwBuildNumber As Long
    dwPlatformId As Long
    szCSDVersion As String * 128
End Type

Private Type MEMORY_BASIC_INFORMATION ' 28 bytes
    BaseAddress As Long
    AllocationBase As Long
    AllocationProtect As Long
    RegionSize As Long
    State As Long
    Protect As Long
    lType As Long
End Type

Private Type SYSTEM_INFO ' 36 Bytes
    dwOemID As Long
    dwPageSize As Long
    lpMinimumApplicationAddress As Long
    lpMaximumApplicationAddress As Long
    dwActiveProcessorMask As Long
    dwNumberOrfProcessors As Long
    dwProcessorType As Long
    dwAllocationGranularity As Long
    wProcessorLevel As Integer
    wProcessorRevision As Integer
End Type

Private Declare Function GetVersionEx Lib "kernel32" Alias "GetVersionExA" (LpVersionInformation As OSVERSIONINFO) As Long
Private Declare Function VirtualQueryEx& Lib "kernel32" (ByVal hProcess As Long, lpAddress As Any, lpBuffer As MEMORY_BASIC_INFORMATION, ByVal dwLength As Long)
Private Declare Sub GetSystemInfo Lib "kernel32" (lpSystemInfo As SYSTEM_INFO)
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal blnheritHandle As Long, ByVal dwAppProcessId As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long

Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Long, ByVal lpWindowName As Long) As Long
Private Declare Function GetParent Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function GetWindow Lib "user32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Const GW_HWNDNEXT = 2

Private Declare Function InvalidateRect Lib "user32" (ByVal hWnd As Long, ByVal lpRect As Long, ByVal bErase As Long) As Long
Const PROCESS_VM_READ = (&H10)
Const PROCESS_VM_WRITE = (&H20)
Const PROCESS_VM_OPERATION = (&H8)
Const PROCESS_QUERY_INFORMATION = (&H400)
Const PROCESS_READ_WRITE_QUERY = PROCESS_VM_READ + PROCESS_VM_WRITE + PROCESS_VM_OPERATION + PROCESS_QUERY_INFORMATION

Const MEM_PRIVATE& = &H20000
Const MEM_COMMIT& = &H1000

'Add 3 labels, 3 textboxes and 1 commandbutton on form

Private Sub Command1_Click()
    Dim pid As Long, hProcess As Long, hWin As Long
    Dim lpMem As Long, ret As Long, lLenMBI As Long
    Dim lWritten As Long, CalcAddress As Long, lPos As Long
    Dim sBuffer As String
    Dim sSearchString As String, sReplaceString As String
    Dim si As SYSTEM_INFO
    Dim mbi As MEMORY_BASIC_INFORMATION
    sSearchString = Text2
    sReplaceString = Text3 & Chr(0)
    If IsWindowsNT Then 'NT store strings in RAM in UNICODE
       sSearchString = StrConv(sSearchString, vbUnicode)
       sReplaceString = StrConv(sReplaceString, vbUnicode)
    End If
    pid = Shell(Text1) 'launch application (calc.exe in this sample)
    hWin = InstanceToWnd(pid) 'get handle of launched window - only to repaint it after changes
'Open process with required access
    hProcess = OpenProcess(PROCESS_READ_WRITE_QUERY, False, pid)
    lLenMBI = Len(mbi)
'Determine applications memory addresses range
    Call GetSystemInfo(si)
    lpMem = si.lpMinimumApplicationAddress
'Scan memory
    Do While lpMem < si.lpMaximumApplicationAddress
        mbi.RegionSize = 0
        ret = VirtualQueryEx(hProcess, ByVal lpMem, mbi, lLenMBI)
        If ret = lLenMBI Then
            If ((mbi.lType = MEM_PRIVATE) And (mbi.State = MEM_COMMIT)) Then ' this block is In use by this process
                If mbi.RegionSize > 0 Then
                   sBuffer = String(mbi.RegionSize, 0)
'Read region into string
                   ReadProcessMemory hProcess, ByVal mbi.BaseAddress, ByVal sBuffer, mbi.RegionSize, lWritten
'Check if region contain search string
                   lPos = InStr(1, sBuffer, sSearchString, vbTextCompare)
                   If lPos Then
                      CalcAddress = mbi.BaseAddress + lPos
                      Me.Show
                      ret = MsgBox("Search string was found at address " & CalcAddress & "." & vbCrLf & "Do you want to replace it?", vbInformation + vbYesNo, "VB-O-Matic")
                      If ret = vbYes Then
'Replace string in virtual memory
                         Call WriteProcessMemory(hProcess, ByVal CalcAddress - 1, ByVal sReplaceString, Len(sReplaceString), lWritten)
'Redraw window
                         InvalidateRect hWin, 0, 1
                      End If
                      Exit Do
                   End If
                End If
            End If
'Increase base address for next searching cicle. Last address may overhead max Long value (Windows use 2GB memory, which is near max long value), so add Error checking
            On Error GoTo Finished
            lpMem = mbi.BaseAddress + mbi.RegionSize
            On Error GoTo 0
        Else
            Exit Do
        End If
    Loop
Finished:
   CloseHandle hProcess
End Sub

Private Sub Form_Load()
   Caption = "VB-O-Matic"
   Label1 = "Start application:"
   Label2 = "String to find:"
   Label3 = "Replace with:"
   Text1 = "Calc.exe"
   Text2 = "Backspace"
   Text3 = "VB-O-Matic"
   Command1.Caption = "&Launch It!"
End Sub

Private Function InstanceToWnd(ByVal target_pid As Long) As Long
  Dim test_hwnd As Long
  Dim test_pid As Long
  Dim test_thread_id As Long
  test_hwnd = FindWindow(ByVal 0&, ByVal 0&)
  Do While test_hwnd <> 0
   If GetParent(test_hwnd) = 0 Then
      test_thread_id = GetWindowThreadProcessId(test_hwnd, test_pid)
      If test_pid = target_pid Then
         InstanceToWnd = test_hwnd
         Exit Do
      End If
   End If
   test_hwnd = GetWindow(test_hwnd, GW_HWNDNEXT)
  Loop
End Function

Private Function IsWindowsNT() As Boolean
   Dim verinfo As OSVERSIONINFO
   verinfo.dwOSVersionInfoSize = Len(verinfo)
   If (GetVersionEx(verinfo)) = 0 Then Exit Function
   If verinfo.dwPlatformId = 2 Then IsWindowsNT = True
End Function
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Need help converting 'trainer' to PureBasic

Post by PB »

Okay, I've converted the code pretty much to PureBasic, but it doesn't work.
Anyone got any ideas why? I'm testing it on Windows 2000 and the Visual
Basic version does work on there, so it's not like the code can't access the
Calc's process memory or anything. In particular, see the line that I've
marked with *** that seems to be causing the failure.

Again, this code is for Windows 2000 only. Once we get it working, we can
start adapting it for all other versions of Windows.

Code: Select all

#PROCESS_VM_READ = $10
#PROCESS_VM_WRITE = $20
#PROCESS_VM_OPERATION = $8
#PROCESS_QUERY_INFORMATION = $400
#PROCESS_READ_WRITE_QUERY = #PROCESS_VM_READ|#PROCESS_VM_WRITE|#PROCESS_VM_OPERATION|#PROCESS_QUERY_INFORMATION

#MEM_PRIVATE = $20000
#MEM_COMMIT = $1000

Global ProcessID.l

Procedure.l RunProgramEx(Filename.s,Parameter.s,Directory.s,ShowFlag.l)
  Info.STARTUPINFO : Info\cb=SizeOf(STARTUPINFO) : Info\dwFlags=1
  Info\wShowWindow=ShowFlag : ProcessInfo.PROCESS_INFORMATION
  ProcessPriority=#NORMAL_PRIORITY_CLASS
  If CreateProcess_(@Filename,@Parameter,0,0,0,ProcessPriority,0,@Directory,@Info,@ProcessInfo)
    ProcessID.l=ProcessInfo\dwProcessId
    Repeat
      win=FindWindow_(0,0)
      While win<>0
        GetWindowThreadProcessId_(win,@pid.l)
        If pid=ProcessID : WinHandle=win : Break : EndIf
        win=GetWindow_(win,#GW_HWNDNEXT)
      Wend
    Until WinHandle
  EndIf
  ProcedureReturn WinHandle
EndProcedure

sSearchString.s = "Backspace"
sReplaceString.s = "PB-O-Matic"

;If IsWindowsNT ;NT store strings in RAM in UNICODE
;   sSearchString = StrConv(sSearchString, vbUnicode)
;   sReplaceString = StrConv(sReplaceString, vbUnicode)
;EndIf

si.SYSTEM_INFO
mbi.MEMORY_BASIC_INFORMATION

hWin=RunProgramEx("c:\winnt\system32\calc.exe","","c:\winnt\system32\",#SW_SHOW)
hProcess = OpenProcess_(#PROCESS_READ_WRITE_QUERY,#FALSE,ProcessID)
lLenMBI = SizeOf(mbi)
GetSystemInfo_(si)
lpMem = si\lpMinimumApplicationAddress
While lpMem < si\lpMaximumApplicationAddress
    mbi\RegionSize = 0
    ; VB version of next line: ret = VirtualQueryEx(hProcess, ByVal lpMem, mbi, lLenMBI)
    ; In the VB version, if you remove "ByVal" then it doesn't work in VB either...
    ret = VirtualQueryEx_(hProcess, lpMem, mbi, lLenMBI) ; *** ALWAYS RETURNS 0 ***
    If ret = lLenMBI
        If ((mbi\lType = #MEM_PRIVATE) And (mbi\State = #MEM_COMMIT))
            If mbi\RegionSize > 0
               sBuffer.s = Space(mbi\RegionSize)
               ReadProcessMemory_(hProcess, mbi\BaseAddress, sBuffer, mbi\RegionSize, lWritten)
               lPos = FindString(sBuffer, sSearchString, 1)
               If lPos
                  CalcAddress = mbi\BaseAddress + lPos
                  WriteProcessMemory_(hProcess, CalcAddress - 1, sReplaceString, Len(sReplaceString), lWritten)
                  InvalidateRect_(hWin, 0, 1)
                  Break
               EndIf
            EndIf
        EndIf
        OnErrorGoto(?Finished)
        lpMem = mbi\BaseAddress + mbi\RegionSize
        OnErrorResume()
    Else
        Break
    EndIf
Wend
Finished:
CloseHandle_(hProcess)
gkaizer
User
User
Posts: 40
Joined: Fri Oct 31, 2003 2:57 pm
Location: Italy

virtualqueryex second parameter

Post by gkaizer »

; VB version of next line: ret = VirtualQueryEx(hProcess, ByVal lpMem, mbi, lLenMBI)
; In the VB version, if you remove "ByVal" then it doesn't work in VB either...
Yes, in VB without ByVal won't work, because the second parameter of VirtualQueryEx is LPCVOID, a const pointer to void, i.e. you tell that this pointer can't be changed by the routine, and in VB can be done forcing it to be passed by value, instead of by reference (as normally is without any modifier).

What I don't know is how to do it in PB, because if you pass a pointer just like you do, it certainly will fail....
have you tried

Code: Select all

#lpMemConst = lpMem
ret = VirtualQueryEx_(hProcess, #lpMemConst, mbi, lLenMBI)
Let me know
--:: Gkaizer ::--
Using PB 3.92 on WinXP SP2 and 3.91 on Mandrake Linux 10.0
bcgreen
User
User
Posts: 33
Joined: Sun Nov 02, 2003 7:33 am
Location: Pullman, WA

Post by bcgreen »

What is a "trainer?"

Bryan
Mr Tickle
User
User
Posts: 25
Joined: Mon Oct 13, 2003 10:33 pm
Location: England, Derbyshire.

Post by Mr Tickle »

bcgreen wrote:What is a "trainer?"

Bryan
It's a running shoe j/k :lol:
Mr Tickle.
Athlon xp 2500+, Gigabyte KT400-FSB333, Gf3 Ti200, SB Live 5.1
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: virtualqueryex second parameter

Post by PB »

> #lpMemConst = lpMem
> ret = VirtualQueryEx_(hProcess, #lpMemConst, mbi, lLenMBI)

That doesn't work, because you get an error about trying to create a
constant from a variable. I tried replacing lpMem with its actual value
(65536) but that didn't work either.

> what is a "trainer"?

It's an app that lets you change the process memory of another app.
For example: if a game gives you 3 lives, the value 3 is stored in the
game's memory somewhere. A trainer lets you change this 3 to another
value, like 255, to give you 255 lives. They're called "trainers" because
you get to play the game with a cheating edge, to "train" yourself in how
the game plays.

In the Visual Basic example here, the Windows Calculator is launched
and then its "Backspace" key is changed to "VB-O-Matic", much like
the 3 lives / 255 lives concept. That's what I'm trying to convert to
PureBasic.
Paul Dwyer
User
User
Posts: 44
Joined: Wed Nov 05, 2003 4:34 am
Location: Tokyo, Japan

Post by Paul Dwyer »

:?:

How do you pass ByVal in PB????

Is there a workaround? That will break a lot of code converted from other basics
Paul Dwyer
Network Engineer
Aussie in Tokyo
Fred
Administrator
Administrator
Posts: 16617
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

Just use '@' to pass by val

Code: Select all

Procedure Hello(*ReturnAddress)
  PokeL(*ReturnAddress, 10)
EndProcedure

Hello(@Result)

Debug Result
gkaizer
User
User
Posts: 40
Joined: Fri Oct 31, 2003 2:57 pm
Location: Italy

Post by gkaizer »

nope :lol: , what you're doing is -- hello receives a pointer and correctly you pass a pointer to variable Result...
where is the byval passing here?

what I think Paul wanted to ask is connected to my post, where I explained why you cannot cancel "ByVal" in VB in a call to VirtualQueryEx.

It seems that PB can't handle API whose parameters are declared constant pointer to void (and who knows to other types).

another example could be using memcpy in PB, the second argument is const void * or else MoveMemory, if someone has time to do a little test and post it back here would make clear if it's a limitation of PB or ours :o
that's my 2 cent's :
--:: Gkaizer ::--
Using PB 3.92 on WinXP SP2 and 3.91 on Mandrake Linux 10.0
Paul Dwyer
User
User
Posts: 44
Joined: Wed Nov 05, 2003 4:34 am
Location: Tokyo, Japan

Post by Paul Dwyer »

Now I'm confused... 8O

Some Windows API functions (or Dlls) require you to send constants by value and not a pointer to that value. If you don't have the constants declared then you can just pass the value "ByVal 5" or something.

How would you handle this in PB?

Thanks
Paul Dwyer
Network Engineer
Aussie in Tokyo
Fred
Administrator
Administrator
Posts: 16617
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

Any example ? I don't understand really this 'By Val' stuff. PB can handle the API call exactly like C/C++ does, so I don't see any case where you will be stuck in PB... ;)
gkaizer
User
User
Posts: 40
Joined: Fri Oct 31, 2003 2:57 pm
Location: Italy

Post by gkaizer »

ok then, I'll try and see with some other API's that have that kind of argument and let you know what I find.

byz :lol:
--:: Gkaizer ::--
Using PB 3.92 on WinXP SP2 and 3.91 on Mandrake Linux 10.0
Paul Dwyer
User
User
Posts: 44
Joined: Wed Nov 05, 2003 4:34 am
Location: Tokyo, Japan

Post by Paul Dwyer »

Fred,

My understanding is that C passes parameters by value and not by reference which is why you pass a pointer. You pass the value of the 32bit address. You can pass actual values on the stack too. One very common one is a constant. The windows API is full of thousands of such constants to be passed as parameters as I'm sure you are aware.

In PB, when you pass a constant abviously you can't pass a pointer because a constant is not a variable it's a compiler equate. So can I just pass a constant as is and the compiler will know to pass the value and not attempt to send a pointer to... ?

Or in PB is everything passed by value rather than by reference so you have to pass you're own pointers?
Paul Dwyer
Network Engineer
Aussie in Tokyo
dontmailme
Enthusiast
Enthusiast
Posts: 537
Joined: Wed Oct 29, 2003 10:35 am

Post by dontmailme »

Paul Dwyer wrote:Now I'm confused... 8O

Some Windows API functions (or Dlls) require you to send constants by value and not a pointer to that value. If you don't have the constants declared then you can just pass the value "ByVal 5" or something.

How would you handle this in PB?

Thanks
You think you're confused 8O 8O 8O 8O !#$%& !!!

It's hard enough trying to use the API's, this thread is harder to understand than Algebra 8O
Paid up PB User !
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

In PB, everything is passed by value, with these exceptions:

Structures are passed by reference
Strings are passed by reference
If you add the '@' before anything, it is also passed by reference.

I never had any trouble with API calls, i really don't understand the problem.

Timo
quidquid Latine dictum sit altum videtur
Post Reply