DLL importing/exporting - not as simple as it sounds...

I have a situation where there are 2 dlls. One (call it Fred.dll) contains some thrid-party code which I need to prevent programs calling directly. I need to create a DLL to replace Fred.dll which actually handles the calls and passes some of them through to the original Fred.dll.

What I've got at the moment is a situation where the Slave DLL imports some of the functions exported by the master, wraps them in a function wrapper of the same prototype and then exports this wrapper.

This is fine in cases where I know the exact prototype of the function. However, I also need to be able to pass calls through to the Master without knowing the full function prototype - prefably without having to write a wrapper function. I would have thought this should be possible, since the OS doesn't care about the parameters anyway, it just passes a function pointer - or am I wrong here as well?

Has any one got an idea of how I can solve this problem?
LVL 1
AJFlemingAsked:
Who is Participating?
 
MadshiConnect With a Mentor Commented:
The corruption is strange. Perhaps that's the reason, I'm not sure. I don't have any other idea. Well, you've seen in your trivial example that my suggestion basically works. So I don't know what to make better...   :-(
0
 
rwilson032697Commented:
How about allowing the app to call the functions you don't wrap directly?

If you are using early binding (ie: creating an interface unit which declares the functions as being external) then you can have one interface unit which is for the master DLL, minus the prototypes for the functions you are wrapping, and a second interface unit for the slave DLL, with the prototypes for the wrapped functions.

Does that help?

Cheers,

Raymond.
0
 
MadshiCommented:
You can use something like this:

procedure SomeFunction;
var p1 : pointer;
begin
  p1 := GetProcAddress(otherDllHandle, 'SomeFunction');
  asm
    JMP [p1]
  end;
end;

Regards, Madshi.
0
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

 
AJFlemingAuthor Commented:
Thanks for the responses guys.

Madshi, If I knew the prototype for the function declared, I could just create a wrapper function - as it is, I don't.

The problems arise with the re-exporting of functions. I have to export with the same prototype - as fas as I know - in order for applications to link to the DLL properly. What I really need to do is find a way to make GetProcAddress return the address of the function in the original DLL.

Raymond,

Same problem as above. I don't know the prototype for the functions I'm trying to export.

All,

I guess the main question has to be "does it matter that I don't know the prototype?" If I export all the functions I don't know the prototypes for as direct asm jumps to the addresses wrapped in zero-parameter procedures will it matter? - How is parameter handling passed?

cheers,

Adam...
0
 
rwilson032697Commented:
I see now. You have the problem of determining the prototype for a DLL function when you only have the DLL itself.

Unfortunately this is a recurring thread here and the answer is that you can't, in general, determine the parameters for a function in a DLL.

Cheers,

Raymond.
0
 
AJFlemingAuthor Commented:
Does it matter that I don't know the prototype though? As far as the DLL is concerned, functions are exported by address only so it should be possible to place a function in between the two which simply jsr[]s to the original version after completing it's own processing. BUT this does depend on how parameters are handled... Any ideas?
0
 
MadshiCommented:
Adam, my answer should just work fine for you!!

Simply declare the functions you don't know the prototype from without any parameters like this:

procedure SomeFunction;

And export it like this:

exports SomeFunction name 'SomeFunction' index 777;

And make your function look like what I've posted in my previous answer. TRY IT!! It will work...   :-)

The executables will import your dll and calling GetProcAddress they'll get YOUR function's address, but that doesn't matter. They'll jump to your function and your function will jump to the original dll, which knows how to handle the parameters. No problem at all... Just try it!

Regards, Madshi.
0
 
AJFlemingAuthor Commented:
Do I need to explicitly name the export?

I've tried it without using explicit naming, and it seems to be getting a bit further, I'm getting Access Violations in the Master DLL now though...

One other thing, do I need to/does it matter if, I declare all the functions as stdcall? Or is what's causing the problem?

cheers,

Adam...
0
 
MadshiCommented:
No, you don't need to export it by name. Just export it the same way as the master dll exports it.
stdcll or not stdcall should only make a difference if the function has parameters or a return value.
Could you please tell me the complete access violation text? And please the address of the exported function in your dll and the address of the original function in the master dll.
Thank you...

Regards, Madshi.

P.S: Hmmm... Perhaps it must be this way?

procedure SomeFunction;
var p1, p2 : pointer;
begin
  p1 := GetProcAddress(otherDllHandle, 'SomeFunction');
  p2 := @p1;
  asm
    JMP [p2]
  end;
end;

I'm always a bit confused with these assembler JMP calls...   :-)
0
 
MadshiCommented:
After thinking about it, please try the new SomeFunction (with JMP [p2]) first of all. I guess, it will work now...
0
 
AJFlemingAuthor Commented:
Tried it with the extra redirection - same problem.

The violations are coming up on a read of 60885574 then on 81010502

I beleive they occur in the functions that I've overriden... Checking now.

cheers,

Adam...
0
 
AJFlemingAuthor Commented:
Checked... I've converted everything to the assembler jump... And it still throws access violations. This carries on throughout the program....

Addresses :

60885574
be010371
270104c4

Any ideas?

Adam...
0
 
MadshiCommented:
Please tell me these infos:

GetModuleHandle(yourDll);
GetModuleHandle(masterDll);
GetProcAddress(yourDllHandle, 'SomeFunction');
GetProcAddress(masterDllHandle, 'SomeFunction');

And the first read access violation is at $60885574?

Regards, Madshi.
0
 
AJFlemingAuthor Commented:
ModuleHandle (Master)                     2359296              
ModuleHandle (Mine)                        1968898048        

GetProcAddress(Master,'Something')   $755COFE
GetProcAddress(Mine,'Something')      unobtainable...

I can't get the proc address from my DLL because the code falls over before I can get an identifiable function from the DLL... Although I have obtained some other calls before then...

cheers,

Adam...
0
 
MadshiCommented:
Oooohhh - ****!!

I know where the problem is...

What I suggested is basically correct, but the problem is that the code in the SomeFunction function in your dll does change some registers and that's most probably the reason of the exceptions.

Give me some minutes, I'll find a solution.

Regards, Madshi.
0
 
MadshiCommented:
Okay, here is the solution:

// this is the function in the master DLL
// you don't know the parameters of this function
function TestFunction(str: string) : boolean;
begin
  MessageBox(0, pchar(str), 'info', 0);
  result := true;
end;

// this is the function you should export
// the "nop" do nothing, they just reserve space
// so we can copy some assembler code to this place
procedure SomeFunction;
begin
  asm
    nop
    nop
    nop
    nop
    nop
  end;
end;

type
  // this is the code for an assembler jump call
  // this call does NOT change any registers!!   :-)
  TJumpInstruct = packed record
    opcode   : byte;     // $E9
    distance : integer;  // distance - 5
  end;

// this function fills the "nop" space with a JMP call to the original function
procedure PrepareSomeFunction;
var ji  : TJumpInstruct;
    dw1 : dword;
begin
  ji.opcode   := $E9;
  ji.distance := integer(@test) - integer(@SomeFunction) - 5;
  VirtualProtect(@SomeFunction, 5, PAGE_EXECUTE_READWRITE, @dw1);
  Move(ji, pointer(@SomeFunction)^, 5);
end;

initialization
  // all functions that you have no prototype for must be prepared
  PrepareSomeFunction;
end.

// now we test what we've done
// this is what the applications do
var TestFunctionVar = function (str: string) : boolean;

begin
  // we do something like GetProcAddress(yourDllHandle, 'SomeFunction');
  TestFunctionVar := @SomeFunction;

  // now we call it
  TestFunctionVar('hack!!');

  // And it really works!!

This assembler copying stuff is very ugly, but it's the only idea I had. I hope it's okay for you...   :-)

Regards, Madshi.
0
 
MadshiCommented:
P.S: You can test my code like this:

function TestFunction(str: string) : boolean;
begin
  MessageBox(0, pchar(str), 'info', 0);
  result := true;
end;

procedure SomeFunction;
begin
  asm
    nop
    nop
    nop
    nop
    nop
  end;
end;

type
  TJumpInstruct = packed record
    opcode   : byte;     // $E9
    distance : integer;  // distance - 5
  end;

procedure PrepareSomeFunction;
var ji  : TJumpInstruct;
    dw1 : dword;
begin
  ji.opcode   := $E9;
  ji.distance := integer(@test) - integer(@SomeFunction) - 5;
  VirtualProtect(@SomeFunction, 5, PAGE_EXECUTE_READWRITE, @dw1);
  Move(ji, pointer(@SomeFunction)^, 5);
end;

var
  TestFunctionVar : function (str: string) : boolean;

begin
  PrepareSomeFunction;
  TestFunctionVar := @SomeFunction;
  TestFunctionVar('hack!!');
end.
0
 
AJFlemingAuthor Commented:
Hmmm. I _think_ I see what you're trying to do... The instant problem I can see is that I have no idea how much space to reserve for the 'hidden' function.. Or am I misunderstanding what you're doing...?

Also, I need to perform some other tasks within the function which I export... How does this affect the 'nop's?

cheers,

Adam...
0
 
AJFlemingAuthor Commented:
Actually, scrub that... I've figures out what you're doing... I'll give it a go and get back to you...

cheers,

Adam...
0
 
MadshiCommented:
Hi Adam,

well, I put in 5 nops there, because the jump instruction exactly needs 5 bytes.

If you need to do something in this function, it will get even more complicated. Please try it without adding anything first. If it you get it running, then we'll think about how to add something.

Regards, Madshi.
0
 
MadshiCommented:
P.S: Going home now. See you tomorrow...
0
 
AJFlemingAuthor Commented:
Yep, hopefully I'll have chance to have a look at the problem by then...

Cheers,

Adam...
0
 
RadlerCommented:
Spying...
0
 
AJFlemingAuthor Commented:
Morning folks... Quick qestion madshi - where does the variable test come from in

  ji.distance := integer(@test) - integer(@SomeFunction) - 5;

??

cheers,

Adam...
0
 
MadshiCommented:
Ooops... sorry... it was meant to be "TestFunction"...   :-)
0
 
AJFlemingAuthor Commented:
Ok, trying it out now... I'll get back to you in a while...

cheers,

  Adam...
0
 
AJFlemingAuthor Commented:
Right, well it appears to work for a trivial example... I'm going to try it with something non-trivial now. I'll get back to you asap.

cheers,

   Adam...
0
 
AJFlemingAuthor Commented:
OK, unfortunately the non-trivial example falls over... I think this is more to do with the code that's calling it to be honest. That and the fact that the DLL I'm protected is OpenGL32.dll. I suspect that there is some crosstalk going on between OpenGL32.dll and the GDI which is casuing the whole thing to fall over. That and when I look at the DLL I create using Quickview it shows the import table as being corrupted...

Any ideas as to whether this corruption is relevant or what is causing it?

cheers,

Adam...
0
 
AJFlemingAuthor Commented:
Cheers for all the help anyway. I dare say the passthrough technique will be useful elsewhere...

cheers,

Adam...
0
All Courses

From novice to tech pro — start learning today.