• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2464
  • Last Modified:

Intercept Windows API Calls

I want to intercept an API function call.  I am writing a TCP/IP tunneling program and need to intercept all calls to winsock32.dll and then make those calls from with in my program.  I do not want to use any 3d party utilities (like SoftICE) or replace the winsock32.dll.  So how do I do this programmically?  What can I do in C/C++ or ASM to intercept these or any other API calls before windows calls the code in the approiate DLL?
I remember using a program called AutoSOCKS that captured the TCP/IP information and automatically entered necessary proxy information so it appeared that your system was directly connected to the internet.  They did not replace the winsock32.dll file to do this either.  
If needed, I will add more points to the question.
0
lordx
Asked:
lordx
  • 21
  • 8
  • 8
  • +4
1 Solution
 
duneramCommented:
Yes this is possible, but you are going to have to put your replacement API's in a DLL and then inject that DLL into any application that uses those apis.  Do you know which application uses the API's that you want to hook?

The reason I ask this is when a 32 bit program starts up, there is a giant table of imported api's that gets resolved.  Up successful loading of the table, you will have a function address for each api.  The trick is to have your DLL install itself in the target application, then replace the api function address in the table with the address of your replacement api (do this for each of them).  Then in your function you need to make (if you want to perform the real api function) the api call the original function address that you replaced.

Its kinda like this:
(example  these addresses are arbritary (not real))

API Jump table
SetForegroundWindow      0x000f0100  
.

Your dll gets mapped into the target process.  You want to hook the SFW api so you locate the function address in the table and replace it with the address of your function

MySetForegroundWindow starts at 0x07000300 (in this example)

API Jump table
SetForegroundWindow    0x07000300   (my address)

Now whenever the SFW api is called in that app, it will reference the jump table and call yours (in your dll).  You need to make sure you eventually invoke the original address of 0x000f0100 so that the original SFW is called.

I will post an answer in code in a few moments.
0
 
duneramCommented:
I've done this before and have my own homemade CD's with the code somewhere to do it.  I haven't found the CD yet, but I'll keep looking.  (i have hundreds of homemade cd's....)
0
 
chensuCommented:
0
Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

 
lordxAuthor Commented:
The winsock spy program is good but it requires me to replace the winsock32.dll.  This is something I did not want to do.  Duneram's idea is what I am looking for.
0
 
MadshiCommented:
Duneram's idea seems to be good, but (even if it really works :-) you would have do manipulate all applications you want to hook.
On "www.sysinternals.com" there is a sample named "regMonitor" (for Win95 and WinNT). This sample hooks system wide (!) all calls for registry. Perhaps you could hook winsock calls in the same way. (I'm not sure, it's only an idea.)
But then you would have to write drivers! :-(
0
 
duneramCommented:
I've done the api hook thing and when I get home I'll take a look again for my source
0
 
lordxAuthor Commented:
Adjusted points to 750
0
 
xyuCommented:
Here is exact answer for Your request...
The ZIP file with entire project I've put at: "http://www.geocities.com/SiliconValley/1741/miscprog/intercept.zip"

The listing is :

/****************************************************************************/
/** intercept.cpp                                                          **/
/** ---------------------------------------------------------------------- **/
/** Example of interception of an API or any DLL function call             **/
/** ---------------------------------------------------------------------- **/
/** The method shown here may be very impressive in conjunction with       **/
/** CreateRemoteThread API                                                 **/
/** ---------------------------------------------------------------------- **/
/** July 23, 1998 by Oleg Kagan                                            **/
/****************************************************************************/

/****************************************************************************/

//============================================================================
#include <windows.h>

// Switch all optimizations off
// (Visual C specific... For any other compiler do the same thing)
//============================================================================
#pragma optimize("", off)

//============================================================================
#define MakePtr(Type, Base, Offset) ((Type)(DWORD(Base) + (DWORD)(Offset)))

//============================================================================
BOOL InterceptDllCall(

      HMODULE hLocalModule,
      const char* c_szDllName,
      const char* c_szApiName,
      PVOID pApiNew,
      PVOID* p_pApiOrg,
      PVOID pApiToChange
      
){
    PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER)hLocalModule;
    PIMAGE_NT_HEADERS pNTHeader;
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
    DWORD dwProtect;
      BOOL bSuccess = FALSE;
   
    DWORD dwAddressToIntercept;

      if (pApiToChange) {
            dwAddressToIntercept = (DWORD)pApiToChange;
      }
      else {
            dwAddressToIntercept = (DWORD)GetProcAddress(
                  GetModuleHandle((char*)c_szDllName), (char*)c_szApiName
            ) /*GetProcAddress*/;
      } /*iff*/;

    if (IsBadReadPtr(hLocalModule, sizeof(PIMAGE_NT_HEADERS)))
        return FALSE;
   
    if (pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE)
        return FALSE;
   
    pNTHeader = MakePtr(PIMAGE_NT_HEADERS, pDOSHeader, pDOSHeader->e_lfanew);
    if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
        return FALSE;
   
    pImportDesc = MakePtr(
            PIMAGE_IMPORT_DESCRIPTOR, hLocalModule,
        pNTHeader->OptionalHeader.DataDirectory[
                  IMAGE_DIRECTORY_ENTRY_IMPORT
            ] /*pNTHeader->OptionalHeader.DataDirectory*/.VirtualAddress
      ) /*MakePtr*/;
   
    if (pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pNTHeader) return FALSE;
   
      while (pImportDesc->Name) {
            PIMAGE_THUNK_DATA pThunk;
   
            pThunk = MakePtr(
                  PIMAGE_THUNK_DATA, hLocalModule, pImportDesc->FirstThunk
            ) /*MakePtr*/;
   
            while (pThunk->u1.Function) {
                  if (DWORD(pThunk->u1.Function) == dwAddressToIntercept) {      
                        if (
                              !IsBadWritePtr(
                                    (LPVOID)pThunk->u1.Function, sizeof(DWORD)
                              ) /*!IsBadWritePtr*/
                        ){
                              if (p_pApiOrg)
                                    *p_pApiOrg = PVOID(pThunk->u1.Function);
                              (PDWORD)pThunk->u1.Function = (PDWORD)pApiNew;
                              bSuccess = TRUE;
                        }
                        else {
                              if (
                                    VirtualProtect(
                                          (LPVOID)(&pThunk->u1.Function), sizeof(DWORD),
                                          PAGE_EXECUTE_READWRITE, &dwProtect
                                    ) /*VirtualProtect*/
                              ){
                                    DWORD dwNewProtect;

                                    if (p_pApiOrg)
                                          *p_pApiOrg = PVOID(pThunk->u1.Function);
                                    pThunk->u1.Function = (PDWORD)pApiNew;
                                    bSuccess = TRUE;

                                    dwNewProtect = dwProtect;
                                    VirtualProtect(
                                          (LPVOID)(&pThunk->u1.Function), sizeof(DWORD),
                                          dwNewProtect, &dwProtect
                                    ) /*VirtualProtect*/;
                              } /*if*/
                        } /*iff*/
                  } /*if*/
                  pThunk++;
            } /*while*/
            pImportDesc++;
      } /*while*/

    return bSuccess;
} /*InterceptDllCall(HMODULE, const char*, const char*, PVOID,PVOID*,PVOID)*/

//============================================================================
void ChangeText(

      char* szText
      
){
      size_t nLength = strlen(szText);
      for (size_t i = 0; i < nLength; ++i) {
            szText[i] = char((i % 2) ? tolower(szText[i]) : toupper(szText[i]));
      } /*for (size_t i)*/
} /*ChangeText(char*)*/

//============================================================================
typedef int (*TMessageBoxFuncPtr)(HWND, LPCTSTR, LPCTSTR, UINT);
TMessageBoxFuncPtr p_fnMessageBoxOrg = NULL;

//============================================================================
int WINAPI MyMessageBox(

      HWND hWnd,          // handle of owner window
      LPCTSTR lpText,     // address of text in message box
      LPCTSTR lpCaption,  // address of title of message box
      UINT uType          // style of message box

){
      if (!p_fnMessageBoxOrg) return 0;
      ChangeText((char*)lpText); ChangeText((char*)lpCaption);
      int nResult = (*p_fnMessageBoxOrg)(hWnd, lpText, lpCaption, uType);
      return nResult;
} /*MyMessageBox(HWND, LPCTSTR, LPCTSTR, UINT)*/

//============================================================================
extern "C" int WINAPI WinMain(

      HINSTANCE hInstance,  // handle to current instance
      HINSTANCE /*hPrevInstance*/,  // handle to previous instance
      LPSTR szCmdLine,  // pointer to command line
      int /*nCmdShow*/  // show state of window

){
      char* c_szTitle = "API Call Interception";
      UINT uStyle = MB_OK | MB_ICONHAND | MB_SYSTEMMODAL;

      MessageBox(NULL, "Here it is normal                 ", c_szTitle, uStyle);

      // Lets Change it
      //----------------
      InterceptDllCall(
            hInstance, "user32.dll", "MessageBoxA",
            (PVOID)&MyMessageBox, (PVOID*)&p_fnMessageBoxOrg,
            NULL
      ) /*InterceptDllCall*/;

      MessageBox(NULL, "Beware of the mad hackers         ", c_szTitle, uStyle);

      InterceptDllCall(
            hInstance, "user32.dll", "MessageBoxA",
            (PVOID)p_fnMessageBoxOrg, NULL, (PVOID)MyMessageBox
      ) /*InterceptDllCall*/;

      MessageBox(NULL, "Here it is almoust normal again :)", c_szTitle, uStyle);

      return 0;
} /*WinMain(HINSTANCE, HINSTANCE, LPSTR, int)*/


0
 
xyuCommented:
Don't forget to check that DLL is loaded :)
0
 
duneramCommented:
hope that's what your looking for.  I am starting my first vacation in over 2 years...  so I won't be around for about a week.  I never found my source code but I know its on one of my cd's...


0
 
lordxAuthor Commented:
Your answer is almost exactly what I am looking for.  The code you submitted works great for API functions in DLL's of the current process but not other processes.  I need to intercept the API calls to other processes as well.  The top of the code lising refrences the CreateRemoteThread function which may work for some NT applications.  I need this to work on both NT and 95/98.  For example, if I have a program running that is displaying messageboxes (or calling any other API functions like "socket" or "connect" for winsock functions), I need this program to alter those messageboxes in the other program.  There are some places in the InterceptDllCall function that reference an HMODULE or HINSTANCE object but if I pass the hInstance of another window to the function it fails.


0
 
xyuCommented:
HMODULE or HINSTANCE here is not a window.... it is instance of the DLL (Module) I'm working now on program (NT service) that You will be able to configure to inject any specified DLL in all precesses runninng after the service started... than i You can use the DLL to intercept API calls (unforunately I can't put too much time on it now - I have deadline to release the version at place i'm working...) if You could wait ... in 1-2 weeks i plan to finish that server... if not - i can transfer to You GetAdmin (hehe) sources that doing something similar... and You can try to use it to build server by yourself.. if not to take general case it could be faster :)
0
 
agreenCommented:
xyu: i'm intersting in the GetAdmin sources. Can you send it to green@maxho.com ?
0
 
xyuCommented:
agreen:
 Wait for 1-2 weeks i'll release the service i talk about with sources...
0
 
agreenCommented:
xyu:
Ok, I'll be waiting. Please don't forget it and don't leave me with only the thing that your nick means :)
0
 
xyuCommented:
agreen: How do You know, what my nick is... (meanwile its a shame, but choose it while i was totally drunk but I don't want to change it  :) )

0
 
agreenCommented:
xyu: :))
So, am I right that your nick came from the russian language ?
Also I think that now you live in Israel and, probably, work at Mercury. This place isn't a chat room, so if you want to know the reasons for such conclusion just mail me. (green@MAXHO.com) :)
0
 
xyuCommented:
to lordx: OK... i have very nice dll to inject any custom dll into specified process and that the custom dll can use method described in my answer to intercept any of the API or other DLL calls... it works now under NT4 and it suppose to work under WIN98... so if You need it leave me a comment... so I'll publish it... (it called syringe.dll) Next week I plan to publish "plaguesrv.exe" that will be NT service to perform "global infection" :)
leave comments here to contact me
0
 
MadshiCommented:
xyu,

that sounds very interesting...
Would be very kind of you to eMail me your dlls, drivers, sources, or whatever (Mathias.Rauen@gmx.de).    :-)

Thanx a lot, Madshi.
0
 
xyuCommented:
Madshi... only after 850 points :)
0
 
MadshiCommented:
Ohhh that's bad!    :-(
I'll think about it...
0
 
xyuCommented:
Madshi... Good luck :)
0
 
lordxAuthor Commented:
Thanks.  I do need it.  Either publish it here or email me at lordx@wam.umd.edu.
0
 
xyuCommented:
lordx: do You want it as is or You can wait ro plaguesrv "Global Infector"? If You want it as is You have just to promise not to publish it until i'll release plaguesrv :) are You agree?
0
 
lordxAuthor Commented:
How about you send me what you have (source included I hope) so I can start integrating it into my program.  Once I see that it works I will grade the question so you get your points.  When you publish plaquesrv, email me a copy at lordx@wam.umd.edu.  I will not release my program that will use it until after you have published plaquesrv.
0
 
xyuCommented:
lords... i sent it to You alreade 2-3 days ago... How is it going....?
0
 
lordxAuthor Commented:
I am having a problem compiling the code with psapi.lib.  I am using Borland C++ 5.02 and it reports that the file (psapi.lib) has an "invalid OMF record, type 0x21".  The code u provided seems to compile fine, but I can't link samp1.  Since I am not using MS Development Studio (according to the make files you used that) I don't know for sure what format to compile each file to.  Currently I am doing this:
samp1_inj: samp1_inf.dll
syringe: syringe.dll
samp1: samp1.exe
I do realize that you had not completed the project to the point of publication when you sent it to me.  Maby I need to compile psapi.lib under Borland.  I have a version of psapi.cpp from the getadmin program but it didn't compile properly.  Where did u get the psapi.lib file you sent me?
I will try to post a comment later on if I get it working.  Do you have ICQ?  If so it may be easier to do an online chat to discuss the program.
0
 
xyuCommented:
lordx...
You can find psapi.dll at winnt\system32...
Use implib.exe utility from borland C to generate psapi.lib... (the version i sent to You is in VC5 lib format)...
don't forget to disable all optimizations...
Wait a second... i'll generate for You BC5.01 project...



0
 
lordxAuthor Commented:
Ok, I got it to compile and run.  (I had to use IMPLIB.EXE for Borland to convert psapi.dll
and syringe.dll to an importable library which created a psapi.lib and syringe.lib that I
could use).
How can I see that it is working?

It looks like I should see the "Hacked" messagebox when samp1_inj.dll is started but I don't.

So, I remarked the "ifdef NEVER" and "endif" statements in samp1_inj.cpp and syringe.cpp so it would always display those message boxes.
When I run samp1.exe:
- I get the "Attach" and "Detatch" messageboxs for samp1_inj.dll and syringe.dll linking to
samp1.exe.  
- I get the "Attach" message box for syringe.dll linking to notepad.exe.
- I don't see any changes in notepad.  I did a search in notepad for text that was not in
the window to have it display a search error messagebox, but the text was not modified.
- When I exit notepad I get the "Detatch" message for syringe.dll's link to notepad.exe

So it is attaching the DLL, but maby not running the HackMessageBox entry function that
makes the modifications.
0
 
xyuCommented:
lordx... my ICQ is 6478563 call me now....
and my email is ok@netvision.net.il

0
 
lordxAuthor Commented:
Thanks for the code.  It worked perfectly once I added the "_" to the HackMessageBox string in the call to InjectModule (in samp1.cpp).
If you could make the neccessary changes for it to work under win98 I would give you another 200 points.
0
 
xyuCommented:
lordx.. i'm working on it ....
0
 
lucidityCommented:
lordx/xyu, I have download this intercept demo and got it to work in 98. I was curious if any of you have gotten it to work for Winsock functions. I have tried to capture send but nothing yet.
0
 
lucidityCommented:
Another thing, This API call interception only seems to work locally within the app. Can this be adapted to catch ALL API calls?
0
 
xyuCommented:
lucidity... yes it can be adapted... and its easy... You just have to get SYRINGE.DLL written by me and use it :)
0
 
lucidityCommented:
And how shall I go about doing that? Are you willing to give me the source.. actually I don't need the source, just how to implement it, but the source would be nice. We could trade... I have some source for some stuff I think is cool and potentially useful:

RAW TCP/IP send accept....
Hide process from taskmanager....
Transparent windows like Norton Utilities....
Multi-Threaded Web Server....
File Monitor...

Pick one, Anyway, If it works for me I would owe you one, just name it. I need to be able to see and alter calls to winsock, I'm making a free fuzzy logic web page screener so I need to see the html text and what sites they are going to, maybe GetHostByName(). I would of course credit you for the code.

ciao,
Jason
0
 
xyuCommented:
lucidity: my E-mail is ok@netvision.net.il what about Yours?
0
 
lucidityCommented:
liquidy@geocities.com
0
 
lucidityCommented:
you got the right email but I haven't heard anything from you yet.... whats up?

0
 
MadshiCommented:
xyu,

I'm back again...  :-)

I still don't have 850 points. So I tried your little source example, converted it to Delphi and got it to work. Also now I'm able to inject other processes.
But there is one big problem: Your code works only with implicit loaded DLLs. Am I right? Is there a possibility to intercept calls to explicit loaded DLLs/functions, too? Of course I could just overwrite the DLL function with a jump to my function. But then I couldn't call the original function anymore...

Thanx for your answer...

Regards, Madshi.

P.S: You don't have to tell me here and now HOW it works. Of course I would be glad about that. I just want to know IF you know how to get this done. Then I'll post a question with some points (<850).  :-)
0
 
xyuCommented:
You can intrecept any API (e.g. DLL calls) there is an extendet sample that allows You to "infect" notepad.exe  :)

0
 
MadshiCommented:
xyu,

if you look at the "Import Table" of "notepad.exe", you'll see that notepad links all the DLLs implicitly. But please look at "Explorer.Exe". There the "Import Table" is empty. So the way your example works it just can't intercept api calls from the explorer. I've tested it. I've injected the explorer with my own little DLL, but your sources just don't find the api calls in the import table. The loops run through without finding something...
But I want to intercept the CopyFile (e.g.) api calls that the explorer does. So - if your extended sample does not use a completely different way of intercepting, it will not work! Or am I wrong?

Regards, Madshi.
0
 
xyuCommented:
Explorer.exe is not a problem... i'v intercepted MessageBox for test :) the intreception have to be performed for appropriate (or all) module :) it's easy.... more than that i'll release Plague package next month... so You'll be able to use it for free :)
0
 
MadshiCommented:
xyu,

that sounds good (especially that word "free" :-))...
Where can I get this Plaque package from? Will it come with sources?

Thank you,
regards, Madshi.
0
 
MadshiCommented:
xyu,

and me again...
Thanx for the tip with the modules. I only looked through the exe's import table. Now I'm scanning through all DLLs, too. And now your loops intercept "CopyFileA" in "Shell32.DLL". But somehow the explorer's copy actions are still not hooked... :-(
So the same question again: What if a process uses LoadLibrary/GetProcAddress directly ("explicit" linking)? Then the jump address is not in the locations you look through! Right?

BTW, in your sources is a little bug! Perhaps you corrected that already, perhaps not - because it has no serious effect. But it's not nice, though.

if ( !IsBadWritePtr( (LPVOID)pThunk->u1.Function, sizeof(DWORD)
) /*!IsBadWritePtr*/ ...

That's wrong! It should be like this:

if ( !IsBadWritePtr( (LPVOID)(&pThunk->u1.Function), sizeof(DWORD)
) /*!IsBadWritePtr*/ ...

Regards, Madshi.
0
 
xyuCommented:
I'm not sure it is a bug.... otherwise You will not be able to compile it... -> is not defined for void* :)

0
 
MadshiCommented:
xyu,

I AM QUITE SURE!  :-)
Nevertheless it works with this bug in most cases, too. This IsBadWritePtr statement tests a wrong pointer and thus returns always that the pointer is bad. If you look at the "dwProcess" value, returned from the VirtualQuery function (if it succeeds), you'll find that you have already read/write access. I moved the second VirtualQuery line directly under the first one. And it all still worked! Please take a closer look at that!

Regards, Madshi.

P.S: I'm not sure about that compiling issue, because I'm using Delphi. But it's clear somehow. You give in the address that is stored in ...u1.Function. But later you don't want to write the the address that is stored in ...u1.Function but you want to write to ...u1.Function itself.
0
 
xyuCommented:
ok... it accepted as a bug... sorry

0
 
xyuCommented:
Madshi, meanwile my mail is ok@netvision.net.il :)
See You :)
Good Luck :)
0
 
lucidityCommented:
I cannot get your syringe demo to work on anything but NT. is this right?
0
 
xyuCommented:
lucidity: yes it's right, because CreateRemoteThread existsonly on NT and Win98, ans VistualAllocEx works only with NT, nut now i'm in process of creating my own AllocRemote that will work on Win98 as well...
0
 
lucidityCommented:
keep me posted, if you get it to work let me know... If you need any help let me know.
0

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 21
  • 8
  • 8
  • +4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now