C# Aufruf in unmanaged C++

JJB

Cogito ergo brumm
Hallo zusammen,

ich weiß nicht ob das ein Sakrileg ist, aber ich bin auf der Suche nach einer Möglichkeit unmanaged und managed Code zu verbinden.
Hintergrund ist, dass mein Chef Angst hat, seine C# DLLs könnten dekompiliert werden und damit seine Geheimnisse preisgegeben. Da hat er vorgeschlagen, einige der Funktionen in nativem Code unmanaged zu schreiben (C++), um dort Funktionen, Übergaben und Variablen zu verwalten, die nicht dekompiliert werden können.
Im besonderen geht es hier um Verschlüsselungsroutinen, die in C# geschrieben sind, aber nicht dekompilierbar sein sollen.

Meine Frage daher:
Kann ich in irgendwie (nicht so leicht dekompilierbaren) unmanaged Code schreiben, aus welchem ich meine C# Dlls benutze oder umgekehrt ? Oder kann ich meine C# Funktionen vielleicht unmanaged kompilieren / konvertieren ?

Ich bin für jede Hilfe dankbar !

MFG JJB

(Wenn das das falsche Unterforum dafür ist, bitte ich um Entschuldigung.)
 
Eine Möglichkeit wäre:
http://msdn.microsoft.com/en-us/library/aa288468(VS.71).aspx

Du kannst dann einige Funktionen exportieren, welche dann von C# über P-Invoke aufgerufen werden können.

Eine andere Möglichkeit wäre es wenn du eine unverwaltete COM-Klasse machen würdest, die dann entsprechend referenziert wird. Oder du erstellst ein gemischtes Assembly in C++/CLI mit unverwaltetem Code und einem verwalteten Wrapper.
 
C++CLI würd ich auch empfehlen

Aber: Es funktioniert vielleicht nicht so gut wie beim .NET Programmen, aber
man kann C/C++-Programme auch dekompileren
 
Hm... Danke erstmal für die Antworten.

Das führt mich zu weiteren Fragen:

Wie aufwendig ist das dekompilieren von unmanaged C++ ?
Und kann ich Funktionen einer C# Dll aus C++ aufrufen ?


MG JJB
 
Bezüglich Frage 2: Soweit ich weiß, ist das nicht möglich.
Eine .NET-DLL heißt zwar DLL, hat aber mit der, die in C/C++ eingebunden werden kann, nicht viel gemeinsam.
Deswegen der Hinweis auf C++CLI; grob gesagt C++ mit .NET Framework
Ein wichtiges Merkmal von C++CLI ist, obwohl es eine .NET-Sprache ist, kann im gleichen Programm "normaler" C++ Code mit C++CLI gemischt werden.
Deswegen könntest du ja in C++CLI einen .NET-Wrapper für die normalen C/C++-Funktionen schreiben.

Und die Sache mit dem Dekompilieren:

Es gibt Ansätze, das ganze komplett automatisch in C-Code umzuwandeln, aber der Code, der dabei raus kommt, ist...ziemlich unbrauchbar
(Bin in dem Gebiet nicht ganz auf dem neuesten Stand, vllt. geht es ja inzwischen besser.
Bezweifle ich aber irgendwie)

Vorallem Dinge wie Geschwindigkeitsoptimierungen der Compiler etc machen ziemliche Probleme, wenn ein Code entstehen soll, der für Menschen nachvollziehbar ist.

Bezüglich dem Zeitaufwand, um ein Programm per Hand/Disassembler zu zerlegen und C-Code daraus zu machen, kann ich leider nichts sagen.
Liegt daran, dass ich in die Richtung zwar ein bisschen "herumgespielt" habe, aber nie ein auch noch so kleines Programm wirklich fertig zerlegt habe...einfach zu faul

Wegen der vollautomatischen Möglichkeit hab ich noch irgendwo ein Beispiel, ich suchs mal.
Damit du einen Eindruck hast, wie die Codequalität so wird.
 
Hier ein sehr mickriger C-Code:
C++:
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<windows.h>

int funk1(int a,int b)
{
	return(2*a+b-(rand()%4));
}

double funk2(int c)
{
	return(sqrt(c)+funk1(c,88));
}

int main()
{
	srand(time(0));
	int x,y;
	x=(int)funk2(4);
	y=funk1(3,96)+(int)funk2(1);
	printf("%d %d\n",x,y);
	return 0;
}

Das ganze wurde mit VS 6.0 kompiliert, zerlegt wurde es mit IDA Pro
Hier das Ergebnis:
C++:
#include <windows.h>
#include <defs.h>


//-------------------------------------------------------------------------
// Data declarations

extern char Format[]; // idb
extern int dword_409038; // weak
extern _UNKNOWN unk_409648; // weak
extern _UNKNOWN unk_409660; // weak

//-------------------------------------------------------------------------
// Function declarations

int __cdecl sub_401000(int a1, int a2);
int __cdecl sub_401030(int a1);
int __cdecl main(int argc, const char **argv, const char *envp); // idb
int __cdecl sub_4010B0(int a1);
// int __cdecl rand(); idb
// int printf(const char *Format, ...); idb
// time_t __cdecl time(time_t *Time); idb
// _DWORD _ftol(); weak
// _DWORD __cdecl _ld12cvt(_DWORD, _DWORD, _DWORD); weak
int __cdecl sub_403498(int a1, int a2);
int __cdecl sub_4034AE(int a1, int a2);
int __cdecl sub_4034C4(int a1, int a2);
int __cdecl sub_4034F1(int a1, int a2);
int __cdecl sub_403D05();
// _DWORD __cdecl flsall(_DWORD); weak
// _DWORD __cdecl __strgtold12(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD, _DWORD, _DWORD); weak


//----- (00401000) --------------------------------------------------------
int __cdecl sub_401000(int a1, int a2)
{
  return a2 + 2 * a1 - rand() % 4;
}

//----- (00401030) --------------------------------------------------------
int __cdecl sub_401030(int a1)
{
  int result; // eax@1
  int v2; // [sp+0h] [bp-4h]@1

  result = sub_401000(a1, 88);
  v2 = result;
  __asm
  {
    fild    [esp+0Ch+var_4]
    fild    [esp+0Ch+arg_0]
    fsqrt
    faddp   st(1), st
  }
  return result;
}

//----- (00401060) --------------------------------------------------------
int __cdecl main(int argc, const char **argv, const char *envp)
{
  int v4; // eax@1
  int v5; // edi@1
  int v6; // esi@1
  int v7; // eax@1

  v4 = time(0);
  sub_4010B0(v4);
  sub_401030(4);
  v5 = _ftol();
  v6 = sub_401000(3, 96);
  sub_401030(1);
  v7 = _ftol();
  printf("%d %d\n", v5, v7 + v6);
  return 0;
}

//----- (004010B0) --------------------------------------------------------
int __cdecl sub_4010B0(int a1)
{
  int result; // eax@1

  result = a1;
  dword_409038 = a1;
  return result;
}

//----- (00403498) --------------------------------------------------------
int __cdecl sub_403498(int a1, int a2)
{
  return _ld12cvt(a1, a2, &unk_409648);
}

//----- (004034AE) --------------------------------------------------------
int __cdecl sub_4034AE(int a1, int a2)
{
  return _ld12cvt(a1, a2, &unk_409660);
}

//----- (004034C4) --------------------------------------------------------
int __cdecl sub_4034C4(int a1, int a2)
{
  char v3; // [sp+0h] [bp-Ch]@1

  __strgtold12(&v3, &a2, a2, 0, 0, 0, 0);
  return sub_403498((int)&v3, a1);
}

//----- (004034F1) --------------------------------------------------------
int __cdecl sub_4034F1(int a1, int a2)
{
  char v3; // [sp+0h] [bp-Ch]@1

  __strgtold12(&v3, &a2, a2, 0, 0, 0, 0);
  return sub_4034AE((int)&v3, a1);
}

//----- (00403D05) --------------------------------------------------------
int __cdecl sub_403D05()
{
  return flsall(1);
}

Ist doch viel besser lesbar und übersichtlicher als das Original, oder? :)
 
Hallo,

ich sehe, so komme ich nicht weiter. Hätte nicht gedacht, dass sich der unmanaged Code ebenso trivial dekompilieren lässt.
Schätze mal, alles was nicht gerade als Assembler Code geschrieben ist, lässt sich relativ verständlich zurück transformieren.

Wie es aussieht besteht keinerlei Ambition, Code (also "Firmengeheimnisse") die in .Net geschrieben worden sind, irgendwie zu schützen, außer dadurch, dass einfach niemand die Programmdaten in die Hand kriegt.
Welche Verschlüsselung oder Verschleierung ich auch einsetze, irgendwo beginnt es mit einem Schlüssel, der auch wieder irgendwie bekannt ist.

Kann ich das so verstehen, dass .Net für den kommerziellen Einsatz nie vorgesehen war ?
Zumindest aus Sicht eines konkurrierenden Softwareherstellers ist das ja unbrauchbar.

MFG JJB
 
Schätze mal, alles was nicht gerade als Assembler Code geschrieben ist, lässt sich relativ verständlich zurück transformieren.

Und wenn man Assemblererfahrung hat und dazu noch einen Disassembler Kann man sich die C-Codegenerierung sowieso sparen
Im Endeffekt lässt sich mit genügend Zeit jedes Programm zerlegen, egal was der Programmierer dagegen unternommen hat.

Wie es aussieht besteht keinerlei Ambition, Code (also "Firmengeheimnisse") die in .Net geschrieben worden sind, irgendwie zu schützen, außer dadurch, dass einfach niemand die Programmdaten in die Hand kriegt.
...Kann ich das so verstehen, dass .Net für den kommerziellen Einsatz nie vorgesehen war ?

Ja, was hat sich MS nur gedacht, wie sie .NET erfunden haben :)

...Der einzige "Tipp", denn MS zu dieser Frage hat, ist, den Code so verwirrend wie möglich zu gestalten (da gibts sogar ein eigenes Tool dafür, den sog. "Dotfuscator"),
damit der Dekompilierer (der Mensch) sich nicht auskennt, was der Code zu bedeuten hat.

Ist aber leider auch nur eine Zeitverzögerung...derjenige braucht vielleicht länger, um den Code zu verstehen, aber sobald er ihn verstanden hat...
 
Hallo,

Dinge wie den Dotfuscator hatte ich mir schon mal zu Genüge geführt. (erinnerte mich an die Spielerei mit dem Firefox Confuscator)
Das ist im Endeffekt nur ein Ersetzen von Namen und Verweisen. Der Code an sich bleibt weiterhin offen und unverändert. Auch wenn durch die vielen gleichen Bezeichner manches automatische Programm ins Straucheln kommen könnte.

Mein letzter Gedanke ist etwas, was ich früher mal mit xml Dateien gemacht habe. Ich verschlüssele und packe meine Nutzdaten, in dem Fall EXEn und DLLs auf Dateiebene und lege einen Entpacker dazu der auf eine bestimmte Bytefolge wartet. Wird diese übergeben, werden die Nutzdaten temporär entpackt, wobei die Existenz der Nutzdaten an die Existenz des Programms selbst gebunden ist.
Mal sehen ob ich das irgendwie übernehmen und die Bytefolge irgendwie verschleiern kann.

Wenn das nicht hinhaut hak' ich das ab und lasse meine Leute im Glauben, ein Aufruf aus unmanaged Code mit Zuhilfenahme eines Programms wie dem Dotfuscator wäre ausreichend sicher, als dass die Konkurrenz die Algorithmen nicht klauen kann.

Danke für eure Hilfe !

JJB
 

Neue Beiträge

Zurück