Login Register






Thread Rating:
  • 0 Vote(s) - 0 Average


Tutorial Making a simple plugin system for your application filter_list
Author
Message
Making a simple plugin system for your application #1
Ok, so since @"Pikami" has been talking a lot about back end exploitation and patching at the source code level, I figured I'd make a quick tutorial on how to do the same sorts of things for non-nefarious purposes.

To do this, I'll be using a very simple project I made called HTTPProxy. You can find the github source code for that project by following the link.

To follow along, go ahead and clone the repository
Code:
git clone git://github.com/phyrrus9/HttpProxy.git
cd HttpProxy
git checkout ffabe4e

That checkout line is important!

So what does HTTPProxy do?
Well, it's a reverse proxy that works based on HTTP/1 by examining the requested vhost domain in the packet and forwarding it to the correct device. To do this, it uses a database, but that database is local to this program. What if we wanted to make it be able to use a different database? Like maybe we use the local database as a cache but we want to run something like this in a cluster (which it is set up to do). Well, we could rewrite the resolving mechanism to figure this out, or we could write a plugin system that would scan a directory for modules and load them at runtime to try to resolve this new stuff.

How would we go about doing that?
Let's start by cloning the repository and getting some edits made.
[Image: PHTK79p.png]

Ok, so to compile this you will need the standard toolchain (gcc/llvm, make, etc). But first, let's go ahead and edit our hosts file to include a couple fake domains:
[Image: UjUzC71.png]
I am doing this in the hosts file because I don't actually own these domains. In a real life application of this program, you would set your A records for all of the domains to be the server running HTTPProxy (not the servers running Apache).

Ok, now we're going to edit the static table in HTTProxy. This is in function main, in main.c. We're only going to add test1.fake to this (and we're going to give it an IP address of 1.1.1.1) just so its easy to find.
[Image: K8imCcx.png]

alright, now let's go ahead and build it.
[Image: OGE5HX1.png]
and run it
[Image: hTcEG9A.png]

Now, I'm going to switch to a second terminal window and I'll do a wget on test1.fake. It will fail (because that IP wont have that vhost), but we should see the debug info for resolving it.
So, I had 3 issues: 1, I didn't have wget installed, so you should install that, and 2, we aren't running correctly, and we don't have the resolution debug stuff in there
[Image: BitpCF9.png]
[Image: Us8sQTa.png]
So, let's fix the resolution stuff too:
Replace the entire determine_request method with the following (this just makes it shorter)
Code:
char *determine_request(char *buffer, ssize_t len)
{
       char *ip;
       char *host;
       char *addname;
       char *addip;
       uint8_t tmp[4];
       struct host_s *ptr;
       ip = NULL;
       host = find_host(buffer, len);
       printf("Debug: %s\n", host);
       for (ptr = hosts; ptr != NULL && strcmp(ptr->hostname, host); ptr = ptr->next);
       if (ptr)
               ip = strdup(ptr->ip);
       printf("\tIP: %s\n", ip);
       return ip;
}

ok, now let's build and run it again:
[Image: BJMz1GR.png]
[Image: BlQ6RiJ.png]

Cool, so it worked. Now, let's try it with test3.fake
[Image: 6jYji2k.png]
[Image: HlF4Foq.png]

This tells us that it's all working fine. So, let's go ahead and kill our program and start working on our module loader:
Create a file named plugin.h
Code:
#ifndef PLUGIN_H
#define PLUGIN_H

#define PLUGIN_DIR "./"

char *resolve_plugin(const char *);

#endif

and a file plugin.c
Code:
#include <plugin.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <dirent.h>
char *resolve_plugin(const char *host)
{
       void *dlyd; // a pointer to our dylib
       char *(*plugin)(const char *); //a pointer to our plugin's entry point
       DIR *dp; // a pointer to our directory
       struct dirent *entity; // will hold information about our directory entity
       char *fp; // buffer to our file path
       struct stat fpstat; // allows us to stat a file to determine if it's what we're looking for
       char *retval; // return value from plugin

       retval = NULL; // initialize retval
       dp = opendir(PLUGIN_DIR); // open current running directory
       if (dp != NULL) // this should never fail, unless we don't have permissions
       {
               while ((entity = readdir(dp)) && retval == NULL) // read loop for all entries in directory, end if found
               {
                       if (strstr(entity->d_name, ".vx") != NULL) // if the file has ".vx" in its name
                       {
                               fp = malloc(strlen(PLUGIN_DIR) + strlen(entity->d_name) + 8); // allocate our buffer
                               *fp = 0; // set a null for security
                               strcpy(fp, PLUGIN_DIR); // copy base path
                               strcpy(fp + strlen(fp), entity->d_name); // copy file name
                               stat(fp, &fpstat); // get file statisticts
                               if (fpstat.st_mode & (S_IFREG | S_IRUSR | S_IXUSR)) // regular file with read and execute permissions
                               {
                                       dlyd = dlopen(fp, RTLD_LOCAL | RTLD_LAZY); // open the library
                                       *(void**)(&plugin) = dlsym(dlyd, "resolve"); // find the resolve function
                                       if (plugin != NULL) // if we found it
                                               retval = (*plugin)(host); // send data to plugin and record result
                                       dlclose(dlyd); // close our handle to the plugin
                               }
                               free(fp); // clear our buffer for next run
                       }
               }
               closedir(dp);
       }
       return retval;
}


Ok, now we need to make 2 changes in Makefile to get our code to be included:
Code:
LIBS = -pthread

<change to >

LIBS = -pthread -ldl

Code:
items = util main db

<change to>

items = util main db plugin

Ok, now make clean and make
[Image: MSo9Wq6.png]

Perfect. Now let's go ahead and add that in to our resolving function
[Image: sYYETxv.png]
modify your function so it looks like this, and also add
Code:
#include <plugin.h>
at the top of main.c

Go ahead and build it again. Right now, absolutely nothing has changed, and we're done with this program. We shouldn't need to make any more edits to it. Now, create yourself a folder called plugins and create a file called static.c
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *resolve(const char *host)
{
       printf("\t--In plugin static\n");
       if (!strcmp(host, "test2.fake"))
               return "2.2.2.2";
       return NULL;
}
and save that file. Let's build our plugin now
[Image: uXkFDwu.png]

And lets install it and change to our run directory, time to test
[Image: mRmE1gv.png]

[Image: SJhKK1F.png]
as you can see, it did load our plugin, but we had a segmentation fault, so I'm going to track that down real quick and report back. It's probably a missing strdup call.
Yep, it turns out we do need a strdup call. I simply wrapped the line to our plugin.
Code:
retval = (*plugin)(host); // send data to plugin and record result
is now
Code:
retval = strdup((*plugin)(host)); // send data to plugin and record result

Code:
make clean
make
and test it again
[Image: HPaXNrH.png]

Now, I'm going to go through and strip all of the debug stuff out of my plugin, and then I'll push this to github for you to browse through or use it elsewhere.

Notes: I only did a simple plugin system, which only had 1 feature that used plugins, but you could expand this as much as you want. A plugin doesn't just have to work on 1 feature either, I designed this so that every feature would have an entry point, and the program uses that entry point to resolve the plugin. You could put more of those functions inside the same plugin to create a "plugin library", and that's exactly what you should do.

[+] 1 user Likes phyrrus9's post
Reply

RE: Making a simple plugin system for your application #2
Thankyou for the tutorial. When I find the time I will follow along and see how it turns out in practice.
The stories and information posted here are artistic works of fiction and falsehood. Only a fool would take anything posted here as fact.
[Image: ouxiEMk.png]

Reply

RE: Making a simple plugin system for your application #3
(02-01-2018, 10:56 PM)ApatheticEuphoria Wrote: Thankyou for the tutorial. When I find the time I will follow along and see how it turns out in practice.

As you should see from my mistakes, I wrote this one up exactly as I was doing it. Would be nice if one of you wrote me a mysql plugin for it though Tongue

Reply

RE: Making a simple plugin system for your application #4
I love the tutorial!
I know this knowledge will help me someday, thanks!

(02-01-2018, 10:59 PM)phyrrus9 Wrote: Would be nice if one of you wrote me a mysql plugin for it though Tongue
Challenge accepted and completed Smile
Would you mind if I made a pull request to your repo adding my created mysql_resolve plugin?

Reply

RE: Making a simple plugin system for your application #5
(02-03-2018, 05:56 PM)Pikami Wrote: I love the tutorial!
I know this knowledge will help me someday, thanks!

(02-01-2018, 10:59 PM)phyrrus9 Wrote: Would be nice if one of you wrote me a mysql plugin for it though Tongue
Challenge accepted and completed Smile
Would you mind if I made a pull request to your repo adding my created mysql_resolve plugin?

Go right ahead. I might make some modifications to it though, fair warning.

Reply

RE: Making a simple plugin system for your application #6
(02-03-2018, 07:36 PM)phyrrus9 Wrote:
(02-03-2018, 05:56 PM)Pikami Wrote:
(02-01-2018, 10:59 PM)phyrrus9 Wrote: Would be nice if one of you wrote me a mysql plugin for it though Tongue
Challenge accepted and completed Smile
Would you mind if I made a pull request to your repo adding my created mysql_resolve plugin?
Go right ahead. I might make some modifications to it though, fair warning.

I just opened my pull request.

Reply

RE: Making a simple plugin system for your application #7
(02-03-2018, 08:31 PM)Pikami Wrote:
(02-03-2018, 07:36 PM)phyrrus9 Wrote:
(02-03-2018, 05:56 PM)Pikami Wrote: Challenge accepted and completed Smile
Would you mind if I made a pull request to your repo adding my created mysql_resolve plugin?
Go right ahead. I might make some modifications to it though, fair warning.

I just opened my pull request.

Merged, actually looks perfect in its current state, I'm impressed!

Normally a plugin system would scan the directory at start time and call a register function or something (which would help to write the configuration), but I like the idea of being able to add plugins without restarting the program (if you start it, you can then run make && make install in the plugin and it will be loaded on the next request).

In short: dylibs are awesome

Reply

RE: Making a simple plugin system for your application #8
(02-03-2018, 08:44 PM)phyrrus9 Wrote:
(02-03-2018, 08:31 PM)Pikami Wrote:
(02-03-2018, 07:36 PM)phyrrus9 Wrote: Go right ahead. I might make some modifications to it though, fair warning.

I just opened my pull request.

Merged, actually looks perfect in its current state, I'm impressed!

Normally a plugin system would scan the directory at start time and call a register function or something (which would help to write the configuration), but I like the idea of being able to add plugins without restarting the program (if you start it, you can then run make && make install in the plugin and it will be loaded on the next request).

In short: dylibs are awesome

I'm glad Wink
And yes, dylibs are awesome.
(This post was last modified: 02-03-2018, 08:57 PM by Pikami.)

Reply







Users browsing this thread: 1 Guest(s)