About Dynamic Link Libraries

All questions regarding Windows programming, post here. API,COM, ActiveX, DirectX, OpenGL, MFC and so on...

Moderators: Darobat, RecursiveS, Dante Shamest, Bugdude, Wizard

About Dynamic Link Libraries

Postby North » Thu May 15, 2008 1:49 pm

Hi again, Ive recently become interested in Dlls, specially in Window Subclassing and Dll Injection. I found some good codes like the Extended Task Manager in code-project (I think it was there), and I also checked some books that talked about Dlls.

But I didnt really find much information about it, it was all more or less the same (a basic Dll that pops up a MsgBox when Injected or executed), so I decided to come here to ask:

Is there any good tutorial or book that you would specially recommend me? My C level is pretty good, and my Windows APIs level, well Id say its medium although Im working a lot with Windows APIs lately, and what I need is like a whole book that talks about Dll creation, injection, interaction, etc...Not just a good book (like Programming Windows which I own) that has one or two small chapters about them..

Also it would be good if the book worked with C instead of C++ :P

Thanks a lot for your time ^^


[edit] Some typos
User avatar
North
 
Posts: 15
Joined: Tue Apr 15, 2008 7:15 am

Postby ventsyv » Thu May 15, 2008 3:58 pm

No need for a book, you need to understand how DLLs operate.
Lets say you have a class with useful code :
Code: Select all
namespace SecretUserAuthorizationAlgorithm
{
class AuthorizeUsers
{
   public:
       bool isAuthorized (string userName, string pass)
       {return false; }
}
}

Now, lets say you have many executable who need to be running this code.Instead of compiling the code into each executable (by adding the source code into each project) you have the option to compile it into a separate module, a DLL.

When you compile the above mentioned class as a Dll, the compiler creates the same binary object as if you compiled it as a part of executable file, however a Dll interface is being added to it (basically a few extra functions that load this object into memory and etc.).

Now, you obviously can't run this Dll as you would've ran a normal executable (it does not have a main() by the way) as you have to create an instance of this class first.
What you can do is, you can reference this file in other executables:
Code: Select all
relyOn UsersAuthorize.dll ;//in reality taken care of by the linker
//you have to add a "reference" to the dll.
using SecretUserAuthorizationAlgorithm
int main()
{
   AuthorizeUsers accessGuard;
   
   if (accessGuard.isAuthorized("user", "pass"))
   {
       AcessArea51();
   }
   else
       cout<<"Please remain call. FBI is on its way to your residence.";
   
   return 0;
}


What happens is that the executable has been told that such class exists and what that class' public functions are, so when the executable is running, it would attempt to run that function.
The way this work is as follows: some extra code is added to your exec, so that the first time you reference something that resides in a dll, that extra code will look in memory to see if the dll is loaded. If it is, it will create an object and everything is fine.
If the dll is not loaded, the OS will look for it and attempt to load it (it looks first in the local folder). If the DLL is not found anywhere you get BSD (blue screen of Death, not the OS) (in Win98, XP is better when it comes to that.)

Why would you use a Dll ?? Well, it gets loaded in memory only once, so you save memory. Also your code is more modular. You can change the implementation and recompile only the dll, not the whole app (as long as you keep the public functions the same).

Now, about Dll, injection: someone could write a dll thats the same as the original one, but change it so that it isAuthorized() always returns true. Then, the attacker will replace the legit dll with the malicious one and that dll will be loaded and called instead.
Then the Ruskies will get access to Area51 and steal all our captured alien space ships :biggun: :lol:

Now this is over simplification of course, dll could be signed with a key that would prevent such injection (keys of all executable and binaries must match), but thats the general idea... :roll:
User avatar
ventsyv
 
Posts: 2810
Joined: Mon Sep 22, 2003 5:25 pm
Location: MD USA

Postby North » Fri May 16, 2008 7:25 am

Thanks a lot for your great (and funny:P) explanation, finally I got to do a small dll injection, it injects the Dll into Windows calculator and when you press "CE", number 3 gets pressed instead of it, but I still have a problem.

Seems like its doing it too slowly or something, because if I press "CE" too quickly, the button 3 isnt pressed instead of it :S This is the code, I know its horrible so please correct it and tell me what Im doing wrong :P

Code: Select all
#include "stdafx.h"

DWORD WINAPI Main();
LRESULT CALLBACK NewWndProc(HWND,UINT,WPARAM,LPARAM);

LONG OldWndProc;

BOOL APIENTRY DllMain (HINSTANCE hInst,   
                       DWORD reason,       
                       LPVOID reserved)   
{
    switch (reason)
    {
      case DLL_PROCESS_ATTACH:
        CreateThread(0, NULL, (LPTHREAD_START_ROUTINE)&Main, NULL, NULL, NULL);
      break;

      case DLL_PROCESS_DETACH:
        break;

      case DLL_THREAD_ATTACH:
        break;

      case DLL_THREAD_DETACH:
        break;
    }

    return TRUE;
}

DWORD WINAPI Main()
{
      HWND hand1=FindWindow(NULL, "Calculator");
      HWND hand2=FindWindowEx(hand1, 0, NULL, "CE");

      OldWndProc=SetWindowLong(hand2,GWL_WNDPROC,(long)NewWndProc);

      ExitThread(0);
     return 0;
}

LRESULT CALLBACK NewWndProc(HWND hWnd,UINT Message,WPARAM wParam,LPARAM lParam)
{
  switch(Message)
  {
    case WM_LBUTTONDOWN:

      HWND hand1=FindWindow(NULL, "Calculator");
      HWND hand2=FindWindowEx(hand1, 0, NULL, "3");

      SendMessage(hand2, WM_LBUTTONDOWN, 0, 0);

      return CallWindowProc((WNDPROC)OldWndProc,hWnd,NULL,wParam,lParam);                                     
   break;
  }

  return CallWindowProc((WNDPROC)OldWndProc,hWnd,Message,wParam,lParam);
}
User avatar
North
 
Posts: 15
Joined: Tue Apr 15, 2008 7:15 am

Postby MXP » Fri May 16, 2008 11:53 am

Things would probably be faster if you stored the HWNDs of the windows you're concerned about instead of calling FindWindow[Ex]() every time you needed them.

Also, never cast a function pointer. If the compiler complains it's because something is very wrong. The only point in your code where you should be casting a function pointer is in your call to SetWindowLong() but even this should be modified: SetWindowLong() is obsolete as it does not support 64-bit computing, switch to SetWindowLongPtr() instead.

Finally, it is unnecessary to create a new thread to run Main(). You can simply call Main(). In either case, the call to ExitThread() is also unnecessary (as when the thread function finishes the thread is automatically exited).
Need information on a function I've posted? Chances are it's at the MSDN.
MXP
 
Posts: 6506
Joined: Mon Sep 22, 2003 5:27 pm

Postby North » Fri May 16, 2008 12:41 pm

I thought that if I didnt start a new thread the program would mess up since my new thread would be executed instead of the programs one :S

[edit]

I was modifying it when I realised that speed is not the problem! Its not that is messes up if I click CE to quickly, its like sometimes it doesnt click correctly 3 number, maybe I shouldnt use WM_LBUTTONDOWN?

Code: Select all
#include "stdafx.h"

DWORD WINAPI Main();
LRESULT CALLBACK NewWndProc(HWND,UINT,WPARAM,LPARAM);

LONG OldWndProc;

HWND Calculator;

BOOL APIENTRY DllMain (HINSTANCE hInst,   
                       DWORD reason,       
                       LPVOID reserved)   
{
    switch (reason)
    {
      case DLL_PROCESS_ATTACH:
        Main();
     break;
    }

    return TRUE;
}

DWORD WINAPI Main()
{
      HWND hand1=FindWindow(NULL, "Calculator");
      HWND hand2=FindWindowEx(hand1, 0, NULL, "CE");
    
     Calculator=FindWindowEx(hand1, 0, NULL, "3");

      OldWndProc=SetWindowLong(hand2,GWL_WNDPROC,(long)NewWndProc);

     return 0;
}

LRESULT CALLBACK NewWndProc(HWND hWnd,UINT Message,WPARAM wParam,LPARAM lParam)
{
  switch(Message)
  {
    case WM_LBUTTONDOWN:
      SendMessage(Calculator, WM_LBUTTONDOWN, 0, 0);

      return CallWindowProc((WNDPROC)OldWndProc,hWnd,NULL,wParam,lParam);                                     
   break;
  }

  return CallWindowProc((WNDPROC)OldWndProc,hWnd,Message,wParam,lParam);
}


By the way I cant use SetWindowLongPtr (I guess because Im on MSVC 6), and I dont really understand what you mean by "casting pointers" :$
User avatar
North
 
Posts: 15
Joined: Tue Apr 15, 2008 7:15 am

Postby MXP » Fri May 16, 2008 3:38 pm

Try handling the BN_CLICKED notification instead of WM_LBUTTONDOWN

As for function pointers:
OldWndProc and NewWndProc are pointers to functions. The compiler knows exactly what type of function they point to. Casting overrides any knowledge the compiler might have - in essence casting is the same as telling the compiler "No no, you only think you know what it is but you're wrong and I know what it really is". In the case of function pointers you should never have to second guess the compiler.

It's ok to cast NewWndProc to a long when you call SetWindowLong() because SetWindowLong() is only storing the value of the pointer and will hand it back to you unchanged later.

Well, actually, SetWindowLong() might hand you something different from what you're expecting if your program is running on a 64-bit computer. longs are 32 bits on both 32-bit and 64-bit computers but pointers are 32 bits on 32-bit computers and 64 bit on 64-bit computers. If you cast NewWndProc to long on a 64-bit computer you are going to lose half of the data of the pointer and so when you actually call NewWndProc your program will probably crash. SetWindowLongPtr() fixes this problem by storing things that are the size of a pointer.

It's not ok to cast OldWndProc to WNDPROC when calling CallWindowProc() because you are going to be calling OldWndProc. If it is not the exact type of function CallWindowProc() expects your program will probably crash (best case). Or it might malfunction in ways that are the stuff of nightmares.

Finally, MSVC 6 is 10 years old. A lot has happened since it came out and (much) better free alternatives are available. For example, MSVC++ Express 2008.
Need information on a function I've posted? Chances are it's at the MSDN.
MXP
 
Posts: 6506
Joined: Mon Sep 22, 2003 5:27 pm

Postby North » Sat May 17, 2008 8:52 am

Thanks Collin, Im downloading MSVC++ Express 2008 at the moment, I tried using BN_CLICKED instead and it didnt work either, also when checking messages received by the button in Spy++ it does never receive a BN_CLICKED message, it receives WM_LBUTTONDOWN instead.

Any other ideas? Im for sure out of them :s
User avatar
North
 
Posts: 15
Joined: Tue Apr 15, 2008 7:15 am

Postby MXP » Sat May 17, 2008 12:39 pm

BN_CLICKED should come in the form of the WM_NOTIFY message not as an actual value of the variable Message.
Need information on a function I've posted? Chances are it's at the MSDN.
MXP
 
Posts: 6506
Joined: Mon Sep 22, 2003 5:27 pm


Return to Windows Programming

Who is online

Users browsing this forum: No registered users and 2 guests