Author Topic: Anyone good with messing with system memory?  (Read 2801 times)

0 Members and 1 Guest are viewing this topic.

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Anyone good with messing with system memory?
« on: October 13, 2020, 02:37:11 am »
If anyone on here knows a good amount about messing with system memory then please lend me a hand. I'm writing a program that reads a string found in memory and then is supposed to write a replacement string to that memory location. I have no problem finding the first location referencing the string and the WriteProcessMemory function does not fail (in the sense that it actually claims to have written the bytes to memory) but I never see anything change because I'm only finding the first reference rather than the one I need. Here is my code so far which uses the PeekPoke library attached. Note: The program can run out of memory depending on the program you are targeting. I've found that notepad++ works fine (in the sense that it doesn't run out of memory finding the first occurence of the string in its memory) so give that a try if you have it installed.

Code: QB64: [Select]
  1.  
  2. CONST PROCESS_VM_READ = &H0010
  3. CONST PROCESS_QUERY_INFORMATION = &H0400
  4. CONST PROCESS_VM_WRITE = &H0020
  5. CONST PROCESS_VM_OPERATION = &H0008
  6.  
  7. CONST TRUE = -1
  8. CONST FALSE = 0
  9.  
  10. TYPE SYSTEM_INFO
  11.     wProcessorArchitecture AS INTEGER
  12.     wReserved AS INTEGER
  13.     dwPageSize AS LONG
  14.     lpMinimumApplicationAddress AS _OFFSET
  15.     lpMaximumApplicationAddress AS _OFFSET
  16.     dwActiveProcessorMask AS _OFFSET
  17.     dwNumberOfProcessors AS LONG
  18.     dwProcessorType AS LONG
  19.     dwAllocationGranularity AS LONG
  20.     wProcessorLevel AS INTEGER
  21.     wProcessorRevision AS INTEGER
  22.  
  23. $IF 64BIT THEN
  24.     TYPE MEMORY_BASIC_INFORMATION
  25.         BaseAddress AS _UNSIGNED _INTEGER64
  26.         AllocationBase AS _UNSIGNED _INTEGER64
  27.         AllocationProtect AS LONG
  28.         alignment1 AS LONG
  29.         RegionSize AS _UNSIGNED _INTEGER64
  30.         State AS LONG
  31.         Protect AS LONG
  32.         Type AS LONG
  33.         alignment2 AS LONG
  34.     END TYPE
  35.     TYPE MEMORY_BASIC_INFORMATION
  36.     BaseAddress AS LONG
  37.     AllocationBase AS LONG
  38.     AllocationProtect AS LONG
  39.     RegionSize AS LONG
  40.     State AS LONG
  41.     Protect AS LONG
  42.     Type AS LONG
  43.     END TYPE
  44.  
  45.     FUNCTION OpenProcess& (BYVAL dwDesiredAccess AS LONG, BYVAL bInheritHandle AS _BYTE, BYVAL dwProcessID AS LONG)
  46.     SUB GetSystemInfo (BYVAL lpSystemInfo AS _OFFSET)
  47.     FUNCTION VirtualQueryEx& (BYVAL hProcess AS LONG, BYVAL lpAddress AS _OFFSET, BYVAL lpBuffer AS _OFFSET, BYVAL dwLength AS LONG)
  48.     FUNCTION ReadProcessMemory%% (BYVAL hProcess AS LONG, BYVAL lpBaseAddress AS _OFFSET, BYVAL lpBuffer AS _OFFSET, BYVAL nSize AS LONG, BYVAL lpNumberOfBytesRead AS _OFFSET)
  49.     FUNCTION WriteProcessMemory%% (BYVAL hProcess AS LONG, BYVAL lpBaseAddress AS _OFFSET, BYVAL lpBuffer AS _OFFSET, BYVAL nSize AS LONG, BYVAL lpNumberOfBytesWritten AS _OFFSET)
  50.     FUNCTION GetCurrentProcessId& ()
  51.     FUNCTION GetLastError& ()
  52.  
  53.     FUNCTION peekb~%% (BYVAL p AS _UNSIGNED _OFFSET) 'Byte
  54.     'SUB pokeb (BYVAL p AS _UNSIGNED _OFFSET, BYVAL n AS _UNSIGNED _BYTE)
  55.  
  56.     FUNCTION strlen& (BYVAL ptr AS _UNSIGNED _OFFSET)
  57.  
  58. 'RelaunchAsAdmin 'if uncommented, will make the program launch as administrator (if not already running as administrator)
  59.  
  60. DIM someData AS STRING
  61. someData = "Basic options" + CHR$(0) 'Whatever string you are searching for
  62.  
  63. DIM pid AS LONG
  64. 'pid = GetCurrentProcessId
  65. pid = 14264 'Whatever PID for program you want to change
  66.  
  67. ret = GetAddressOfData(pid, someData, LEN(someData), "hello!") 'replace "hello!" with whatever you want to attempt
  68.  
  69. IF ret THEN
  70.     PRINT "Found at:"; ret
  71.     PRINT PointerToString(ret)
  72.     PRINT "Length of pointer string:"; PointerLen(ret)
  73.     PRINT "Not found"
  74.  
  75.  
  76. FUNCTION GetAddressOfData%& (pid AS LONG, pdata AS STRING, plen AS LONG, change AS STRING)
  77.     DIM process AS LONG
  78.     process = OpenProcess(PROCESS_VM_READ OR PROCESS_QUERY_INFORMATION OR PROCESS_VM_WRITE OR PROCESS_VM_OPERATION, FALSE, pid)
  79.     IF process THEN
  80.         DIM si AS SYSTEM_INFO
  81.         GetSystemInfo (_OFFSET(si))
  82.         DIM info AS MEMORY_BASIC_INFORMATION
  83.         DIM p AS _OFFSET
  84.         DO
  85.             IF VirtualQueryEx(process, p, _OFFSET(info), LEN(info)) = LEN(info) THEN
  86.                 p = info.BaseAddress
  87.                 REDIM chunk(info.RegionSize) AS STRING * 32
  88.                 DIM bytesRead AS _UNSIGNED _INTEGER64
  89.                 DIM processmemory AS LONG
  90.                 IF ReadProcessMemory(process, p, _OFFSET(chunk(0)), info.RegionSize, _OFFSET(bytesRead)) THEN
  91.                     PRINT "Bytes Read:"; bytesRead
  92.                     DIM i AS LONG
  93.                     FOR i = 0 TO bytesRead - plen
  94.                         IF pdata = PointerToString(p + i) THEN
  95.                             GetAddressOfData = p + i
  96.                             DIM writes AS LONG
  97.                             DIM bytesWritten AS LONG
  98.                             writes = WriteProcessMemory(process, p + i, _OFFSET(change), LEN(change), _OFFSET(bytesWritten))
  99.                             PRINT bytesWritten
  100.                             EXIT FUNCTION
  101.                         ELSE
  102.                             chunk(i) = "" 'I don't know if this helps. I just figured it "might" free memory?
  103.                         END IF
  104.                     NEXT
  105.                 END IF
  106.                 p = p + info.RegionSize
  107.             ELSE
  108.                 PRINT "0X" + HEX$(GetLastError)
  109.                 EXIT FUNCTION
  110.             END IF
  111.         LOOP WHILE p < si.lpMaximumApplicationAddress
  112.     END IF
  113.  
  114. FUNCTION PointerToString$ (value AS _OFFSET)
  115.     DIM offtostring AS STRING
  116.     FOR x = 0 TO PointerLen(value)
  117.         offtostring = offtostring + CHR$(peekb(value + x))
  118.     NEXT
  119.     PointerToString = offtostring
  120.  
  121. FUNCTION PointerLen& (value AS _OFFSET)
  122.     PointerLen = strlen(value)
  123.  
  124. SUB RelaunchAsAdmin
  125.     DIM adminCheck AS LONG
  126.     adminCheck = _SHELLHIDE(">nul 2>&1 " + CHR$(34) + "%SYSTEMROOT%\system32\cacls.exe" + CHR$(34) + " " + CHR$(34) + "%SYSTEMROOT%\system32\config\system" + CHR$(34))
  127.     IF adminCheck = 5 THEN 'Not running as administrator
  128.         SHELL _HIDE _DONTWAIT "PowerShell Start-Process " + "'" + CHR$(34) + COMMAND$(0) + CHR$(34) + "'" + " -Verb runAs"
  129.         SYSTEM
  130.     ELSE
  131.         EXIT SUB
  132.     END IF
« Last Edit: October 13, 2020, 02:40:49 am by SpriggsySpriggs »
Shuwatch!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Anyone good with messing with system memory?
« Reply #1 on: October 14, 2020, 12:44:34 am »
Never mind! Got it figured out! Going to eventually post a tutorial on making trainers in QB64! Painstaking process, but fun!

Code requires use of threadstack.exe which I have conveniently attached. (not tested in 32 bit compilations as of yet)

Current source (for Hitman Blood Money (retail) Infinite Ammo hack):
Code: QB64: [Select]
  1.  
  2. $IF 64BIT THEN
  3.     TYPE PROCESSENTRY32
  4.         dwSize AS LONG
  5.         cntUsage AS LONG
  6.         th32ProcessID AS _INTEGER64
  7.         th32DefaultHeapID AS _UNSIGNED _INTEGER64
  8.         th32ModuleID AS LONG
  9.         cntThreads AS LONG
  10.         th32ParentProcessID AS LONG
  11.         pcPriClassBase AS LONG
  12.         dwFlags AS LONG
  13.         szExeFile AS STRING * 260
  14.     END TYPE
  15.     TYPE PROCESSENTRY32
  16.     dwSize AS LONG
  17.     cntUsage AS LONG
  18.     th32ProcessID AS LONG
  19.     th32DefaultHeapID AS _UNSIGNED LONG
  20.     th32ModuleID AS LONG
  21.     cntThreads AS LONG
  22.     th32ParentProcessID AS LONG
  23.     pcPriClassBase AS LONG
  24.     dwFlags AS LONG
  25.     szExeFile AS STRING * 260
  26.     END TYPE
  27.  
  28. CONST PROCESS_VM_READ = &H0010
  29. CONST PROCESS_QUERY_INFORMATION = &H0400
  30. CONST PROCESS_VM_WRITE = &H0020
  31. CONST PROCESS_VM_OPERATION = &H0008
  32.  
  33. CONST TRUE = -1
  34. CONST FALSE = 0
  35.  
  36. CONST TH32CS_INHERIT = &H80000000
  37. CONST TH32CS_SNAPHEAPLIST = &H00000001
  38. CONST TH32CS_SNAPMODULE = &H00000008
  39. CONST TH32CS_SNAPMODULE32 = &H00000010
  40. CONST TH32CS_SNAPPROCESS = &H00000002
  41. CONST TH32CS_SNAPTHREAD = &H00000004
  42. CONST TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST OR TH32CS_SNAPMODULE OR TH32CS_SNAPPROCESS OR TH32CS_SNAPTHREAD
  43.  
  44. CONST STANDARD_RIGHTS_REQUIRED = &H000F0000
  45. CONST SYNCHRONIZE = &H00100000
  46. CONST PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED OR SYNCHRONIZE OR &HFFFF
  47.  
  48.     FUNCTION CreateToolhelp32Snapshot& (BYVAL dwFlags AS LONG, BYVAL th32ProcessID AS LONG)
  49.     FUNCTION Process32First%% (BYVAL hSnapshot AS LONG, BYVAL lppe AS _OFFSET)
  50.     FUNCTION Process32Next%% (BYVAL hSnapshot AS LONG, BYVAL lppe AS _OFFSET)
  51.     FUNCTION OpenProcess& (BYVAL dwDesiredAccess AS LONG, BYVAL bInheritHandle AS _BYTE, BYVAL dwProcessId AS LONG)
  52.     FUNCTION ReadProcessMemory%% (BYVAL hProcess AS LONG, BYVAL lpBaseAddress AS _OFFSET, BYVAL lpBuffer AS _OFFSET, BYVAL nSize AS LONG, BYVAL lpNumberOfBytesRead AS _OFFSET)
  53.     FUNCTION WriteProcessMemory%% (BYVAL hProcess AS LONG, BYVAL lpBaseAddress AS _OFFSET, BYVAL lpBuffer AS _OFFSET, BYVAL nSize AS LONG, BYVAL lpNumberOfBytesWritten AS _OFFSET)
  54.     FUNCTION CloseHandle%% (BYVAL hObject AS LONG)
  55.  
  56.     FUNCTION GetKeyState% (BYVAL nVirtKey AS LONG) 'reads Windows key presses independently
  57.     FUNCTION MessageBeep%% (BYVAL uType AS _UNSIGNED INTEGER)
  58.  
  59. DIM hProcessSnap AS LONG
  60. DIM SHARED hProcess AS LONG
  61. DIM pe32 AS PROCESSENTRY32
  62.  
  63. DIM SHARED doInfAmmo AS _BYTE
  64. DIM ammolevel AS INTEGER
  65. doInfAmmo = FALSE
  66. DIM SHARED InfiniteAmmoAddress AS _OFFSET
  67. DIM SHARED startaddress AS _OFFSET
  68. startaddress = &H00000DD8
  69. DIM SHARED ammo_offsets(7) AS LONG
  70.  
  71. ammo_offsets(1) = &HB4
  72. ammo_offsets(2) = &H10
  73. ammo_offsets(3) = &H4
  74. ammo_offsets(4) = &H10
  75. ammo_offsets(5) = &H24
  76. ammo_offsets(6) = &HB0
  77. ammo_offsets(7) = &H94
  78.  
  79. hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
  80. pe32.dwSize = LEN(pe32)
  81.  
  82. IF Process32First(hProcessSnap, _OFFSET(pe32)) THEN
  83.     WHILE Process32Next(hProcessSnap, _OFFSET(pe32))
  84.         IF _STRCMP(LEFT$(pe32.szExeFile, INSTR(pe32.szExeFile, ".exe" + CHR$(0)) + 3), "HitmanBloodMoney.exe") = 0 THEN
  85.             hProcess = OpenProcess(PROCESS_VM_READ OR PROCESS_QUERY_INFORMATION OR PROCESS_VM_WRITE OR PROCESS_VM_OPERATION, FALSE, pe32.th32ProcessID)
  86.             DIM SHARED threadaddress AS LONG
  87.             threadaddress = GetThread0Address(pe32.th32ProcessID)
  88.             InfiniteAmmoAddress = threadaddress - startaddress
  89.  
  90.             DIM memo AS _BYTE
  91.             DIM result AS _UNSIGNED LONG
  92.             memo = ReadProcessMemory(hProcess, InfiniteAmmoAddress, _OFFSET(result), 4, 0)
  93.             PRINT HEX$(result)
  94.             InfiniteAmmoAddress = result
  95.             DIM x AS INTEGER
  96.             FOR x = 1 TO 6
  97.                 result = 0
  98.                 memo = ReadProcessMemory(hProcess, InfiniteAmmoAddress + ammo_offsets(x), _OFFSET(result), 4, 0)
  99.                 InfiniteAmmoAddress = result
  100.             NEXT
  101.             InfiniteAmmoAddress = InfiniteAmmoAddress + ammo_offsets(7)
  102.             EXIT WHILE
  103.         END IF
  104.     WEND
  105.     DIM closeh AS LONG
  106.     closeh = CloseHandle(hProcessSnap)
  107.     IF hProcess = 0 THEN
  108.         PRINT "Hitman Blood Money not found"
  109.     ELSE
  110.         TrainerScreen
  111.         WHILE INKEY$ <> CHR$(27)
  112.             IF GetKeyState(113) < 0 THEN
  113.                 doInfAmmo = NOT doInfAmmo
  114.                 DIM procmemory AS _BYTE
  115.                 procmemory = ReadProcessMemory(hProcess, InfiniteAmmoAddress, _OFFSET(ammolevel), 4, 0)
  116.                 TrainerScreen
  117.                 BEEP
  118.             END IF
  119.             IF doInfAmmo THEN
  120.                 ammolevel = 999
  121.                 procmemory = WriteProcessMemory(hProcess, InfiniteAmmoAddress, _OFFSET(ammolevel), 4, 0)
  122.             END IF
  123.         WEND
  124.         closeh = CloseHandle(hProcess)
  125.     END IF
  126.  
  127. SUB TrainerScreen
  128.     CLS
  129.     PRINT HEX$(InfiniteAmmoAddress)
  130.     IF doInfAmmo THEN PRINT "[F2] - INFINITE AMMO [ENABLED]" ELSE PRINT "[F2] - INFINITE AMMO [DISABLED]"
  131.     _DISPLAY
  132.  
  133. FUNCTION GetThread0Address& (PID AS LONG)
  134.     SHELL _HIDE "cmd /c threadstack " + _TRIM$(STR$(PID)) + " > " + "threadstack"
  135.     OPEN "threadstack" FOR BINARY AS #1
  136.     DIM threadline AS STRING
  137.     DO
  138.         IF NOT EOF(1) THEN
  139.             LINE INPUT #1, threadline
  140.         END IF
  141.     LOOP UNTIL INSTR(threadline, "THREADSTACK 0 BASE ADDRESS: ") OR EOF(1)
  142.     threadline = RIGHT$(threadline, LEN(threadline) - INSTR(threadline, "THREADSTACK 0 BASE ADDRESS: ") - LEN("THREADSTACK 0 BASE ADDRESS: ") - 1)
  143.     KILL "threadstack"
  144.     GetThread0Address = VAL("&H" + threadline)

Video showing me using the code:
Shuwatch!