Jump to content

Pathrewrite: Difference between revisions

From EDM2
No edit summary
Ak120 (talk | contribs)
No edit summary
Line 1: Line 1:
== Using the Innotek Libc pathrewrite function ==
== Using the Innotek Libc pathrewrite function ==
 
When porting application from unix flavours you might run into the problem that some parts are hardcoded into the application, most famous is of course '''/etc'''.
When porting application from unix flavours you might run into the problem that some parts are hardcoded into the application, most famous is ofcourse '''/etc'''.


Libc provides a interface to work around these problems without completely rewriting the application, and thus easying the maintenance for the porter.
Libc provides a interface to work around these problems without completely rewriting the application, and thus easying the maintenance for the porter.
Line 21: Line 20:


Libc will actualy do: fp = fopen('c:\mptn\etc/test.txt','b');
Libc will actualy do: fp = fopen('c:\mptn\etc/test.txt','b');
</pre>
</pre>


Line 27: Line 25:


First lets see what is documented inside the libc hooks.c file:
First lets see what is documented inside the libc hooks.c file:
<pre>
<code>
 
/** @page pg_hooks  Hooks
/** @page pg_hooks  Hooks
  *
*
  * Presently this is a simple feature to load and call dlls as specified in
* Presently this is a simple feature to load and call dlls as specified in
  * the LIBC_HOOK_DLLS environment variable.
* the LIBC_HOOK_DLLS environment variable.
  *
*
  *
*
  * @subsection  LIBC_HOOK_DLLS
* @subsection  LIBC_HOOK_DLLS
  *
*
  * The format is <dllname>@<export>[!<type>]. LIBC will load <dllname>,
* The format is <dllname>@<export>[!<type>]. LIBC will load <dllname>,
  * resolve <export>and call <export> according to the convention of <type>.
* resolve <export>and call <export> according to the convention of <type>.
  * Single of double quotes can be applied to the <dllname>. If multiple hooks
* Single of double quotes can be applied to the <dllname>. If multiple hooks
  * is desired, they shall be separated by the semicolon, space or tab character.
* is desired, they shall be separated by the semicolon, space or tab character.
 
  * Failure to parse this variable, load dlls and resolve functions as specified
* Failure to parse this variable, load dlls and resolve functions as specified
  * bye this variable will not prevent libc processes from starting. So, do NOT
* bye this variable will not prevent libc processes from starting. So, do NOT
  * count on it as a security measure.
* count on it as a security measure.
  *
*
  * @subsubsection  Types
* @subsubsection  Types
  *
*
  * The default type is 'generic' and means that the libc module handle and
* The default type is 'generic' and means that the libc module handle and
  * the address of __libc_Back_ldrSymbol will be passed.
* the address of __libc_Back_ldrSymbol will be passed.
  *
*
  * 'pathrewrite' is another type. The parameters here are the three
* 'pathrewrite' is another type. The parameters here are the three
  * __libc_PathRewrite functions. The function should
* __libc_PathRewrite functions. The function should
  *
*
  *
*
  * @subsubsection  Examples
* @subsubsection  Examples
  *
*
  *  set LIBC_HOOK_DLLS=prwhome.dll@_instprws!pathrewrite
*  set LIBC_HOOK_DLLS=prwhome.dll@_instprws!pathrewrite
  *  set LIBC_HOOK_DLLS=/usr/lib/prwhome.dll@_instprws!pathrewrite
*  set LIBC_HOOK_DLLS=/usr/lib/prwhome.dll@_instprws!pathrewrite
  *  set LIBC_HOOK_DLLS="c:\My Plugins\prwhome.dll"@_instprws!pathrewrite
*  set LIBC_HOOK_DLLS="c:\My Plugins\prwhome.dll"@_instprws!pathrewrite
  *  set LIBC_HOOK_DLLS=prwhome@_insthomeprws!pathrewrite prwhome@_instetcprws!pathrewrite
*  set LIBC_HOOK_DLLS=prwhome@_insthomeprws!pathrewrite prwhome@_instetcprws!pathrewrite
  */
*/
</code>
 
</pre>
 


so we need to create a DLL which export a function that sets the rewritten paths.
so we need to create a DLL which export a function that sets the rewritten paths.


== Creating an example ==
== Creating an example ==
So we want to creat a DLL which we place in the CONFIG.SYS with a line like:
So we want to creat a DLL which we place in the CONFIG.SYS with a line like:
<pre>
<code>
set LIBC_HOOK_DLLS=ecsprw.dll@_ecsInitPath!pathrewrite
set LIBC_HOOK_DLLS=ecsprw.dll@_ecsInitPath!pathrewrite
</pre>
</code>


This means 2 things.
This means 2 things.
Line 80: Line 74:


== The source files ==
== The source files ==
=== ecsprw.def ===
=== ecsprw.def ===
<pre>
<code>
LIBRARY ECSPRW INITINSTANCE TERMINSTANCE
LIBRARY ECSPRW INITINSTANCE TERMINSTANCE
DATA MULTIPLE
DATA MULTIPLE
EXPORTS
EXPORTS
_ecsInitPath
_ecsInitPath
</pre>
</code>
''note: the DESCRIPTION tag comes later''
''note: the DESCRIPTION tag comes later''


=== ecsprw.c ===
=== ecsprw.c ===
<pre>
<code>
#include <InnoTekLIBC/pathrewrite.h>
#include <InnoTekLIBC/pathrewrite.h>
#include <string.h>
#include <string.h>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
 
char                gszEtcTo[256];
char                gszEtcTo[256];
char                gszVARLOGTo[256];
char                gszVARLOGTo[256];
char                gszTMPTo[256];
char                gszTMPTo[256];
 
 
__LIBC_PATHREWRITE  gEtcRule =
__LIBC_PATHREWRITE  gEtcRule =
{
{
     __LIBC_PRWF_TYPE_DIR | __LIBC_PRWF_CASE_SENSITIVE,
     __LIBC_PRWF_TYPE_DIR | __LIBC_PRWF_CASE_SENSITIVE,
     "/etc",  4,
     "/etc",  4,
     &gszEtcTo[0], 0
     &gszEtcTo[0], 0
};
};
 
__LIBC_PATHREWRITE  gTMPRule =
__LIBC_PATHREWRITE  gTMPRule =
{
{
     __LIBC_PRWF_TYPE_DIR | __LIBC_PRWF_CASE_SENSITIVE,
     __LIBC_PRWF_TYPE_DIR | __LIBC_PRWF_CASE_SENSITIVE,
     "/tmp",  4,
     "/tmp",  4,
     &gszTMPTo[0], 0
     &gszTMPTo[0], 0
};
};
 
__LIBC_PATHREWRITE  gVARLOGRule =
__LIBC_PATHREWRITE  gVARLOGRule =
{
{
     __LIBC_PRWF_TYPE_DIR | __LIBC_PRWF_CASE_SENSITIVE,
     __LIBC_PRWF_TYPE_DIR | __LIBC_PRWF_CASE_SENSITIVE,
     "/var/log",  8,
     "/var/log",  8,
     &gszVARLOGTo[0], 0
     &gszVARLOGTo[0], 0
};
};
 
 
void ecsInitPath(int (*pfnAdd)(), int (*pfnRemove)(), int (*pfnRewrite)());
 
 
void ecsInitPath(int (*pfnAdd)(), int (*pfnRemove)(), int (*pfnRewrite)())
void ecsInitPath(int (*pfnAdd)(), int (*pfnRemove)(), int (*pfnRewrite)());
{
 
 
void ecsInitPath(int (*pfnAdd)(), int (*pfnRemove)(), int (*pfnRewrite)())
{
   int    rcRet = 0;
   int    rcRet = 0;
   strcpy(&gszEtcTo[0], getenv("ETC"));
   strcpy(&gszEtcTo[0], getenv("ETC"));
   strcpy(&gszVARLOGTo[0], getenv("LOGFILES"));
   strcpy(&gszVARLOGTo[0], getenv("LOGFILES"));
   strcpy(&gszTMPTo[0], getenv("TMP"));
   strcpy(&gszTMPTo[0], getenv("TMP"));
 
     gEtcRule.cchTo = strlen(gEtcRule.pszTo);
     gEtcRule.cchTo = strlen(gEtcRule.pszTo);
     gVARLOGRule.cchTo = strlen(gVARLOGRule.pszTo);
     gVARLOGRule.cchTo = strlen(gVARLOGRule.pszTo);
     gTMPRule.cchTo = strlen(gTMPRule.pszTo);
     gTMPRule.cchTo = strlen(gTMPRule.pszTo);
 
     if ((*pfnAdd)(&gEtcRule, 1))
     if ((*pfnAdd)(&gEtcRule, 1))
     {
     {
Line 151: Line 139:
     {
     {
         rcRet++;
         rcRet++;
     }
     }
 
}
}
</code>
</pre>


What do we see in the source file ??
What do we see in the source file ??


we declare some structures to hold the rewrite data:
we declare some structures to hold the rewrite data:
<pre>
<code>
__LIBC_PATHREWRITE  gVARLOGRule =
__LIBC_PATHREWRITE  gVARLOGRule =
{
{
     __LIBC_PRWF_TYPE_DIR | __LIBC_PRWF_CASE_SENSITIVE,
     __LIBC_PRWF_TYPE_DIR | __LIBC_PRWF_CASE_SENSITIVE,
     "/var/log",  8,
     "/var/log",  8,
     &gszVARLOGTo[0], 0
     &gszVARLOGTo[0], 0
};
};
</pre>
</code>
we tell that gVARLOGRule holds information for a case sensitive directory rewrite of: '/var/log' to the contents of variable gszVARLOGTo.
we tell that gVARLOGRule holds information for a case sensitive directory rewrite of: '/var/log' to the contents of variable gszVARLOGTo.


This variable is initialized in:
This variable is initialized in:
<pre>
<code>
   strcpy(&gszVARLOGTo[0], getenv("LOGFILES"));
   strcpy(&gszVARLOGTo[0], getenv("LOGFILES"));
</pre>
</code>
where we fill it whith the contents of the environment variable 'LOGFILES' ( e.g. C:\VAR\LOG )
where we fill it whith the contents of the environment variable 'LOGFILES' ( e.g. C:\VAR\LOG )


Line 178: Line 165:


this is how we call it:
this is how we call it:
<pre>
<code>
if ((*pfnAdd)(&gVARLOGRule, 1))
if ((*pfnAdd)(&gVARLOGRule, 1))
     {
     {
         rcRet++;
         rcRet++;
     }
     }
</pre>
</code>


== Compiling ==
== Compiling ==
compiling is very easy, make sure you have GCC 3.3.5 CSD1  
compiling is very easy, make sure you have GCC 3.3.5 CSD1  
<pre>
<code>
gcc -Zdll ecsprw.c ecsprw.def
gcc -Zdll ecsprw.c ecsprw.def
</pre>
</code>


Ofcourse this is a very easy example which we could easely extend with a administration program to maintain a database of which paths to rewrite.
Of course this is a very easy example which we could easely extend with a administration program to maintain a database of which paths to rewrite.


== Using pathrewrite in your executable ==
== Using pathrewrite in your executable ==
Pathrewrite functions can also be used inside your executable, without creating an external dll to handle them.
Pathrewrite functions can also be used inside your executable, without creating an external dll to handle them.


Line 202: Line 187:
Instead of modifying a lot of files, you can use __libc_PathRewriteAdd() to modify your program behaviour.
Instead of modifying a lot of files, you can use __libc_PathRewriteAdd() to modify your program behaviour.


<pre>
<code>
int os2_init_paths( void)
int os2_init_paths( void)
{
{
int rc;
int rc;
char gszFreshClamTo[_MAX_PATH];
char gszFreshClamTo[_MAX_PATH];
Line 235: Line 220:
// return
// return
return rc;
return rc;
}
}
</pre>
</code>


You need to create an array enough big to contain all your static redirection rules, fill the __LIBC_PATHREWRITE fields (either as static members or at runtime), and call __libc_PathRewriteAdd() where the first parameter is the address of your array and the second parameter is the number of items in the array.
You need to create an array enough big to contain all your static redirection rules, fill the __LIBC_PATHREWRITE fields (either as static members or at runtime), and call __libc_PathRewriteAdd() where the first parameter is the address of your array and the second parameter is the number of items in the array.


Call os2_init_paths() as the first line of main() and it will do the trick :-)
Call os2_init_paths() as the first line of main() and it will do the trick :-)


[[Category:Tools Articles]]
[[Category:Tools Articles]]

Revision as of 19:27, 5 April 2016

Using the Innotek Libc pathrewrite function

When porting application from unix flavours you might run into the problem that some parts are hardcoded into the application, most famous is of course /etc.

Libc provides a interface to work around these problems without completely rewriting the application, and thus easying the maintenance for the porter.

This interface is called pathrewrite, Let me try to explain how it works.

  • Through a environment setting you tell libc to load a DLL in which a pathrewrite function is available.
  • Libc loads this DLL on every load instance calls the specified entry point with 3 parameters
    1. Pathrewrite add function
    2. Pathrewrite remove function
    3. Pathrewrite modify function
  • with these parameters the function inside the user supplied DLL can alter LIBC internals which are responsible for Path rewriting.
    • Every path rewrite entry is a structure with some parameters, I will not go into them here.
  • Whenever LIBC encounters a function call which uses file names as parameters it will apply the rules of the pathrewriters
e.g. if a path rewriter is set to rewrite '/etc' to 'c:\mptn\etc' this is what will happen:

a program issues: fp = fopen('/etc/test.txt','b');

Libc will actualy do: fp = fopen('c:\mptn\etc/test.txt','b');

In this article I'll try to outline the posibilities from what I used myself and will give a example.

First lets see what is documented inside the libc hooks.c file:

/** @page pg_hooks  Hooks
 *
 * Presently this is a simple feature to load and call dlls as specified in
 * the LIBC_HOOK_DLLS environment variable.
 *
 *
 * @subsection  LIBC_HOOK_DLLS
 *
 * The format is <dllname>@<export>[!<type>]. LIBC will load <dllname>,
 * resolve <export>and call <export> according to the convention of <type>.
 * Single of double quotes can be applied to the <dllname>. If multiple hooks
 * is desired, they shall be separated by the semicolon, space or tab character.

 * Failure to parse this variable, load dlls and resolve functions as specified
 * bye this variable will not prevent libc processes from starting. So, do NOT
 * count on it as a security measure.
 *
 * @subsubsection   Types
 *
 * The default type is 'generic' and means that the libc module handle and
 * the address of __libc_Back_ldrSymbol will be passed.
 *
 * 'pathrewrite' is another type. The parameters here are the three
 * __libc_PathRewrite functions. The function should
 *
 *
 * @subsubsection   Examples
 *
 *  set LIBC_HOOK_DLLS=prwhome.dll@_instprws!pathrewrite
 *  set LIBC_HOOK_DLLS=/usr/lib/prwhome.dll@_instprws!pathrewrite
 *  set LIBC_HOOK_DLLS="c:\My Plugins\prwhome.dll"@_instprws!pathrewrite
 *  set LIBC_HOOK_DLLS=prwhome@_insthomeprws!pathrewrite prwhome@_instetcprws!pathrewrite
 */

so we need to create a DLL which export a function that sets the rewritten paths.

Creating an example

So we want to creat a DLL which we place in the CONFIG.SYS with a line like:

set LIBC_HOOK_DLLS=ecsprw.dll@_ecsInitPath!pathrewrite

This means 2 things.

  1. ecsprw.dll should be somewhere in the LIBPATH, lets put it in ecs\dll
  2. ecsprw.dll should export _ecsInitPath

The source files

ecsprw.def

LIBRARY ECSPRW INITINSTANCE TERMINSTANCE
DATA MULTIPLE
EXPORTS

_ecsInitPath note: the DESCRIPTION tag comes later

ecsprw.c

#include <InnoTekLIBC/pathrewrite.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

char                gszEtcTo[256];
char                gszVARLOGTo[256];
char                gszTMPTo[256];

__LIBC_PATHREWRITE  gEtcRule =
{
   __LIBC_PRWF_TYPE_DIR | __LIBC_PRWF_CASE_SENSITIVE,
   "/etc",  4,
   &gszEtcTo[0], 0
};

__LIBC_PATHREWRITE  gTMPRule =
{
   __LIBC_PRWF_TYPE_DIR | __LIBC_PRWF_CASE_SENSITIVE,
   "/tmp",  4,
   &gszTMPTo[0], 0
};

__LIBC_PATHREWRITE  gVARLOGRule =
{
   __LIBC_PRWF_TYPE_DIR | __LIBC_PRWF_CASE_SENSITIVE,
   "/var/log",  8,
   &gszVARLOGTo[0], 0
};

void ecsInitPath(int (*pfnAdd)(), int (*pfnRemove)(), int (*pfnRewrite)());

void ecsInitPath(int (*pfnAdd)(), int (*pfnRemove)(), int (*pfnRewrite)())
{
 int     rcRet = 0;
 strcpy(&gszEtcTo[0], getenv("ETC"));
 strcpy(&gszVARLOGTo[0], getenv("LOGFILES"));
 strcpy(&gszTMPTo[0], getenv("TMP"));

   gEtcRule.cchTo = strlen(gEtcRule.pszTo);
   gVARLOGRule.cchTo = strlen(gVARLOGRule.pszTo);
   gTMPRule.cchTo = strlen(gTMPRule.pszTo);

   if ((*pfnAdd)(&gEtcRule, 1))
   {
       rcRet++;
   }
   if ((*pfnAdd)(&gTMPRule, 1))
   {
       rcRet++;
   }
   if ((*pfnAdd)(&gVARLOGRule, 1))
   {
       rcRet++;
   }  
}

What do we see in the source file ??

we declare some structures to hold the rewrite data:

__LIBC_PATHREWRITE  gVARLOGRule =
{
   __LIBC_PRWF_TYPE_DIR | __LIBC_PRWF_CASE_SENSITIVE,
   "/var/log",  8,
   &gszVARLOGTo[0], 0
};

we tell that gVARLOGRule holds information for a case sensitive directory rewrite of: '/var/log' to the contents of variable gszVARLOGTo.

This variable is initialized in:

 strcpy(&gszVARLOGTo[0], getenv("LOGFILES"));

where we fill it whith the contents of the environment variable 'LOGFILES' ( e.g. C:\VAR\LOG )

When called by LIBC the ecsInitPath functions gets 3 parameters, the first is only used here, which is a function pointer, this pointer is to a function which extends the path rewrite table and it takes a structure as described above as parameter.

this is how we call it:

if ((*pfnAdd)(&gVARLOGRule, 1))
   {
       rcRet++;
   }

Compiling

compiling is very easy, make sure you have GCC 3.3.5 CSD1

gcc -Zdll ecsprw.c ecsprw.def

Of course this is a very easy example which we could easely extend with a administration program to maintain a database of which paths to rewrite.

Using pathrewrite in your executable

Pathrewrite functions can also be used inside your executable, without creating an external dll to handle them.

This allows you to access files (e.g. clamd.conf) in different places other than the hard-coded paths (e.g. /etc/clamd.conf), because many Unix programs have fixed names or coded when running then configure scripts, so they depend on user configuration.

Instead of modifying a lot of files, you can use __libc_PathRewriteAdd() to modify your program behaviour.

int os2_init_paths( void)
{

int rc; char gszFreshClamTo[_MAX_PATH]; char gszClamdTo[_MAX_PATH]; __LIBC_PATHREWRITE gClamRules[2] = { { __LIBC_PRWF_TYPE_FILE | __LIBC_PRWF_CASE_SENSITIVE, CONFDIR"/freshclam.conf", 0, &gszFreshClamTo[0], 0 }, { __LIBC_PRWF_TYPE_FILE | __LIBC_PRWF_CASE_SENSITIVE, CONFDIR"/clamd.conf", 0, &gszClamdTo[0], 0 } }; // map to new names strcpy( gszFreshClamTo, getenv("ETC")); strcat( gszFreshClamTo, "/freshclam.conf"); strcpy( gszClamdTo, getenv("ETC")); strcat( gszClamdTo, "/clamd.conf"); // adjust strings len gClamRules[0].cchFrom = strlen(gClamRules[0].pszFrom); gClamRules[1].cchFrom = strlen(gClamRules[1].pszFrom); gClamRules[0].cchTo = strlen(gszFreshClamTo); gClamRules[1].cchTo = strlen(gszClamdTo); // add a new mapping rule rc = __libc_PathRewriteAdd( &gClamRules[0], 2); // return return rc;

}

You need to create an array enough big to contain all your static redirection rules, fill the __LIBC_PATHREWRITE fields (either as static members or at runtime), and call __libc_PathRewriteAdd() where the first parameter is the address of your array and the second parameter is the number of items in the array.

Call os2_init_paths() as the first line of main() and it will do the trick :-)