C# DLL to Python

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
tschaka81
User
Beiträge: 31
Registriert: Donnerstag 24. Januar 2019, 08:15

Hallo,

ich habe derzeit folgenden abgekürzten C# Code:

Code: Alles auswählen

        [DllImport("PxCrypto.dll", CharSet = CharSet.Ansi,
        EntryPoint = "_MakeHash@8")]
        private static extern int MakeHash(string password, ref object Hash);

	...
	object var_CryptoPass = "";
        MakeHash("", ref var_CryptoPass);
diesen wollte ich nach Python übersetzen. (Einen Großteil habe ich hier https://stackoverflow.com/questions/252 ... rom-python entnommen ohne wirklich zu verstehen wie das Ganze funktioniert...)

Code: Alles auswählen

hllDll = ctypes.WinDLL (r"PxCrypto.dll")
hllApiProto = ctypes.WINFUNCTYPE (
    ctypes.c_int,      # Return type.
    ctypes.c_void_p,   # string password
    ctypes.c_void_p)   # object Hash

hllApiParams = (1, "p1", 0), (1, "p2", 0),
hllApi = hllApiProto (("_MakeHash@8", hllDll), hllApiParams)
p1 = ctypes.c_char
p2 = ctypes.c_char_p
a = hllApi (ctypes.byref (p1), p2)
print(a)

Ich bin jetzt was C# und generell DLLs anbelangt nicht wirklich gut und komme hier nicht weiter.
Ich verstehe noch nicht, wie ich die p1, p2 Parameter richtig setzen muss, ob c_char das Richtige ist?

Derzeit bekomme ich den Fehler in python:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\mwagner\AppData\Local\Programs\Python\Python310-32\lib\code.py", line 90, in runcode
    exec(code, self.locals)
  File "<input>", line 11, in <module>
TypeError: byref() argument must be a ctypes instance, not '_ctypes.PyCSimpleType'

Weiß jemand, was ich falsch mache und würde es mir erklären?
Benutzeravatar
__blackjack__
User
Beiträge: 13931
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@tschaka81: Warum hast Du Dir gerade *das* Beispiel ausgesucht, bei dem das so umständlich über einen Funktionsprototypen gemacht wird?

Wobei die wichtigste Frage wohl ist, ob das überhaupt mit `ctypes` geht, denn das ist für native DLLs und nicht für C#-DLLs. Oder enthalten die nativen Code über den dann der .NET-Code aufgerufen wird?

Ansonsten ist die Fehlermeldung recht offensichtlich und erwartbar, weil Du versuchst die Datentypen als Werte zu übergeben, statt Objekte mit einem Wert davon zu erstellen, und die dann zu übergeben. So wie das im Beispiel ja auch gemacht wird.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
Sirius3
User
Beiträge: 18225
Registriert: Sonntag 21. Oktober 2012, 17:20

Woher kommt dieses PxCrypto.dll? Oder anders gefragt, welche Hash-Funktion steckt dahinter? Es gibt für alle relevanten Hash-Funktionen auch Python-Implementierungen.

Ein Passwort mit einer normalen Hash-Funktion zu bearbeiten, ist ein großes Sicherheitsproblem. Neben dem Algorithmus hat man noch die Anzahl der Iterationen, einen Salt oder weitere Parameter. Übliche Methoden sind bcrypt, PBKDF2 oder Argon2.

Das Password soll als ANSI-codierter c_char_p übergeben werden. ctypes kann ByteStrings automatisch konvertieren. Was da als object Hash zurückkommt, kann man schlehct sagen, das ist ein simpler Pointer, den Du per byref übergeben mußt. Du dagegen übergibst keine Werte, sondern die Typen.
tschaka81
User
Beiträge: 31
Registriert: Donnerstag 24. Januar 2019, 08:15

Nach ein wenig hin und her, habe ich auch gemerkt, dass das Ganze ein wenig umständlich ist:

Mittels:

Code: Alles auswählen

hllDll = ctypes.WinDLL (r"PxCrypto.dll")
a = ctypes.c_char()
hllDll.MakeHash("Hallo", ctypes.byref(a))
print(a)
bekomme ich schonmal das erste Zeichen zurück. (Glaube ich, zumindest keinen Fehler).

Wie kann ich den Datentyp von c_Char in String ändern?
Benutzeravatar
__blackjack__
User
Beiträge: 13931
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@tschaka81: String gibt es in C nicht als Datentyp. Das sind in C Zeiger auf Char. Das heisst Du musst hier ein Zeichen-Array anlegen das gross genug ist um das Ergebnis aufzunehmen, und dann einen Zeiger darauf übergeben.

An der Stelle kann man übrigens auch ganz gut sehen wie gefährlich C ist. Du übergibst da gerade einen Zeiger auf Speicherplatz für *ein* Zeichen, der Aufruf schreibt aber mehr als ein Zeichen dort hin und überschreibt irgendwelche Daten die dahinter im Speicher liegen. Das kann gut gehen, weil der Speicherplatz nicht (mehr) verwendet wird, das kann aber auch katastrophale Folgen haben. Von verfälschten Daten, bis zu harten Programmabstürzen.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
tschaka81
User
Beiträge: 31
Registriert: Donnerstag 24. Januar 2019, 08:15

__blackjack__ hat geschrieben: Donnerstag 24. März 2022, 12:03 @tschaka81: String gibt es in C nicht als Datentyp. Das sind in C Zeiger auf Char. Das heisst Du musst hier ein Zeichen-Array anlegen das gross genug ist um das Ergebnis aufzunehmen, und dann einen Zeiger darauf übergeben.

An der Stelle kann man übrigens auch ganz gut sehen wie gefährlich C ist. Du übergibst da gerade einen Zeiger auf Speicherplatz für *ein* Zeichen, der Aufruf schreibt aber mehr als ein Zeichen dort hin und überschreibt irgendwelche Daten die dahinter im Speicher liegen. Das kann gut gehen, weil der Speicherplatz nicht (mehr) verwendet wird, das kann aber auch katastrophale Folgen haben. Von verfälschten Daten, bis zu harten Programmabstürzen.
Das gebe ich dir recht mit dem Speicher.

Wie macht man das konkret mit dem Zeichen Array per ctypes?
tschaka81
User
Beiträge: 31
Registriert: Donnerstag 24. Januar 2019, 08:15

Ich habe jetzt mal folgendes versucht:

Code: Alles auswählen

import ctypes
hllDll = ctypes.WinDLL (r"C:\Canberra\Apex\Exefiles\PxCrypto.dll")
length_str = 20
a = [ctypes.create_string_buffer(length_str) for i in range(length_str)]
pointers = (ctypes.c_char_p*length_str)(*map(ctypes.addressof, a))
hllDll.MakeHash("Hallo", ctypes.byref(pointers))
print([k.value for k in a])
Das gibt mir zwar keinen Fehler zurück, aber auch nur ein bytearray mit lauter

Code: Alles auswählen

[b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'']
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Eh. Das ist doch komplett geraten. Wieso produzierst du denn ploetzlich eine Liste von Pointern? Das hat doch niemand irgendwo gefordert.

Mir fehlt hier vor allem die Angabe, was die Funktion als "Hash"-Objekt haben will. Ein void_p ist ja schoen und gut, aber der muss schon auf eine gewisse Menge Speicher zeigen. Wie sieht den die C++(!) Deklaration der Funktion aus? Und die all ihrer Argumente?
tschaka81
User
Beiträge: 31
Registriert: Donnerstag 24. Januar 2019, 08:15

__deets__ hat geschrieben: Donnerstag 24. März 2022, 12:38 Eh. Das ist doch komplett geraten. Wieso produzierst du denn ploetzlich eine Liste von Pointern? Das hat doch niemand irgendwo gefordert.

Mir fehlt hier vor allem die Angabe, was die Funktion als "Hash"-Objekt haben will. Ein void_p ist ja schoen und gut, aber der muss schon auf eine gewisse Menge Speicher zeigen. Wie sieht den die C++(!) Deklaration der Funktion aus? Und die all ihrer Argumente?
Die Declaration habe ich leider nicht. Ich bin davon ausgegangen, dass ein String am Schluss ein Zeiger auf einzelne Positionen im String zeigt. Scheinbar aber nicht^^
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich weiss nicht, was "ein String am Schluss ein Zeiger auf einzelne Positionen im String" sein soll. Das zweite Argument ist aus C++-Sicht "nur" ein Pointer, aber eben auf einen unbekannten Typ - Hash.

Bist du sicher, dass du keine Deklaration hast? Irgendwo in der Dokumentation der DLL muss das doch stehen, der Hersteller gibt die ja nicht ohne alles raus. Und dann gibt es ein "ref object Hash" im C#, wie sieht denn das aus?
tschaka81
User
Beiträge: 31
Registriert: Donnerstag 24. Januar 2019, 08:15

Wie gesagt, dass habe ich nicht. Müsste ich erstmal nachfragen beim Hersteller.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und den C# Code hast du auch nicht? Dann hilft nur die Nachfrage, ja.
tschaka81
User
Beiträge: 31
Registriert: Donnerstag 24. Januar 2019, 08:15

alles klar, ich werde mal den Kontakt suchen. Vielen Dank schonmal für die Hilfe
Sirius3
User
Beiträge: 18225
Registriert: Sonntag 21. Oktober 2012, 17:20

Woher kommt die PxCrypto.dll? Was ist das für eine Bibliothek? Was wird weiter in dem C#-Programm mit dem Ergebnis gemacht?
tschaka81
User
Beiträge: 31
Registriert: Donnerstag 24. Januar 2019, 08:15

Die Cryptodll gehört zu einem speziellen Programm und sorgt für die Anmeldung an diesem. Ich habe jetzt den Part mit dem MakeHash hinbekommen:
daraufhin sollte ich mich mit Authenticate einloggen können. Hier scheitere ich wieder an secman. secman kommt aus sessions.Properties("SecMgr") und ist der gleiche datentyp wie secman2 (Hab ich geprüft. es sind zumindest die gleichen Methoden verbaut)

Wenn ich jetzt

Code: Alles auswählen

hllDll.Authenticate(secman, facility, user, automation.byref(a))
ausführe, dann bekomme ich:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\mwagner\AppData\Local\Programs\Python\Python310-32\lib\code.py", line 90, in runcode
    exec(code, self.locals)
  File "<input>", line 14, in <module>
ctypes.ArgumentError: argument 1: <class 'TypeError'>: Don't know how to convert parameter 1
Laut Hersteller ist der datentyp ein SecMgr, d.h. vom typ secman2 (in meinem Code als Beispiel)

Wie bekomme ich Python das nun beigebracht?

Hier ist der Code den ich derzeit habe:

Code: Alles auswählen

password = ""
user = "Administrator"
facility = "Default"
import ctypes
from comtypes import automation
import win32com.client
sessions = win32com.client.Dispatch("Canberra.SessionMgr.1")
secman = sessions.Properties("SecMgr")
secman2 = win32com.client.Dispatch("Canberra.SecMan.SecMgr.1")
hllDll = ctypes.WinDLL (r"C:\Canberra\Apex\Exefiles\PxCrypto.dll")
a = automation.VARIANT()
hllDll.MakeHash(password, automation.byref(a))
hllDll.Authenticate.restype = ctypes.c_long
hllDll.Authenticate(secman, facility, user, automation.byref(a))
sessions.BeginSession("localhost")

Parallel habe ich einen Code für VBA bekommen:

Code: Alles auswählen

Private Declare Function MakeHash Lib "PxCrypto" Alias "_MakeHash@8" (ByVal password As String, ByRef Hash As Variant) As Boolean
Private Declare Function Authenticate Lib "PxCrypto" Alias "_Authenticate@16" (ByVal SecurityManager As SecMgr, ByVal Facility As String, ByVal UserName As String, ByRef PasswordHash As Variant) As Long
Dim var_CryptoPass                      As Variant
Dim obj_SecMgr                          As SecMgr
Dim obj_SM                              As SessionMgr

Set obj_SM = New SessionMgr
MakeHash "", var_CryptoPass
Set obj_SecMgr = obj_SM.Properties("SecMgr")
Authenticate obj_SecMgr, FACILITY_NAME, USER_NAME, var_CryptoPass
Ich verstehe noch nicht was die Aliasse machen?
Antworten