PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : DLL-Calls abfangen


MeLLe
2004-06-28, 12:05:16
Hallo Mädels,

neugierig wie ich immer bin will ich heute mal was wissen, was für euch sicher 'n Klacks ist, mir aber arges Kopfweh bereitet:

Wie kann ich Funktionsaufrufe einer Anwendung abfangen, z.B. zum mitloggen, und dann an die eigentliche Ziel-DLL weitergeben? Speziell interessant wären da OpenGL-Calls, denn im Prinzip reicht es ja aus, in das App-Verzeichnis meine Intercept-DLL als opengl32.dll reinzustellen. Nur leider hab ich irgendwie ne Denkbarrikade, denn ich weiss nicht, wie ich, wenn ich die gl.h in mein Projekt per #include einbinde, meine eigenen gl...()-Funktionen deklarieren und definieren kann, ohne dass der Compiler sicher über eine Neudefinition beschwert.

Habt ihr ne Idee? Merci!

D-Swat
2004-06-28, 21:11:33
Hm, bin mir da auch nicht so schlüssig, was der richtige Weg ist.
Soll das in C oder C++ geschrieben werden?

Meine Lösungsansätze:
1) namespace
Pack die ganzen OpenGL deklarationen in einen namespace und ruf aus dem globalen namespaces die richtige Funktion auf.

namespace OpenGL
{
void glVertex3f(...);
}

void glVertex3f(...)
{
LogCall();
OpenGL::glVertex3f()
}


2) Alle OpenGL funktionen selbst implementieren und die eigentlichen OpenGL funktionen über Funktionszeiger aufrufen.


PFNGLACTIVETEXTUREARBPROC original_glActiveTextureARB= NULL;
void init()
{
original_glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) wglGetProcAddress("glActiveTextureARB");
}

void glActiveTextureARG(...)
{
LogCall();
original_glActiveTextureARB(...)
}



Hoffe es war was für dich dabei :)

MeLLe
2004-06-29, 07:57:32
Danke! Das sind zwei interessante Ansätze!

Allerdings hab ich mich inzwischen auf einen Weg begeben der einentlich funktionieren müsste - es aber leider nicht tut:


[...snip...]

#define GLI_API __declspec(dllexport)

[...snip...]

typedef unsigned int GLenum;
typedef void (*FP_GLBEGIN)(unsigned int);

[...snip...]

HINSTANCE h_GLInst;
FP_GLBEGIN h_glBegin;

h_GLInst = LoadLibrary("OPENGL32.DLL");
h_glBegin = (FP_GLBEGIN)GetProcAddress(h_GLInst, "glBegin");

[...snip...]

GLI_API void APIENTRY glBegin(GLenum mode)
{
MessageBox(NULL, "glBegin called.","GLI",MB_ICONINFORMATION|MB_OK);
(h_glBegin)(mode);
}

[...snip...]


Der Build der DLL wird ordnungsgemäß durchgeführt, ohne Warnungen oder Fehler. Leider meldete die Test-Anwendung (simple OpenGL-App) "Prozedureinsprungpunkt 'glMatrixMode' in Modul 'OPENGL32.DLL' nicht gefunden.". Danach hab ich nach obigem Schema einfach noch glMatrixMode() reingebastelt. Ohne Erfolg - die Meldung bleibt gleich.

Warum?

MeLLe
2004-06-29, 16:11:04
Nunja - wie auch immer ... inzwischen klappt es. Es fehlte nur ne .def-Datei im Projekt, die der Linker mit verwursten kann, und die wie folgt aussieht:


LIBRARY gli
EXPORTS
glBegin @1

[...snip...]


So long ...

Demirug
2004-06-29, 16:34:44
Wobei du bei der Def datei noch darauf achten soltest das du den einzelnen Funktion en die gleiche Ordinalnummer wie bei der Original DLL gibts.

BTW: Wenn das Programm einen Schutz gegen solche Layer-Dlls hat bist du mit dem Verfahren natürlich aufgeschmissen.

zeckensack
2004-06-29, 23:44:31
Du musst wglGetProcAddress benutzen. GL-Funktionen sind (zumindest theoretisch) abhängig vom GL-Kontext, an welchen HDC dieser gebunden ist, und auf welchem Monitor das zugehörige Fenster dargestellt wird.
Deswegen musst du eigentlich auch den Kontext kapseln, und das ganze muss thread-sicher sein (schau mal TlsAllocate auf MSDN nach, das sollte dir eine Idee bescheren).

Schau dir mal glTrace (http://www.hawksoft.com/gltrace/) an. Das ist quasi das Projekt, was du gerade versuchst zu schreiben, aber es ist schon fertig ;)

MeLLe
2004-06-30, 08:55:34
Danke für die Info, ihr beiden.

Demi: Ich weiss dass meine Lösung eventuell nicht die beste ist, aber es geht mir auch in erster Linie nicht soooo sehr um eine total ausgereifte Anwendung, sondern vielmehr einfach um ein kleines Tool, an dem ich mein Verständnis für die Interna von OpenGL und Windows erweitern kann. Lerning by doing sozusagen. Und wer weiss - vielleicht wirds ja am Ende doch ganz nützlich ;o)

Zecki: Danke für den Tip mit wglGetProcAddress - das wusste ich noch nicht. Aber ich werds beachten und einbauen. In Sachen TlsAlloc() habe ich mich grad kurz belesen, aber ich denke ich werde das erst implementieren, wenn ich sämtliche OpenGL-Basics in der DLL habe und es dann darum geht, die ganze Sache zu optimieren und zu verfeinern. Für's erste reicht mir ne einfache, wenn auch nicht so sehr sicher Lösung. Ich hatte ja versucht, APIHiJack (http://www.codeproject.com/dll/apihijack.asp) auf meine Bedürfnisse anzupassen - aber das bescherte mir die Situation, dass sich beim Start meiner OpenGL-Test-Anwendung mein PC zu einem spontanen Reboot entschieden hat. Und das war der Punkt, wo ich mir gesagt habe: das geht auch einfacher! ;D

Falls ihr trotzdem noch weitere Tips habt - immer her damit, ich bin dankbar ;)
Zum Beispiel wäre es cool, zu erfahren, wie man rausbekommt, welche Funktion welche Ordinalszahl in der originalen OpenGL32.dll hat ...

Demirug
2004-06-30, 10:07:11
Original geschrieben von MeLLe
Zum Beispiel wäre es cool, zu erfahren, wie man rausbekommt, welche Funktion welche Ordinalszahl in der originalen OpenGL32.dll hat ...

Dependency Walker (http://www.dependencywalker.com/) ist dein Freund.

MeLLe
2004-06-30, 11:24:32
Super Sache, das ;D
Danke!

Ein Problem ist grad aber noch aufgetaucht:

ich muss ja auch die wgl*-Funktionen implementieren und exportieren - leider verträgt sich das nicht ganz, weil ich ja <windows.h> und damit indirekt auch <wingdi.h> include - und genau in dieser sind die wgl*-Funktionen schon geklariert! Wie kann ich das umgehen?

MeLLe
2004-07-01, 13:47:18
Nunja - es gibt in der Zwischenzeit ein grundlegendes Problem:
Der Aufruf von

HMODULE GLDLL = LoadLibrary("...........opengl32.dll");

schlägt immer fehl - und zwar mit dem ErrorCOde 998 (ERROR_NO_ACCESS), was laut Microsoft bedeutet, dass die DLL nicht geladen werden kann, weil sie in schon geschützten/benutzen Speicher gemappt werden soll. Gibts da nen Trick, den Fehler zu beheben?

Demirug
2004-07-01, 14:00:10
Versuch es mal mit GetModuleHandleEx oder GetModuleHandle

MeLLe
2004-07-01, 14:51:49
Danke, Demi, aber das hilft leider nicht - GetModuleHandle() bricht mit ErrorCode 126 = MODULE_NOT_FOUND ab. Was ja auch logisch ist, denn wenn GetModuleHandle() erfolgreich sein soll, muss ja das Module zuvor schon per LoadLibrary() in den Speicher ggemappt worden sein ... Ein Teufelskreis, wie mir scheint ;)

Was mir allerdings noch viel komischer erscheint - jeglicher Aufruf von LoadLibrary(), egal mit welcher DLL als Parameter, schlägt fehl. Wie kann das sein? Ist da ne grundlegende Einstellung im Visual-Studio-Projekt falsch?

Xmas
2004-07-02, 02:52:10
Mal so ne richtig blöde Denkkette:
Du stellst deine OpenGL32.dll ins Applikationsverzeichnis, weil Windows beim Suchen der DLL erst in aktuellen Verzeichnis, dann im Systemverzeichnis sucht.
Deine OpenGL32.dll wird nun in den Speicher geladen und die DLLMain wird ausgeführt, welche eine DLL namens "OpenGL32.dll" öffnen und Pointer auf Funktionen darauf ermitteln soll. Im aktuellen Verzeichnis befindet sich nun eine Datei namens "OpenGL32.dll"...

Was nun?

del_4901
2004-07-02, 02:58:42
Original geschrieben von Xmas
Mal so ne richtig blöde Denkkette:
Du stellst deine OpenGL32.dll ins Applikationsverzeichnis, weil Windows beim Suchen der DLL erst in aktuellen Verzeichnis, dann im Systemverzeichnis sucht.
Deine OpenGL32.dll wird nun in den Speicher geladen und die DLLMain wird ausgeführt, welche eine DLL namens "OpenGL32.dll" öffnen und Pointer auf Funktionen darauf ermitteln soll. Im aktuellen Verzeichnis befindet sich nun eine Datei namens "OpenGL32.dll"...

Was nun?
ein unendl.Kreis .... vielleicht sollte er explizit im "$(windir)/system32" nach suchen ...

MeLLe
2004-07-02, 06:37:21
Original geschrieben von AlphaTier
ein unendl.Kreis .... vielleicht sollte er explizit im "$(windir)/system32" nach suchen ...

Das tue ich inzwischen auch - sonst gibts nen endless Loop, das ist klar soweit!
Aber den Fehler, der besteht leider immernoch. Ich glaub ich stell mal mein ganzes Projekt hier zur Verfügung ...Oder?

zeckensack
2004-07-02, 08:11:02
Ährm ... vielleicht liegt's daran:
"
The entry-point function should perform only simple initialization or termination tasks. It must not call the LoadLibrary or LoadLibraryEx function (or a function that calls these functions), because this may create dependency loops in the DLL load order. This can result in a DLL being used before the system has executed its initialization code. Similarly, the entry-point function must not call the FreeLibrary function (or a function that calls FreeLibrary), because this can result in a DLL being used after the system has executed its termination code.
"

Das steht in der Beschreibung von DllMain (http://msdn.microsoft.com/library/en-us/dllproc/base/dllmain.asp).

MeLLe
2004-07-02, 09:11:16
Original geschrieben von zeckensack
Ährm ... vielleicht liegt's daran:
"[snip]"

Das steht in der Beschreibung von DllMain (http://msdn.microsoft.com/library/en-us/dllproc/base/dllmain.asp).
Die Vermutung hatte ich auch schon - nur leider hab ich bisher noch keine andere Möglichkeit gefunden, die DLL in einer separaten Funktion zu initialisieren. Ich muss es in DllMain machen, wen ein Process die DLL lädt, sonst laufen ja die Weiterleitungen auf die originalen glFuncs nicht, sobald ne glFunc aufgerufen wird. Denn ich muss ja initialiseren, um die Fuktionszeiger zu bekommen.

Aber Moment...

Da fällt mir ein ... wenn ich ne globale Variable bInitDone nehme, und die in jeder Funktion checke ... und ggf. da dann beim ersten Aufruf initialisiere ... das wäre doch was!

Muss ich gleich mal testen!

MeLLe
2004-07-02, 09:55:40
Ok, gesagt getan - und mit etwas mehr Erfolg.

Die DLL wird nun erfolgreich geladen, und auch die Funktionszeiger krieg ich sehr schön geliefert.

Dummerweise kackt aber nach dem Init sofort die Testanwendung mit ner Speicherzugriffsverletzung ab ... Hmm ... Und im Debugger-CallStack find ich nur Speicheradressen, keine Funktionsnamen, anhand derer ich da was genaueres nachvollziehen könnte ... :(

Update:
Nachdem ich jetzt ein kleines Log eingebaut hab, weiss ich, dass die DLL abschmiert, sobald sie glEnable aufrufen will. Das ist die erste gl-Funktion in meiner Test-App, vorher wird per GLUT ein GL-Window erzeugt - und die Aufrufe da (alles wgl-Funktionen) laufen wunderbar. Komisch ...

zeckensack
2004-07-02, 11:43:21
Original geschrieben von MeLLe
Da fällt mir ein ... wenn ich ne globale Variable bInitDone nehme, und die in jeder Funktion checke ... und ggf. da dann beim ersten Aufruf initialisiere ... das wäre doch was!Noch'n Vorschlag:class
Context
{
public:
Context() { memset(this,0,sizeof(Context); }
public:
GLvoid (*glBegin)(GLenum);
<...>
};

Context context;
_______________

__declspec(dllexport) GLvoid APIENTRY
glBegin(GLenum ptype)
{
if (NULL==context.glBegin)
{
context.glBegin=wglGetProcAddress(<...>);
}
context.glBegin(ptype);
}
Threadsicher macht man das ganze btw indem man in einem TLS-Slot einen Zeiger auf die zum aktiven GL-Kontext gehörige "Context"-Struktur speichert.
Original geschrieben von MeLLe
Ok, gesagt getan - und mit etwas mehr Erfolg.

Die DLL wird nun erfolgreich geladen, und auch die Funktionszeiger krieg ich sehr schön geliefert.

Dummerweise kackt aber nach dem Init sofort die Testanwendung mit ner Speicherzugriffsverletzung ab ... Hmm ... Und im Debugger-CallStack find ich nur Speicheradressen, keine Funktionsnamen, anhand derer ich da was genaueres nachvollziehen könnte ... :(

Update:
Nachdem ich jetzt ein kleines Log eingebaut hab, weiss ich, dass die DLL abschmiert, sobald sie glEnable aufrufen will. Das ist die erste gl-Funktion in meiner Test-App, vorher wird per GLUT ein GL-Window erzeugt - und die Aufrufe da (alles wgl-Funktionen) laufen wunderbar. Komisch ... IMO höchstwahrscheinlich die falsche calling convention. GL nutzt __stdcall (bzw APIENTRY, aber das ist ein Makro, welches zu __stdcall aufgelöst wird).

MeLLe
2004-07-02, 12:06:45
Original geschrieben von zeckensack
Noch'n Vorschlag:
[snip]

Threadsicher macht man das ganze btw indem man in einem TLS-Slot einen Zeiger auf die zum aktiven GL-Kontext gehörige "Context"-Struktur speichert.

Tausend dank - ich werd wenn alles soweit erstmal klappt die Threading-Sicherheit angehen ...

Original geschrieben von zeckensack
IMO höchstwahrscheinlich die falsche calling convention. GL nutzt __stdcall (bzw APIENTRY, aber das ist ein Makro, welches zu __stdcall aufgelöst wird).
Kannst du Gedanken lesen? Hab grad die Projekteigenschaften in eben jenem Punkt abgeändert - und voilà, es geht! Danke dir!

MeLLe
2004-07-05, 10:27:41
Ok, nun denn - für alle die es wagen wollen hier (http://www.allyn-andreas.de/files/opengl32.dll) eine kleine Public Preview mit der Möglichkeit, in fast jeder OpenGL-App ein Wireframee-Rendering zu erzwingen, bzw. die Texturierung ein- und auszuschalten.

Kleine Anleitung:
* diese Datei (http://www.allyn-andreas.de/files/opengl32.dll) runterladen
* ins Anwendungsverzeichnis stellen (d.h. da, wo auch die zu startende .exe-Datei liegt)
* zum Umschalten zwischen Wireframe und Soild Rendering einfach [Einfg] drücken (dabei ist LineStippling per [Entf] umschaltbar)
* zum Umschalten zwischen Texturing und non-textured Rendering einfach [Pos1] drücken

Bug-Reports, Hinweise, Wünsche und dergleichen könnt ihr gleich hier posten. Merci!

Edit
Updated...

Chris Lux
2004-07-05, 15:31:12
Original geschrieben von zeckensack
Noch'n Vorschlag:class
Context
{
public:
Context() { memset(this,0,sizeof(Context); }
public:
GLvoid (*glBegin)(GLenum);
<...>
};

Context context;
_______________

__declspec(dllexport) GLvoid APIENTRY
glBegin(GLenum ptype)
{
if (NULL==context.glBegin)
{
context.glBegin=wglGetProcAddress(<...>);
}
context.glBegin(ptype);
}


die idee mit dem wglGetProcAdress funktioniert nicht für die normalen (opengl 1.0) kernfunktionen, für diese wird nur NULL zurückgeliefert. für alle anderen (z.b. glBindTexture) funktioniert dein vorschlag, jedoch für alle anderen muss GetProcAdress benutzt werden, leider.

Chris Lux
2004-07-06, 11:34:17
@melle

kannst du meine beobachtung bestätigen?

die idee mit dem wglGetProcAdress funktioniert nicht für die normalen (opengl 1.0) kernfunktionen, für diese wird nur NULL zurückgeliefert. für alle anderen (z.b. glBindTexture) funktioniert dein vorschlag, jedoch für alle anderen muss GetProcAdress benutzt werden, leider.

MeLLe
2004-07-06, 12:02:46
Original geschrieben von Chris Lux
@melle
kannst du meine beobachtung bestätigen?
Nunja, nicht. Noch nicht. Denn zur Zeit läufts eigentlich ganz sauber per GetProcAddress(), und die rund 340 Funktionen sind so implementiert. Falls es doch wiedererwarten Probleme geben sollte (Testet bitte, testet! ;)) werde ich wohl das ganze umsetzen, aber erstmal solls so bleiben, auch wenns nicht ganz Spec-konform ist.

Wo wir gerade beim Testen sind - kennt wer eine kleine feine OpenGL-App, die

a) im Fenster laufen
und
b) viele verschiedene Szenen, Optionen und Haste-nich-gesehn bieten

tut. Für Hinweise benutzen Sie bitte die Antwortfunktion dieses Forums ;)

MeLLe
2004-07-06, 15:21:10
Appdäijt (http://www.allyn-andreas.de/files/opengl32.dll)!
* jetzt NEU: mit FPS-Counter!
* allgemeine Fixes