C++ zu Python übersetzung

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
freaK
User
Beiträge: 3
Registriert: Freitag 5. August 2016, 19:22

C++ zu Python übersetzung

Beitragvon freaK » Freitag 5. August 2016, 19:37

Guten Abend zusammen,

ich hatte mir vorgenommen mit Python unter Windows zu experimentieren und hatte aus vergangener Zeit ein altes C++ Schnipsel gefunden welches nun von mir nach Python übersetzt werden wollte.

Das ganze soll ein Spiel suspended starten, eine Multiplayer DLL laden und das Spiel darauf weiter ausführen.

  1. #include <windows.h>
  2. #include <cstring>
  3. // The purpose of this program is just to show the basics of a custom launcher that people can make in SA-MP.
  4. #include <iostream>
  5. #include <stdio.h>
  6. #include <direct.h>
  7.  
  8. // entry point
  9.  
  10. int main(int argc, char* argv[])
  11. {
  12.    
  13.     // Prepare to create a new process.
  14.     PROCESS_INFORMATION ProcessInfo;
  15.     STARTUPINFO StartupInfo;
  16.  
  17.     memset(&ProcessInfo, 0, sizeof(PROCESS_INFORMATION));
  18.     memset(&StartupInfo, 0, sizeof(STARTUPINFO));
  19.    
  20.     char *cwpath = NULL;
  21.     cwpath = getcwd(NULL, 0); // or _getcwd
  22.     if ( cwpath != NULL)
  23.         printf("%s\n", cwpath);
  24.     // Tell the user to enter an IP.
  25.     std::cout << "Please enter the IP you would like to connect to.\n";
  26.    
  27.     // Get the IP.
  28.     char ip[24];
  29.     std::cin >> ip;
  30.  
  31.     // Tell the user to enter a port
  32.     std::cout << "Please enter the port.\n";
  33.  
  34.     // Get the port they typed.
  35.     int port;
  36.     std::cin >> port;
  37.  
  38.     // Get the user's gta_sa location
  39.     char exeLocation[256], name[24];
  40.     DWORD buffer = sizeof(exeLocation);
  41.  
  42.     // Open registry key
  43.     HKEY hKey;
  44.     long lError = RegOpenKeyEx(HKEY_CURRENT_USER,
  45.         "Software\\SAMP",
  46.         0,
  47.         KEY_READ,
  48.         &hKey);
  49.  
  50.     // Get value
  51.     DWORD dwRet = RegQueryValueEx(hKey, "gta_sa_exe", NULL, NULL, (LPBYTE)&exeLocation, &buffer);
  52.  
  53.     // Make sure we got a good value for the gta_sa path
  54.     if (dwRet != ERROR_SUCCESS)
  55.     {
  56.         MessageBoxA(NULL, "Could not get the location of your GTA:SA installation. Is SA-MP installed correctly?", "SA:MP Launcher", MB_ICONERROR);
  57.         return 0;
  58.     }
  59.  
  60.     // remove \gta_sa.exe in a new variable (leaving just the directory path)
  61.     char path[256];
  62.     //strcpy(path, sizeof(path), exeLocation);
  63.     strcpy(path, exeLocation);
  64.     path[strlen(path) - 11] = '\0';
  65.  
  66.     // Get the player name
  67.     buffer = sizeof(name);
  68.     dwRet = RegQueryValueEx(hKey, "PlayerName", NULL, NULL, (LPBYTE)&name, &buffer);
  69.  
  70.     // Close registry
  71.     RegCloseKey(hKey);
  72.  
  73.     char commandLine[128];
  74.     if (dwRet != ERROR_SUCCESS)
  75.     {
  76.         // Since a name couldn't be found, ask for one.
  77.         std::cout << "Enter a name";
  78.         std::cin >> name;
  79.     }
  80.  
  81.     // Construct it all in one command line string.
  82.     //sprintf(commandLine, sizeof(commandLine), "-c -h %s -p %d -n %s", ip, port, name);
  83.     sprintf(commandLine, "-c -h %s -p %d -n %s", ip, port, name);
  84.  
  85.     // Create a new process, but don't let it run yet, it's suspended.
  86.     if (CreateProcess(exeLocation, commandLine, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED, NULL, path, &StartupInfo, &ProcessInfo))
  87.     {
  88.         // Create a new string that will hold the path to the file samp.dll
  89.         char szWithSampdll[256] = "";
  90.         //sprintf(szWithSampdll, sizeof(szWithSampdll), "%s\\samp.dll", path);
  91.         sprintf(szWithSampdll, "%s\\samp.dll", path);
  92.         // Get the module handle to kernal32.dll
  93.         HMODULE hMod = GetModuleHandle("kernel32.dll");
  94.  
  95.         // Create address variable to hold the address of the LoadLibrary function.
  96.         void* addr = NULL;
  97.  
  98.         // If it was a valid handle.
  99.         if (hMod)
  100.             // Get the address of the LoadLibrary function so we can load samp.dll
  101.             addr = (void*)GetProcAddress(hMod, "LoadLibraryA");
  102.         else
  103.         {
  104.             MessageBoxA(NULL, "Could not find kernel32.dll", "SA:MP Launcher", MB_ICONERROR);
  105.             return 0;
  106.         }
  107.  
  108.         // Allocate memory in the new process we just created to store the string of the samp.dll file path.
  109.         void* arg = (void*)VirtualAllocEx(ProcessInfo.hProcess, NULL, strlen(szWithSampdll), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  110.        
  111.         // Make sure the space was allocated.
  112.         if (arg != NULL)
  113.             // Write to the memory we just allocated the file path to samp.dll including directory.
  114.             WriteProcessMemory(ProcessInfo.hProcess, arg, szWithSampdll, strlen(szWithSampdll), NULL);
  115.         else
  116.         {
  117.             // arg is null, and we can't continue then.
  118.             // Let the user know there was a problem and exit.
  119.             MessageBoxA(NULL, "Memory could not be allocated to inject samp.dll", "SA:MP Launcher", MB_ICONERROR);
  120.             return 0;
  121.         }
  122.  
  123.         // Create new handle to our remote thread.
  124.         HANDLE id = NULL;
  125.  
  126.         // Make sure The address of LoadLibrary isn't NULL
  127.         if (addr != NULL)
  128.         {
  129.             // Create a remote thread that calls LoadLibrary, and as the parameter, the memory location we just wrote the samp.dll path to.
  130.             // also don't execute this thread, but just create.
  131.             id = CreateRemoteThread(ProcessInfo.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)addr, arg, CREATE_SUSPENDED, NULL);
  132.         }
  133.         else
  134.         {
  135.             MessageBoxA(NULL, "Could not find the address of LoadLibraryA", "SA:MP Launcher", MB_ICONERROR);
  136.             return 0;
  137.         }
  138.  
  139.         // Make sure id is a valid handle
  140.         if (id)
  141.         {
  142.             // Resume the remote thread.
  143.             ResumeThread(id);
  144.  
  145.             std::cout << "...patience..." << std::endl;
  146.  
  147.             // Wait for the remote thread to finish executing.
  148.             WaitForSingleObject(id, INFINITE);
  149.         }
  150.         else
  151.         {
  152.             MessageBoxA(NULL, "the ID returned from CreateRemoteThread was invalid.", "SA:MP Launcher", MB_ICONERROR);
  153.             return 0;
  154.         }
  155.  
  156.         // Free the memory we just allocated that stores the samp.dll file path since LoadLibrary has been called and it's not needed anymore.
  157.         VirtualFreeEx(ProcessInfo.hProcess, arg, 0, MEM_RELEASE);
  158.  
  159.         // Resume the process (It was suspended, remember?)
  160.         ResumeThread(ProcessInfo.hThread);
  161.  
  162.         // Close the handle to the process we created.
  163.         CloseHandle(ProcessInfo.hProcess);
  164.  
  165.     }
  166.    
  167.     // Done!
  168.     return 0;
  169. }


Nun habe ich angefangen es umzusetzen und habe folgendes zusammengebracht:

  1. from _winreg import *
  2. import time
  3. import ctypes
  4. import sys
  5. from ctypes import *
  6. from subprocess import Popen
  7. from ctypes import wintypes
  8. from _ctypes import call_function
  9. import win32process
  10. import win32con
  11. import win32event
  12. import win32api
  13.  
  14. SERVER_IP = '127.0.0.1'
  15. SERVER_PORT = '7777'
  16.  
  17. gta_path = 'C:\Program Files (x86)\Rockstar Games\GTA San Andreas\\'
  18. registry         = ConnectRegistry(None, HKEY_CURRENT_USER)
  19. sampRegistryKey = OpenKey(registry, 'SOFTWARE\SAMP\\')
  20. name = QueryValueEx(sampRegistryKey, 'playerName')[0]
  21.  
  22. command = '-c -n %s -h %s -p %s' % (name, SERVER_IP, SERVER_PORT)
  23.  
  24. si = win32process.STARTUPINFO()
  25. (hProcess, hThread, dwProcessId, dwThreadId) = win32process.CreateProcess('%sgta_sa.exe' % gta_path, command, None, None, False, win32con.DETACHED_PROCESS | win32con.CREATE_SUSPENDED, None, gta_path, si)
  26. print '[*] PID: %s' % dwProcessId
  27.  
  28. kernel32 = windll.kernel32
  29.  
  30. kernel32.LoadLibraryA.restype = c_void_p
  31. kernel32.GetProcAddress.argtypes = c_void_p, c_char_p
  32. kernel32.GetProcAddress.restype = c_void_p
  33.  
  34. hMod = kernel32.LoadLibraryA(b"kernel32")
  35.  
  36. if not hMod:
  37.     print '[!] hMod not loaded'
  38.     raise SystemExit
  39. else:
  40.     print '[*] hMod loaded'
  41.  
  42.  
  43. addr = kernel32.GetProcAddress(hMod, b"LoadLibraryA")
  44.  
  45. if addr == 0:
  46.     print '[!] Could not get LoadLibraryA addr'
  47.     raise SystemExit
  48. else:
  49.     print '[*] LoadLibraryA addr found'
  50.  
  51. dll_path = 'C:\Program Files (x86)\Rockstar Games\GTA San Andreas\samp.dll'
  52.  
  53. dll_len = len(dll_path)
  54.  
  55. arg = kernel32.VirtualAllocEx(int(hProcess), 0, dll_len, win32con.MEM_RESERVE | win32con.MEM_COMMIT, win32con.PAGE_READWRITE)
  56.  
  57. if not arg:
  58.     print '[!] Could not allocate memory'
  59.     raise SystemExit
  60. else:
  61.     print '[*] Memory allocated'
  62.  
  63. written = c_int(0)
  64. kernel32.WriteProcessMemory(int(hProcess), arg, dll_path, len(dll_path), byref(written))
  65.  
  66. thread_id = c_ulong(0)
  67. thread = kernel32.CreateRemoteThread(int(hProcess), 0, 0, c_long(addr), arg, win32con.CREATE_SUSPENDED, thread_id)
  68.  
  69. if not thread:
  70.     print '[!] Could not create remote thread'
  71.     raise SystemExit
  72. else:
  73.     print '[*] Thread created'
  74.  
  75. kernel32.ResumeThread(thread)
  76.  
  77. print 'waiting...'
  78. win32event.WaitForSingleObject(thread, win32event.INFINITE)
  79. print 'finished'
  80.  
  81. kernel32.VirtualFreeEx(int(hProcess), arg, 0, win32con.MEM_RELEASE)
  82.  
  83. kernel32.ResumeThread(int(hThread))
  84.  
  85. hProcess.Close()


Das Problem ist, dass sich das Spiel bei der Python Variante sofort nach 'WaitForSingleObject' schließt.
Die C++ Variante läuft einwandfrei.

Habe ich hier etwas übersehen?

Danke im voraus.
BlackJack

Re: C++ zu Python übersetzung

Beitragvon BlackJack » Donnerstag 18. August 2016, 11:04

Ein paar allgemeine Anmerkungen zum Quelltext:

Namen mit einem führenden Unterstrich bedeuten, dass das keine öffentliche API ist. Aus `_ctypes` solltest man nichts importieren. `_winreg` ist leider eine Ausnahme, da heisst das offizielle Modul (in Python 2) tatsächlich so.

Sternchen-Importe sind Böse™. Das kann dann nämlich schon eine Quelle für Fehler sein, weil Du nicht weisst was da alles importiert wird und welche anderen Namen dabei eventuell verdeckt werden. Ausserdem ist das Programm schwerer zu durchschauen wenn man nicht mehr einfach nachvollziehen kann wo ein Name her kommt.

Du schliesst weder die Registry noch den Schlüssel. Da bietet sich die ``with``-Anweisung an.

Pfadteile setzt man mit `os.path.join()` zusammen.

Ich kenn mich mit den Windowsfunktionen nicht aus, aber Du weichst bei der Übersetzung von C++ ab. Im Python-Programm kommt `GetModuleHandle()` nicht vor.

``raise SystemExit`` ist ungewöhnlich. `sys.exit()` wäre da eigentlich der Aufruf der an der Stelle stehen würde. Oder einfach ein ``return`` in diesen speziellen Fällen.

So sieht es etwas mehr nach Python aus:
  1. #!/usr/bin/env python
  2. # coding: utf8
  3. from __future__ import absolute_import, division, print_function
  4. from ctypes import c_char_p, c_long, c_ulong, c_void_p, windll
  5. import os
  6. import _winreg as winreg
  7. import win32process
  8. import win32con
  9. import win32event
  10.  
  11. SERVER_IP = '127.0.0.1'
  12. SERVER_PORT = '7777'
  13. GTA_PATH = r'C:\Program Files (x86)\Rockstar Games\GTA San Andreas'
  14. DLL_PATH = os.path.join(GTA_PATH, 'samp.dll')
  15.  
  16. kernel32 = windll.kernel32
  17. kernel32.LoadLibraryA.restype = c_void_p
  18. kernel32.GetProcAddress.argtypes = c_void_p, c_char_p
  19. kernel32.GetProcAddress.restype = c_void_p
  20.  
  21.  
  22. def main():
  23.     with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as registry:
  24.         with winreg.OpenKey(registry, r'Software\SAMP') as samp_registry_key:
  25.             player_name = winreg.QueryValueEx(
  26.                 samp_registry_key, 'PlayerName'
  27.             )[0]
  28.  
  29.     startup_info = win32process.STARTUPINFO()
  30.     process_handle, thread_handle, process_id, _ = win32process.CreateProcess(
  31.         os.path.join(GTA_PATH, 'gta_sa.exe'),
  32.         '-c -n {0} -h {1} -p {2}'.format(player_name, SERVER_IP, SERVER_PORT),
  33.         None,
  34.         None,
  35.         False,
  36.         win32con.DETACHED_PROCESS | win32con.CREATE_SUSPENDED,
  37.         None,
  38.         GTA_PATH,
  39.         startup_info
  40.     )
  41.     with process_handle:
  42.         print('[*] PID: %s' % process_id)
  43.          
  44.         module_handle = kernel32.LoadLibraryA(b'kernel32')
  45.         if not module_handle:
  46.             print('[!] module_handle not loaded')
  47.             return
  48.         else:
  49.             print('[*] module_handle loaded')
  50.          
  51.         addr = kernel32.GetProcAddress(module_handle, b'LoadLibraryA')
  52.         if not addr:
  53.             print('[!] Could not get LoadLibraryA addr')
  54.             return
  55.         else:
  56.             print('[*] LoadLibraryA addr found')
  57.          
  58.         arg = kernel32.VirtualAllocEx(
  59.             int(process_handle),
  60.             0,
  61.             len(DLL_PATH),
  62.             win32con.MEM_RESERVE | win32con.MEM_COMMIT,
  63.             win32con.PAGE_READWRITE
  64.         )
  65.         if not arg:
  66.             print('[!] Could not allocate memory')
  67.             return
  68.         else:
  69.             print('[*] Memory allocated')
  70.         try:
  71.             kernel32.WriteProcessMemory(
  72.                 int(process_handle), arg, DLL_PATH, len(DLL_PATH), None
  73.             )
  74.              
  75.             thread_id = kernel32.CreateRemoteThread(
  76.                 int(process_handle),
  77.                 0,
  78.                 0,
  79.                 c_long(addr),
  80.                 arg,
  81.                 win32con.CREATE_SUSPENDED,
  82.                 c_ulong(0)
  83.             )
  84.             if not thread_id:
  85.                 print('[!] Could not create remote thread')
  86.                 return
  87.             else:
  88.                 print('[*] Thread created')
  89.              
  90.             kernel32.ResumeThread(thread_id)
  91.              
  92.             print('waiting...')
  93.             win32event.WaitForSingleObject(thread_id, win32event.INFINITE)
  94.             print('finished')
  95.         finally:    
  96.             kernel32.VirtualFreeEx(
  97.                 int(process_handle), arg, 0, win32con.MEM_RELEASE
  98.             )
  99.  
  100.         kernel32.ResumeThread(int(thread_handle))
  101.  
  102.  
  103. if __name__ == '__main__':
  104.     main()

Was ist denn der Rückgabewert von `WaitForSingleObject()`?
freaK
User
Beiträge: 3
Registriert: Freitag 5. August 2016, 19:22

Re: C++ zu Python übersetzung

Beitragvon freaK » Samstag 20. August 2016, 13:31

Zu meiner Verteidigung im Sachen Qualität des Codes, ich habe das meiste nur als PoC aus verschiedenen Schnipseln zusammengesetzt und werde natürlich den Code überarbeiten.

GetModuleHandle hatte ich durch LoadLibrary ersetzt da ich einen dieser Wunderbaren Windows Fehlermeldungen bekommen hatte, dass kernel32.dll nicht gefunden wurde. Dies könnte auch eine der potentiellen Fehlerquellen sein.

Den rückgabewerr von WaitForSingleObject habe ich leider nicht zur Hand und es ist mir auch leider nicht möglich das in der näheren Zukunft nachzustellen.

Die Ergebnisse werde ich in einiger Zeit auf diesen Thread Posten.
freaK
User
Beiträge: 3
Registriert: Freitag 5. August 2016, 19:22

Re: C++ zu Python übersetzung

Beitragvon freaK » Donnerstag 1. September 2016, 00:10

Nach etwas Debugging konnte ich den Fehler "finden".

Der Fehler liegt in

Code: Alles auswählen

kernel32.VirtualFreeEx(
    int(process_handle), arg, 0, win32con.MEM_RELEASE
)

und

Code: Alles auswählen

kernel32.ResumeThread(int(thread_handle))


Ich erhallte jeweils immer einen Windows Fehlercode 5 (Access denied).

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder