C++ zu Python übersetzung

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

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.

[codebox=cpp file=Unbenannt.cpp]#include <windows.h>
#include <cstring>
// The purpose of this program is just to show the basics of a custom launcher that people can make in SA-MP.
#include <iostream>
#include <stdio.h>
#include <direct.h>

// entry point

int main(int argc, char* argv[])
{

// Prepare to create a new process.
PROCESS_INFORMATION ProcessInfo;
STARTUPINFO StartupInfo;

memset(&ProcessInfo, 0, sizeof(PROCESS_INFORMATION));
memset(&StartupInfo, 0, sizeof(STARTUPINFO));

char *cwpath = NULL;
cwpath = getcwd(NULL, 0); // or _getcwd
if ( cwpath != NULL)
printf("%s\n", cwpath);
// Tell the user to enter an IP.
std::cout << "Please enter the IP you would like to connect to.\n";

// Get the IP.
char ip[24];
std::cin >> ip;

// Tell the user to enter a port
std::cout << "Please enter the port.\n";

// Get the port they typed.
int port;
std::cin >> port;

// Get the user's gta_sa location
char exeLocation[256], name[24];
DWORD buffer = sizeof(exeLocation);

// Open registry key
HKEY hKey;
long lError = RegOpenKeyEx(HKEY_CURRENT_USER,
"Software\\SAMP",
0,
KEY_READ,
&hKey);

// Get value
DWORD dwRet = RegQueryValueEx(hKey, "gta_sa_exe", NULL, NULL, (LPBYTE)&exeLocation, &buffer);

// Make sure we got a good value for the gta_sa path
if (dwRet != ERROR_SUCCESS)
{
MessageBoxA(NULL, "Could not get the location of your GTA:SA installation. Is SA-MP installed correctly?", "SA:MP Launcher", MB_ICONERROR);
return 0;
}

// remove \gta_sa.exe in a new variable (leaving just the directory path)
char path[256];
//strcpy(path, sizeof(path), exeLocation);
strcpy(path, exeLocation);
path[strlen(path) - 11] = '\0';

// Get the player name
buffer = sizeof(name);
dwRet = RegQueryValueEx(hKey, "PlayerName", NULL, NULL, (LPBYTE)&name, &buffer);

// Close registry
RegCloseKey(hKey);

char commandLine[128];
if (dwRet != ERROR_SUCCESS)
{
// Since a name couldn't be found, ask for one.
std::cout << "Enter a name";
std::cin >> name;
}

// Construct it all in one command line string.
//sprintf(commandLine, sizeof(commandLine), "-c -h %s -p %d -n %s", ip, port, name);
sprintf(commandLine, "-c -h %s -p %d -n %s", ip, port, name);

// Create a new process, but don't let it run yet, it's suspended.
if (CreateProcess(exeLocation, commandLine, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED, NULL, path, &StartupInfo, &ProcessInfo))
{
// Create a new string that will hold the path to the file samp.dll
char szWithSampdll[256] = "";
//sprintf(szWithSampdll, sizeof(szWithSampdll), "%s\\samp.dll", path);
sprintf(szWithSampdll, "%s\\samp.dll", path);
// Get the module handle to kernal32.dll
HMODULE hMod = GetModuleHandle("kernel32.dll");

// Create address variable to hold the address of the LoadLibrary function.
void* addr = NULL;

// If it was a valid handle.
if (hMod)
// Get the address of the LoadLibrary function so we can load samp.dll
addr = (void*)GetProcAddress(hMod, "LoadLibraryA");
else
{
MessageBoxA(NULL, "Could not find kernel32.dll", "SA:MP Launcher", MB_ICONERROR);
return 0;
}

// Allocate memory in the new process we just created to store the string of the samp.dll file path.
void* arg = (void*)VirtualAllocEx(ProcessInfo.hProcess, NULL, strlen(szWithSampdll), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

// Make sure the space was allocated.
if (arg != NULL)
// Write to the memory we just allocated the file path to samp.dll including directory.
WriteProcessMemory(ProcessInfo.hProcess, arg, szWithSampdll, strlen(szWithSampdll), NULL);
else
{
// arg is null, and we can't continue then.
// Let the user know there was a problem and exit.
MessageBoxA(NULL, "Memory could not be allocated to inject samp.dll", "SA:MP Launcher", MB_ICONERROR);
return 0;
}

// Create new handle to our remote thread.
HANDLE id = NULL;

// Make sure The address of LoadLibrary isn't NULL
if (addr != NULL)
{
// Create a remote thread that calls LoadLibrary, and as the parameter, the memory location we just wrote the samp.dll path to.
// also don't execute this thread, but just create.
id = CreateRemoteThread(ProcessInfo.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)addr, arg, CREATE_SUSPENDED, NULL);
}
else
{
MessageBoxA(NULL, "Could not find the address of LoadLibraryA", "SA:MP Launcher", MB_ICONERROR);
return 0;
}

// Make sure id is a valid handle
if (id)
{
// Resume the remote thread.
ResumeThread(id);

std::cout << "...patience..." << std::endl;

// Wait for the remote thread to finish executing.
WaitForSingleObject(id, INFINITE);
}
else
{
MessageBoxA(NULL, "the ID returned from CreateRemoteThread was invalid.", "SA:MP Launcher", MB_ICONERROR);
return 0;
}

// Free the memory we just allocated that stores the samp.dll file path since LoadLibrary has been called and it's not needed anymore.
VirtualFreeEx(ProcessInfo.hProcess, arg, 0, MEM_RELEASE);

// Resume the process (It was suspended, remember?)
ResumeThread(ProcessInfo.hThread);

// Close the handle to the process we created.
CloseHandle(ProcessInfo.hProcess);

}

// Done!
return 0;
}
[/code]

Nun habe ich angefangen es umzusetzen und habe folgendes zusammengebracht:

Code: Alles auswählen

from _winreg import *
import time
import ctypes 
import sys
from ctypes import *
from subprocess import Popen
from ctypes import wintypes
from _ctypes import call_function
import win32process
import win32con 
import win32event
import win32api

SERVER_IP = '127.0.0.1'
SERVER_PORT = '7777'

gta_path = 'C:\Program Files (x86)\Rockstar Games\GTA San Andreas\\'
registry 		 = ConnectRegistry(None, HKEY_CURRENT_USER)
sampRegistryKey = OpenKey(registry, 'SOFTWARE\SAMP\\')
name = QueryValueEx(sampRegistryKey, 'playerName')[0]

command = '-c -n %s -h %s -p %s' % (name, SERVER_IP, SERVER_PORT)

si = win32process.STARTUPINFO()
(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)
print '[*] PID: %s' % dwProcessId

kernel32 = windll.kernel32 

kernel32.LoadLibraryA.restype = c_void_p
kernel32.GetProcAddress.argtypes = c_void_p, c_char_p
kernel32.GetProcAddress.restype = c_void_p

hMod = kernel32.LoadLibraryA(b"kernel32")

if not hMod:
	print '[!] hMod not loaded'
	raise SystemExit
else:
	print '[*] hMod loaded'


addr = kernel32.GetProcAddress(hMod, b"LoadLibraryA")

if addr == 0:
	print '[!] Could not get LoadLibraryA addr'
	raise SystemExit
else:
	print '[*] LoadLibraryA addr found'

dll_path = 'C:\Program Files (x86)\Rockstar Games\GTA San Andreas\samp.dll'

dll_len = len(dll_path)

arg = kernel32.VirtualAllocEx(int(hProcess), 0, dll_len, win32con.MEM_RESERVE | win32con.MEM_COMMIT, win32con.PAGE_READWRITE)

if not arg:
	print '[!] Could not allocate memory'
	raise SystemExit
else:
	print '[*] Memory allocated'

written = c_int(0)
kernel32.WriteProcessMemory(int(hProcess), arg, dll_path, len(dll_path), byref(written))

thread_id = c_ulong(0)
thread = kernel32.CreateRemoteThread(int(hProcess), 0, 0, c_long(addr), arg, win32con.CREATE_SUSPENDED, thread_id)

if not thread:
	print '[!] Could not create remote thread'
	raise SystemExit
else:
	print '[*] Thread created'

kernel32.ResumeThread(thread)

print 'waiting...'
win32event.WaitForSingleObject(thread, win32event.INFINITE)
print 'finished'

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

kernel32.ResumeThread(int(hThread))

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

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:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
from ctypes import c_char_p, c_long, c_ulong, c_void_p, windll
import os
import _winreg as winreg
import win32process
import win32con
import win32event
 
SERVER_IP = '127.0.0.1'
SERVER_PORT = '7777'
GTA_PATH = r'C:\Program Files (x86)\Rockstar Games\GTA San Andreas'
DLL_PATH = os.path.join(GTA_PATH, 'samp.dll')

kernel32 = windll.kernel32
kernel32.LoadLibraryA.restype = c_void_p
kernel32.GetProcAddress.argtypes = c_void_p, c_char_p
kernel32.GetProcAddress.restype = c_void_p


def main():
    with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as registry:
        with winreg.OpenKey(registry, r'Software\SAMP') as samp_registry_key:
            player_name = winreg.QueryValueEx(
                samp_registry_key, 'PlayerName'
            )[0]

    startup_info = win32process.STARTUPINFO()
    process_handle, thread_handle, process_id, _ = win32process.CreateProcess(
        os.path.join(GTA_PATH, 'gta_sa.exe'),
        '-c -n {0} -h {1} -p {2}'.format(player_name, SERVER_IP, SERVER_PORT),
        None,
        None,
        False,
        win32con.DETACHED_PROCESS | win32con.CREATE_SUSPENDED,
        None,
        GTA_PATH,
        startup_info
    )
    with process_handle:
        print('[*] PID: %s' % process_id)
         
        module_handle = kernel32.LoadLibraryA(b'kernel32')
        if not module_handle:
            print('[!] module_handle not loaded')
            return
        else:
            print('[*] module_handle loaded')
         
        addr = kernel32.GetProcAddress(module_handle, b'LoadLibraryA')
        if not addr:
            print('[!] Could not get LoadLibraryA addr')
            return
        else:
            print('[*] LoadLibraryA addr found')
         
        arg = kernel32.VirtualAllocEx(
            int(process_handle),
            0,
            len(DLL_PATH),
            win32con.MEM_RESERVE | win32con.MEM_COMMIT,
            win32con.PAGE_READWRITE
        )
        if not arg:
            print('[!] Could not allocate memory')
            return
        else:
            print('[*] Memory allocated')
        try:
            kernel32.WriteProcessMemory(
                int(process_handle), arg, DLL_PATH, len(DLL_PATH), None
            )
             
            thread_id = kernel32.CreateRemoteThread(
                int(process_handle),
                0,
                0,
                c_long(addr),
                arg,
                win32con.CREATE_SUSPENDED,
                c_ulong(0)
            )
            if not thread_id:
                print('[!] Could not create remote thread')
                return
            else:
                print('[*] Thread created')
             
            kernel32.ResumeThread(thread_id)
             
            print('waiting...')
            win32event.WaitForSingleObject(thread_id, win32event.INFINITE)
            print('finished')
        finally:     
            kernel32.VirtualFreeEx(
                int(process_handle), arg, 0, win32con.MEM_RELEASE
            )

        kernel32.ResumeThread(int(thread_handle))


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

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

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).
Antworten