
/*

  dcetest.c 
  Written by: Dave Aitel & Pasi Eronen

  This code under GPL v 2.0 .
  See gpl.txt for more information.

*/

#include "tcpstuff.h"

#define DCEPORT 135
#define BINDPACKET 0x0b
#define REQUESTPACKET 0x00
#define RESPONSEPACKET 0x02
#define FLAGS 0x03		/* PFC_FIRST_FRAG | PFC_LAST_FRAG */
#define EPT_MAX_ANNOTATION_SIZE 64
#define EPT_LOOKUP 2
#define RPC_C_EP_ALL_ELTS 0

struct uuid_t {
  unsigned int time_low;
  unsigned int time_mid;
  unsigned int time_hi_and_version;
  unsigned int clock_seq_hi_and_reserved;
  unsigned int clock_seq_low;
  unsigned char node[6];
};

/* UUID for Endpoint mapper interface */
static const struct uuid_t EPT_UUID =
{
  0xe1af8308, 0x5d1f, 0x11c9, 0x91, 0xa4,
  { 0x08, 0x00, 0x2b, 0x14, 0xa0, 0xfa }
};
#define EPT_VERSION 3

/* UUID for DCE RPC transfer syntax */
static const struct uuid_t TRANSFER_SYNTAX_UUID =
{
  0x8a885d04, 0x1ceb, 0x11c9, 0x9f, 0xe8,
  { 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60 }
};
#define TRANSFER_SYNTAX_VERSION 2

struct lookup_handle_t {
  unsigned int attributes;
  struct uuid_t uuid;
};

static unsigned int next_call_id = 0;
static unsigned int little_endian;

void usage()
{
  printf("Usage: dcedump host\n");
  printf("Version 1.0 brought to you by Dave Aitel, dave@immunitysec.com.\n");
  printf("Please e-mail me if you have any questions/comments/etc\n");
  printf("Run this program at your own risk.\n");
  printf("This program under GPL v 2.0\n");
  exit(1);
}


unsigned int
load_intel_short(const unsigned char *from)
{
  return (((unsigned int) from[0]) |
	  ((unsigned int) from[1]) << 8);
}

unsigned int
load_network_short(const unsigned char *from)
{
  return (((unsigned int) from[1]) |
	  ((unsigned int) from[0]) << 8);
}

unsigned int
load_intel_long(const unsigned char *from)
{
  return (((unsigned int) from[0]) |
	  ((unsigned int) from[1]) << 8 |
	  ((unsigned int) from[2]) << 16 |
	  ((unsigned int) from[3]) << 24);
}

unsigned int
load_network_long(const unsigned char *from)
{
  return (((unsigned int) from[3]) |
	  ((unsigned int) from[2]) << 8 |
	  ((unsigned int) from[1]) << 16 |
	  ((unsigned int) from[0]) << 24);
}

unsigned int
load_short(const unsigned char *from)
{
  if (little_endian)
    return load_intel_short(from);
  else
    return load_network_short(from);
}

unsigned long
load_long(const unsigned char *from)
{
  if (little_endian)
    return load_intel_long(from);
  else
    return load_network_long(from);
}

void
store_intel_short(unsigned char *to, unsigned int value)
{
  to[0] = value & 0xff;
  to[1] = (value >> 8) & 0xff;
}

void
store_intel_long(unsigned char *to, unsigned int value)
{
  to[0] = value & 0xff;
  to[1] = (value >> 8) & 0xff;
  to[2] = (value >> 16) & 0xff;
  to[3] = (value >> 24) & 0xff;
}

void
load_uuid(const unsigned char *buffer, struct uuid_t* uuid)
{
  uuid->time_low = load_long(buffer);
  uuid->time_mid = load_short(buffer+4);
  uuid->time_hi_and_version = load_short(buffer+6);
  uuid->clock_seq_hi_and_reserved = buffer[8];
  uuid->clock_seq_low = buffer[9];
  memcpy(uuid->node, buffer+10, 6);
}

void
load_intel_uuid(const unsigned char *buffer, struct uuid_t* uuid)
{
  uuid->time_low = load_intel_long(buffer);
  uuid->time_mid = load_intel_short(buffer+4);
  uuid->time_hi_and_version = load_intel_short(buffer+6);
  uuid->clock_seq_hi_and_reserved = buffer[8];
  uuid->clock_seq_low = buffer[9];
  memcpy(uuid->node, buffer+10, 6);
}

int
store_intel_uuid(unsigned char *buffer, const struct uuid_t* uuid)
{
  store_intel_long(buffer, uuid->time_low);
  store_intel_short(buffer+4, uuid->time_mid);
  store_intel_short(buffer+6, uuid->time_hi_and_version);
  buffer[8] = uuid->clock_seq_hi_and_reserved;
  buffer[9] = uuid->clock_seq_low;
  memcpy(buffer+10, uuid->node, 6);
  return 16;
}

void
uuid_to_string(char *buffer, const struct uuid_t* uuid)
{
  
  sprintf(buffer, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
	  uuid->time_low, uuid->time_mid, uuid->time_hi_and_version,
	  uuid->clock_seq_hi_and_reserved,
	  uuid->clock_seq_low,
	  uuid->node[0], uuid->node[1],
	  uuid->node[2], uuid->node[3],
	  uuid->node[4], uuid->node[5]);
}


int 
dce_enum_bind(int fd)
{
  unsigned char buffer[1000];
  unsigned char *frag_lengthp;
  int frag_length;
  unsigned char *p;

  memset(buffer, 0x00, sizeof(buffer));
  p = buffer;

  /* Bind PDU header */
  
  *p++ = 0x05; /*version*/
  *p++ = 0x00; /*minor version*/
  *p++ = BINDPACKET; 
  *p++ = FLAGS;

  /* data representation (little endian, ASCII, IEEE floating point) */
  *p++ = 0x10;
  *p++ = 0x00;
  *p++ = 0x00;
  *p++ = 0x00;

  /* now we have 2 bytes of frag length */
  frag_lengthp = buffer+8; /*copy the length of the fragment here later*/
  p += 2;

  /* auth length = 0 */
  p += 2;

  /* call id */
  store_intel_long(p, next_call_id++);
  p += 4;

  /* max xmit frag */
  store_intel_short(p, 4096);
  p += 2;
  /* max recv frag */
  store_intel_short(p, 4096);
  p += 2;

  /* assoc group = 0 */
  p += 4;

  /* num ctx items */
  *p++ = 1;
  /* alignment padding */
  p += 3;

  /* u_int16 p_cont_id */
  *p++ = 0x00;
  *p++ = 0x00;

  /* u_int8 n_transfer_syn */
  *p++ = 1;
  /* alignment padding */
  p += 1;

  /* Interface UUID + version */
  p += store_intel_uuid(p, &EPT_UUID);
  store_intel_long(p, EPT_VERSION);
  p += 4;

  /* Transfer syntax UUID + version*/
  p += store_intel_uuid(p, &TRANSFER_SYNTAX_UUID);
  store_intel_long(p, TRANSFER_SYNTAX_VERSION);
  p += 4;
  
  /* printf("bind frag length=%d\n",p-buffer); */
  frag_length = p-buffer;
  store_intel_short(frag_lengthp, frag_length);

  write_data(fd, frag_length, buffer);
  return 1;
}

int
dce_enum_get_next(int fd, struct lookup_handle_t *entry_handle)
{
  unsigned char buffer[1000];
  unsigned char *frag_lengthp;
  int frag_length;
  unsigned char *p;

  memset(buffer, 0x00, sizeof(buffer));
  p = buffer;

  /* Start of Request PDU header */
  
  *p++ = 0x05; /*version*/
  *p++ = 0x00; /*minor version*/
  *p++ = REQUESTPACKET; 
  *p++ = FLAGS;

  /* data representation (little endian, ASCII, IEEE floating point) */
  *p++ = 0x10;
  *p++ = 0x00;
  *p++ = 0x00;
  *p++ = 0x00;

  /* now we have 2 bytes of frag length */
  frag_lengthp = buffer+8; /*copy the length of the fragment here later*/
  p += 2;

  /* auth length = 0 */
  p += 2;

  /* call id */
  store_intel_long(p, next_call_id++);
  p += 4;

  /* alloc_hint (0) */
  p += 4;
  
  /* context ID (0) */
  p += 2;

  /* opnum */
  store_intel_short(p, EPT_LOOKUP);
  p += 2;

  /* End of Request PDU header */
  
  /* Parameters for ept_lookup */

  /* unsigned32 inquiry_type */
  store_intel_long(p, RPC_C_EP_ALL_ELTS);
  p += 4;

  /* uuid_p_t object = NULL */
  p += 4;

  /* rpc_if_id_p_t interface_id = NULL */
  p += 4;

  /* unsigned32 vers_option = 0 (not used) */
  p += 4;
  
  /* unsigned32 entry_handle.attributes */
  store_intel_long(p, entry_handle->attributes);
  p += 4;
  
  /* uuid_t entry_handle.context_handle_uuid */
  p += store_intel_uuid(p, &entry_handle->uuid);

  /* unsigned32 max_ents */
  store_intel_long(p, 1);
  p += 4;
  
  frag_length = p-buffer;
  store_intel_short(frag_lengthp, frag_length);

  write_data(fd, frag_length, buffer);
  return 1;
}

unsigned char * 
uuid_to_exe(unsigned char *uuid)
{
  int i;
  typedef struct     {
    unsigned char *uuid;
    unsigned char *name;
  } maplist_t;
  
  maplist_t maplist[] =
    {
      { "a00c021c-2be2-11d2-b678-0000f87a8f8e", "ntfrs.exe" },
      { "50abc2a4-574d-40b3-9d66-ee4fd5fba076", "DNS.exe" },
      { "906b0ce0-c70b-1067-b317-00dd010662da", "msdtc.exe"},
      { "1ff70682-0a51-30e8-076d-740be8cee98b", "mstask.exe"},
      { "378e52b0-c0a9-11cf-822d-00aa0051e40f", "mstask.exe"},
      { "3f99b900-4d87-101b-99b7-aa0004007f07", "sqlservr.exe"},
      { "6bffd098-a112-3610-9833-46c3f874532d", "tcpsvcs.exe"},
      { "5b821720-f63b-11d0-aad2-00c04fc324db", "tcpsvcs.exe"},
      { "fdb3a030-065f-11d1-bb9b-00a024ea5525", "mqsvc.exe"},
      { "a00c021c-2be2-11d2-b678-0000f87a8f8e", "msqvc.exe"},
      { "45f52c28-7f9f-101a-b52b-08002b2efabe", "wins.exe"},
      { "367abb81-9844-35f1-ad32-98f038001003", "SERVICES.EXE"},
      { "bfa951d1-2f0e-11d3-bfd1-00c04fa3490a", "inetinfo.exe"},
      { "8cfb5d70-31a4-11cf-a7d8-00805f48a135", "inetinfo.exe"},
      { "4f82f460-0e21-11cf-909e-00805f48a135", "inetinfo.exe"},
      { "82ad4280-036b-11cf-972c-00aa006887b0", "inetinfo.exe"},
      { "76d12b80-3467-11d3-91ff-0090272f9ea3", "mqsvc.exe"},
      { "1088a980-eae5-11d0-8d9b-00a02453c337", "mqsvc.exe"},
      { NULL, NULL }
    };

  i=0;
  while (maplist[i].uuid!=NULL)
    {
      if (!strcmp(maplist[i].uuid,uuid))
	{
	  return maplist[i].name;
	}
      i++;
    }
  return NULL;
}

int
read_dce_pdu(int fd, unsigned char *buf)
{
  int frag_length;
  read_data(fd, 16, buf);
  if ((buf[4] & 0xf0) == 0x10) {
    little_endian = 1;
  } else {
    little_endian = 0;
  }
  frag_length = load_short(buf+8);
  read_data(fd, frag_length-16, buf+16);
  return frag_length;
}

int
dce_parse_enum_response(const unsigned char *buf,
			struct lookup_handle_t *entry_handle)
{
  const unsigned char *p;
  int tint;
  char annotation[EPT_MAX_ANNOTATION_SIZE+1];
  int floor, floors;
  int address_type;

  char tmp_address[200];
  char tmp_address2[200];

  p = buf;

  if (p[2] != RESPONSEPACKET) {
    printf("Unexpected DCE PDU type %02x\n", p[2]);
    return 0;
  }
 
  /* Skip common DCE header */
  p += 16;

  /* Skip alloc_hint, p_cont_id, cancel_count, and padding */
  p += 8; 

  /* context_handle_attributes */
  entry_handle->attributes = load_long(p);
  p += 4;

  /* context_handle_uuid */
  load_uuid(p, &entry_handle->uuid);
  p += 16;

  /* num_ents */
  if (load_long(p) == 0) {
    return 0;
  }
  p += 4;
 
  /* skip something */
  p += 36;

  /* annotation */
  tint = load_long(p);
  p += 4;
  if (tint > EPT_MAX_ANNOTATION_SIZE) {
    printf("Annotation length (%d) too long\n", tint);
    return 0;
  }
  /* annotation length includes trailing NULL */
  memcpy(annotation, p, tint);
  p += tint;
  printf("\nannotation=%s\n",annotation);
  
  while ((p-buf) % 4 != 0) {
    p++;
  }

  /* Skip tower lengths */
  p += 8;
 
  floors = load_intel_short(p);
  p += 2;

  /* printf("floors = %d\n", floors);*/

  tmp_address[0] = '\0';
  for (floor = 1; floor <= floors; floor++) {
    tint = load_intel_short(p);
    p += 2;

    /*printf("floor %d lhs = %d bytes\n", floor, tint);*/

    if (floor == 1) {
      struct uuid_t uuid;
      load_intel_uuid(p+1, &uuid);
      uuid_to_string(tmp_address, &uuid);
      printf("uuid=%s , version=%d\n", tmp_address, load_intel_short(p+17));
      if (uuid_to_exe(tmp_address))
	{
	  printf("Executable on NT: %s\n",uuid_to_exe(tmp_address));
	}
    } else if (floor == 2 || floor == 3) {
      /* don't print */
    } else {
      address_type = *p;
    }
    p += tint;

    tint = load_intel_short(p);
    p += 2;
    if (floor > 3) {
      switch (address_type) {
	case 0x07:
	  sprintf(tmp_address, "ncacn_ip_tcp:%%s[%d]", load_network_short(p));
	  break;
	case 0x08:
	  sprintf(tmp_address, "ncadg_ip_udp:%%s[%d]", load_network_short(p));
	  break;
	case 0x09:
	  sprintf(tmp_address2, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
	  if (tmp_address[0] != '\0') {
	    printf(tmp_address, tmp_address2);
	    puts("");
	  } else {
	    printf("IP: %s\n", tmp_address2);
	  }
	  break;
	case 0x0f:
	  sprintf(tmp_address, "ncacn_np:%%s[%s]", p);
	  break;
	case 0x10:
	  printf("ncalrpc[%s]\n", p);
	  break;
	case 0x11:
	  if (tmp_address[0] != '\0') {
	    printf(tmp_address, p);
	    puts("");
	  } else {
	    printf("NetBIOS: %s\n", p);
	  }
	  break;
	case 0x1f:
	  sprintf(tmp_address, "ncacn_http:%%s[%d]", load_network_short(p));
	  break;
	default:
	  printf("unknown address type 0x%02x (floor %d, %d bytes)\n",
		 address_type, floor, tint);
      }
    }
    p += tint;
  }
  
  if ((entry_handle->uuid.time_low == 0) &&
      (entry_handle->uuid.time_mid == 0) &&
      (entry_handle->uuid.time_hi_and_version == 0) &&
      (entry_handle->uuid.clock_seq_hi_and_reserved == 0) &&
      (entry_handle->uuid.clock_seq_low == 0) &&
      memcmp(entry_handle->uuid.node, "\0\0\0\0\0\0", 6) == 0) {
    return 0;
  }
  
  return 1;
}

int
main(int argc, char *argv[])
{
  int fd;
  int morestuff = 1;
  char buffer[5000];
  struct lookup_handle_t entry_handle;

  printf("DCE-RPC tester.\n");
  
  if (argc<2)
       usage();
  
  fd = tcpconnect(argv[1], DCEPORT);
  if (fd == -1)
  {
    printf("Was not able to connect to %s\n", argv[1]);
    exit(1);
  }

  printf("TcpConnected\n");

  dce_enum_bind(fd);
  read_dce_pdu(fd, buffer);

  memset(&entry_handle, 0, sizeof(entry_handle));
  while (morestuff)
  {
    dce_enum_get_next(fd, &entry_handle);
    read_dce_pdu(fd, buffer);
    morestuff = dce_parse_enum_response(buffer, &entry_handle);
  }

  printf("\nDone\n");
  return 1;
}

