Class: Rex::Post::Meterpreter::Extensions::Stdapi::Railgun::Util

Inherits:
Object
  • Object
show all
Includes:
DLLHelper
Defined in:
lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb

Overview

Utility methods and constants for dealing with most types of variables.

Constant Summary collapse

PRIMITIVE_TYPE_SIZES =
{
  :int => 4,
  :__int8 => 1,
  :__int16 => 2,
  :__int32 => 4,
  :__int64 => 8,
  :bool => 1,
  :char => 1,
  :short => 2,
  :long => 4,
  :long_long => 8,
  :float => 4,
  :double => 8,
  :long_double => 8,
  :wchar_t => 2,
}
TYPE_DEFINITIONS =

Maps a data type to its corresponding primitive or special type :pointer. Note, primitive types are mapped to themselves.

typedef info: msdn.microsoft.com/en-us/library/aa383751(v=vs.85).aspx

{
  ##
  # Primitives
  ##
  :int => :int,
  :__int8 => :__int8,
  :__int16 => :__int16,
  :__int32 => :__int32,
  :__int64 => :__int64,
  :bool => :bool,
  :char => :char,
  :short => :short,
  :long => :long,
  :long_long => :long_long,
  :float => :float,
  :double => :double,
  :long_double => :long_double,
  :wchar_t => :wchar_t,
  ##
  # Non-pointers
  ##
  #typedef WORD ATOM;
  :ATOM => :short,
  #typedef int BOOL;
  :BOOL => :int,
  #typedef BYTE BOOLEAN;
  :BOOLEAN => :char,
  #typedef unsigned char BYTE;
  :BYTE => :char,
  #typedef char CHAR;
  :CHAR => :char,
  #typedef DWORD COLORREF;
  :COLORREF => :long,
  #typedef unsigned long DWORD;
  :DWORD => :long,
  #typedef unsigned int DWORD32;
  :DWORD32 => :int,
  #typedef unsigned __int64 DWORD64;
  :DWORD64 => :__int64,
  #typedef float FLOAT;
  :FLOAT => :float,
  #typedef int HFILE;
  :HFILE => :int,
  #typedef LONG HRESULT;
  :HRESULT => :long,
  #typedef int INT;
  :INT => :int,
  #typedef signed int INT32;
  :INT32 => :int,
  #typedef signed __int64 INT64;
  :INT64 => :__int64,
  #typedef WORD LANGID;
  :LANGID => :short,
  #typedef DWORD LCID;
  :LCID => :long,
  #typedef DWORD LCTYPE;
  :LCTYPE => :long,
  #typedef DWORD LGRPID;
  :LGRPID => :long,
  #typedef long LONG;
  :LONG => :long,
  #typedef signed int LONG32;
  :LONG32 => :int,
  #typedef __int64 LONG64;
  :LONG64 => :__int64,
  #typedef PDWORD PLCID;
  :PLCID => :pointer,
  #typedef LPVOID SC_LOCK;
  :SC_LOCK => :pointer,
  #typedef short SHORT;
  :SHORT => :short,
  #typedef unsigned char UCHAR;
  :UCHAR => :char,
  #typedef unsigned int UINT;
  :UINT => :int,
  #typedef unsigned int UINT32;
  :UINT32 => :int,
  #typedef unsigned long ULONG;
  :ULONG => :long,
  #typedef unsigned int ULONG32;
  :ULONG32 => :int,
  #typedef unsigned __int64 ULONG64;
  :ULONG64 => :__int64,
  #typedef unsigned short USHORT;
  :USHORT => :short,
  #typedef wchar_t WCHAR;
  :WCHAR => :wchar_t,
  #typedef unsigned short WORD;
  :WORD => :short,
  ##
  # Pointers declared with *
  ##
  #typedef DWORD* LPCOLORREF;
  :LPCOLORREF => :pointer,
  #typedef void* LPCVOID;
  :LPCVOID => :pointer,
  #typedef WCHAR* LPCWSTR;
  :LPCWSTR => :pointer,
  #typedef DWORD* LPDWORD;
  :LPDWORD => :pointer,
  #typedef HANDLE* LPHANDLE;
  :LPHANDLE => :pointer,
  #typedef int* LPINT;
  :LPINT => :pointer,
  #typedef long* LPLONG;
  :LPLONG => :pointer,
  #typedef CHAR* LPSTR;
  :LPSTR => :pointer,
  #typedef void* LPVOID;
  :LPVOID => :pointer,
  #typedef WORD* LPWORD;
  :LPWORD => :pointer,
  #typedef WCHAR* LPWSTR;
  :LPWSTR => :pointer,
  #typedef BOOL* PBOOL;
  :PBOOL => :pointer,
  #typedef BOOLEAN* PBOOLEAN;
  :PBOOLEAN => :pointer,
  #typedef BYTE* PBYTE;
  :PBYTE => :pointer,
  #typedef CHAR* PCHAR;
  :PCHAR => :pointer,
  #typedef CHAR* PCSTR;
  :PCSTR => :pointer,
  #typedef WCHAR* PCWSTR;
  :PCWSTR => :pointer,
  #typedef DWORD* PDWORD;
  :PDWORD => :pointer,
  #typedef DWORDLONG* PDWORDLONG;
  :PDWORDLONG => :pointer,
  #typedef DWORD_PTR* PDWORD_PTR;
  :PDWORD_PTR => :pointer,
  #typedef DWORD32* PDWORD32;
  :PDWORD32 => :pointer,
  #typedef DWORD64* PDWORD64;
  :PDWORD64 => :pointer,
  #typedef FLOAT* PFLOAT;
  :PFLOAT => :pointer,
  #typedef HANDLE* PHANDLE;
  :PHANDLE => :pointer,
  #typedef HKEY* PHKEY;
  :PHKEY => :pointer,
  #typedef int* PINT;
  :PINT => :pointer,
  #typedef INT_PTR* PINT_PTR;
  :PINT_PTR => :pointer,
  #typedef INT32* PINT32;
  :PINT32 => :pointer,
  #typedef INT64* PINT64;
  :PINT64 => :pointer,
  #typedef LONG* PLONG;
  :PLONG => :pointer,
  #typedef LONGLONG* PLONGLONG;
  :PLONGLONG => :pointer,
  #typedef LONG_PTR* PLONG_PTR;
  :PLONG_PTR => :pointer,
  #typedef LONG32* PLONG32;
  :PLONG32 => :pointer,
  #typedef LONG64* PLONG64;
  :PLONG64 => :pointer,
  #typedef SHORT* PSHORT;
  :PSHORT => :pointer,
  #typedef SIZE_T* PSIZE_T;
  :PSIZE_T => :pointer,
  #typedef SSIZE_T* PSSIZE_T;
  :PSSIZE_T => :pointer,
  #typedef CHAR* PSTR;
  :PSTR => :pointer,
  #typedef TBYTE* PTBYTE;
  :PTBYTE => :pointer,
  #typedef TCHAR* PTCHAR;
  :PTCHAR => :pointer,
  #typedef UCHAR* PUCHAR;
  :PUCHAR => :pointer,
  #typedef UINT* PUINT;
  :PUINT => :pointer,
  #typedef UINT_PTR* PUINT_PTR;
  :PUINT_PTR => :pointer,
  #typedef UINT32* PUINT32;
  :PUINT32 => :pointer,
  #typedef UINT64* PUINT64;
  :PUINT64 => :pointer,
  #typedef ULONG* PULONG;
  :PULONG => :pointer,
  #typedef ULONGLONG* PULONGLONG;
  :PULONGLONG => :pointer,
  #typedef ULONG_PTR* PULONG_PTR;
  :PULONG_PTR => :pointer,
  #typedef ULONG32* PULONG32;
  :PULONG32 => :pointer,
  #typedef ULONG64* PULONG64;
  :PULONG64 => :pointer,
  #typedef USHORT* PUSHORT;
  :PUSHORT => :pointer,
  #typedef void* PVOID;
  :PVOID => :pointer,
  #typedef WCHAR* PWCHAR;
  :PWCHAR => :pointer,
  #typedef WORD* PWORD;
  :PWORD => :pointer,
  #typedef WCHAR* PWSTR;
  :PWSTR => :pointer,
  #typedef HANDLE HACCEL;
  :HACCEL => :pointer,
  ##
  #  Handles
  ##
  #typedef PVOID HANDLE;
  :HANDLE => :pointer,
  #typedef HANDLE HBITMAP;
  :HBITMAP => :pointer,
  #typedef HANDLE HBRUSH;
  :HBRUSH => :pointer,
  #typedef HANDLE HCOLORSPACE;
  :HCOLORSPACE => :pointer,
  #typedef HANDLE HCONV;
  :HCONV => :pointer,
  #typedef HANDLE HCONVLIST;
  :HCONVLIST => :pointer,
  #typedef HANDLE HDC;
  :HDC => :pointer,
  #typedef HANDLE HDDEDATA;
  :HDDEDATA => :pointer,
  #typedef HANDLE HDESK;
  :HDESK => :pointer,
  #typedef HANDLE HDROP;
  :HDROP => :pointer,
  #typedef HANDLE HDWP;
  :HDWP => :pointer,
  #typedef HANDLE HENHMETAFILE;
  :HENHMETAFILE => :pointer,
  #typedef HANDLE HFONT;
  :HFONT => :pointer,
  #typedef HANDLE HGDIOBJ;
  :HGDIOBJ => :pointer,
  #typedef HANDLE HGLOBAL;
  :HGLOBAL => :pointer,
  #typedef HANDLE HHOOK;
  :HHOOK => :pointer,
  #typedef HANDLE HICON;
  :HICON => :pointer,
  #typedef HANDLE HINSTANCE;
  :HINSTANCE => :pointer,
  #typedef HANDLE HKEY;
  :HKEY => :pointer,
  #typedef HANDLE HKL;
  :HKL => :pointer,
  #typedef HANDLE HLOCAL;
  :HLOCAL => :pointer,
  #typedef HANDLE HMENU;
  :HMENU => :pointer,
  #typedef HANDLE HMETAFILE;
  :HMETAFILE => :pointer,
  #typedef HANDLE HPALETTE;
  :HPALETTE => :pointer,
  #typedef HANDLE HPEN;
  :HPEN => :pointer,
  #typedef HANDLE HRGN;
  :HRGN => :pointer,
  #typedef HANDLE HRSRC;
  :HRSRC => :pointer,
  #typedef HANDLE HSZ;
  :HSZ => :pointer,
  #typedef HANDLE WINSTA;
  :WINSTA => :pointer,
  #typedef HANDLE HWND;
  :HWND => :pointer,
  #typedef HANDLE SC_HANDLE;
  :SC_HANDLE => :pointer,
  #typedef HANDLE SERVICE_STATUS_HANDLE;
  :SERVICE_STATUS_HANDLE => :pointer,
}

Instance Method Summary collapse

Methods included from DLLHelper

#asciiz_to_str, #assemble_buffer, #param_to_number, #str_to_ascii_z, #str_to_uni_z, #uniz_to_str

Constructor Details

#initialize(railgun, platform) ⇒ Util

param 'railgun' is a Railgun instance. param 'platform' is a value like client.platform


317
318
319
320
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 317

def initialize(railgun, platform)
  @railgun = railgun
  @is_64bit = is_64bit_platform?(platform)
end

Instance Method Details

#calc_padding(offset) ⇒ Object

Number of bytes that needed to be added to be aligned.


608
609
610
611
612
613
614
615
616
617
618
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 608

def calc_padding(offset)
  align = required_alignment

  # If offset is not aligned...
  if (offset % align) != 0
    # Calculate padding needed to be aligned
    align - (offset & (align - 1))
  else
    0
  end
end

#is_64bit_platform?(platform) ⇒ Boolean

Returns true if given platform has 64bit architecture expects client.platform

Returns:

  • (Boolean)

641
642
643
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 641

def is_64bit_platform?(platform)
  platform =~ /win64/
end

#is_array_type?(type) ⇒ Boolean

Returns whether the given type represents an array of another type For example BYTE, BYTE, or even PDWORD

Returns:

  • (Boolean)

515
516
517
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 515

def is_array_type?(type)
  return type =~ /^\w+\[\w+\]$/ ? true : false
end

#is_null_pointer(pointer) ⇒ Object

Returns true if pointer will be considered a 'null' pointer.

If pointer is nil or 0, returns true If pointer is a String, if 0 after unpacking, returns true false otherwise

See #unpack_pointer


343
344
345
346
347
348
349
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 343

def is_null_pointer(pointer)
  if pointer.kind_of? String
    pointer = unpack_pointer(pointer)
  end

  return pointer.nil? || pointer == 0
end

#is_pointer_type?(type) ⇒ Boolean

Returns true if the data type is a pointer, false otherwise

Returns:

  • (Boolean)

509
510
511
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 509

def is_pointer_type?(type)
  return TYPE_DEFINITIONS[type] == :pointer
end

#is_struct_type?(type) ⇒ Boolean

Returns true if the type passed describes a data structure, false otherwise

Returns:

  • (Boolean)

520
521
522
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 520

def is_struct_type?(type)
  return type.kind_of? Array
end

#judge_bit_field(value, mappings) ⇒ Object

Evaluates a bit field, returning a hash representing the meaning and state of each bit.

Parameters:

+value+:: a bit field represented by a Fixnum
+mappings+:: { 'WINAPI_CONSTANT_NAME' => :descriptive_symbol, ... }

Returns:

{ :descriptive_symbol => true/false, ... }

656
657
658
659
660
661
662
663
664
665
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 656

def judge_bit_field(value, mappings)
  flags = {}
  rg = railgun

  mappings.each do |constant_name, key|
    flags[key] = (value & rg.const(constant_name)) != 0
  end

  flags
end

#memread(address, size, buffer = nil) ⇒ Object

Read a given number of bytes from memory or from a provided buffer.

If buffer is not provided, read size bytes from the client's memory. If buffer is provided, reads size characters from the index of address.


404
405
406
407
408
409
410
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 404

def memread(address, size, buffer = nil)
  if buffer.nil?
    return railgun.memread(address, size)
  else
    return buffer[address .. (address + size - 1)]
  end
end

#pointer_sizeObject

Returns the pointer size for this architecture


526
527
528
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 526

def pointer_size
  is_64bit ? 8 : 4
end

#read_array(type, length, bufptr, buffer = nil) ⇒ Object

Read length number of instances of type from bufptr .

bufptr is an index in buffer or, if buffer is nil, a memory address


469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 469

def read_array(type, length, bufptr, buffer = nil)
  if length <= 0
    return []
  end

  size = sizeof_type(type)
  # Grab the bytes that the array consists of
  buffer = memread(bufptr, size * length, buffer)

  offset = 0

  1.upto(length).map do |n|
    data = read_data(type, offset, buffer)

    offset = offset + size

    data
  end
end

#read_data(type, position, buffer = nil) ⇒ Object

Reads data structures and several windows data types


422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 422

def read_data(type, position, buffer = nil)
  if buffer.nil?
    buffer = memread(position, sizeof_type(type))
    position = 0
  end

  # If we're asked to read a data structure, deligate to read_struct
  if is_struct_type?(type)
    return read_struct(type, buffer, position)
  end

  # If the type is an array with a given size...
  #    BYTE[3] for example or BYTE[ENCRYPTED_PWLEN] or even PDWORD[23]
  if is_array_type?(type)
    # Separate the element type from the size of the array
    element_type, length = split_array_type(type)

    # Have read_array take care of the rest
    return read_array(element_type, length, position, buffer)
  end

  size = sizeof_type(type)
  raw  = memread(position, size, buffer)

  # read/unpack data for the types we have hard-coded support for
  case type
  when :LPWSTR
    # null-terminated string of 16-bit Unicode characters
    return read_wstring(read_pointer(raw))
  when :DWORD
    # Both on x86 and x64, DWORD is 32 bits
    return raw.unpack('V').first
  when :BOOL
    return raw.unpack('l').first == 1
  when :LONG
    return raw.unpack('l').first
  end

  #If nothing worked thus far, return it raw
  return raw
end

#read_pointer(buffer, offset = 0) ⇒ Object

Read and unpack a pointer from the given buffer at a given offset


415
416
417
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 415

def read_pointer(buffer, offset = 0)
  unpack_pointer(buffer[offset, (offset + pointer_size)])
end

#read_string(pointer, length = nil) ⇒ Object

Reads null-terminated ASCII strings from memory.

Given a pointer to a null terminated array of CHARs, return a ruby String. If pointer is NULL (see #is_null_pointer) returns an empty string.


385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 385

def read_string(pointer, length=nil)
  if is_null_pointer(pointer)
    return ''
  end

  unless length
    length = railgun.kernel32.lstrlenA(pointer)['return']
  end

  chars = read_array(:CHAR, length, pointer)
  return chars.join('')
end

#read_struct(definition, buffer, offset = 0) ⇒ Object

Construct the data structure described in definition from buffer starting from the index offset


493
494
495
496
497
498
499
500
501
502
503
504
505
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 493

def read_struct(definition, buffer, offset = 0)
  data = {}

  offsets = struct_offsets(definition, offset)

  definition.each do |mapping|
    key, data_type = mapping

    data[key] = read_data(data_type, offsets.shift, buffer)
  end

  data
end

#read_wstring(pointer, length = nil) ⇒ Object

Reads null-terminated unicode strings from memory.

Given a pointer to a null terminated array of WCHARs, return a ruby String. If pointer is NULL (see #is_null_pointer) returns an empty string.


358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 358

def read_wstring(pointer, length = nil)
  # Return an empty string for null pointers
  if is_null_pointer(pointer)
    return ''
  end

  # If length not provided, use lstrlenW
  if length.nil?
    length = railgun.kernel32.lstrlenW(pointer)['return']
  end

  # Retrieve the array of characters
  chars = read_array(:WCHAR, length, pointer)

  # Concatenate the characters and convert to a ruby string
  str = uniz_to_str(chars.join(''))

  return str
end

#required_alignmentObject


601
602
603
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 601

def required_alignment
  is_64bit ? 8 : 4
end

#sizeof_struct(struct) ⇒ Object

Calculates the size of struct after alignment.


569
570
571
572
573
574
575
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 569

def sizeof_struct(struct)
  offsets = struct_offsets(struct, 0)
  last_data_size = sizeof_type(struct.last[1])
  size_no_padding = offsets.last + last_data_size

  return size_no_padding + calc_padding(size_no_padding)
end

#sizeof_type(type) ⇒ Object

Return the size, in bytes, of the given type


531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 531

def sizeof_type(type)
  if is_pointer_type?(type)
    return pointer_size
  end

  if type.kind_of? String
    if is_array_type?(type)
      element_type, length = split_array_type(type)
      return length * sizeof_type(element_type)
    else
      return sizeof_type(type.to_sym)
    end
  end

  if is_struct_type?(type)
    return sizeof_struct(type)
  end

  if TYPE_DEFINITIONS.has_key?(type)
    primitive = TYPE_DEFINITIONS[type]

    if primitive == :pointer
      return pointer_size
    end

    if PRIMITIVE_TYPE_SIZES.has_key?(primitive)
      return PRIMITIVE_TYPE_SIZES[primitive]
    else
      raise "Type #{type} was mapped to non-existent primitive #{primitive}"
    end
  end

  raise "Unable to determine size for type #{type}."
end

#split_array_type(type) ⇒ Object

Given an explicit array definition (e.g. BYTE) return size (e.g. 23) and and type (e.g. BYTE). If a constant is given, attempt to resolve it that constant.


625
626
627
628
629
630
631
632
633
634
635
636
637
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 625

def split_array_type(type)
  if type =~ /^(\w+)\[(\w+)\]$/
    element_type = $1
    length = $2
    unless length =~ /^\d+$/
      length = railgun.const(length)
    end

    return element_type.to_sym, length.to_i
  else
    raise "Can not split non-array type #{type}"
  end
end

#struct_offsets(definition, offset) ⇒ Object

Given a description of a data structure, returns an Array containing the offset from the beginning for each subsequent element, taking into consideration alignment and padding.


582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 582

def struct_offsets(definition, offset)
  padding = 0
  offsets = []
  definition.each do |mapping|
    key, data_type = mapping
    if sizeof_type(data_type) > padding
      offset = offset + padding
    end

    offsets.push(offset)

    offset = offset + sizeof_type(data_type)
    padding = calc_padding(offset)
  end

  offsets
end

#unpack_pointer(packed_pointer) ⇒ Object

Given a packed pointer, unpacks it according to architecture


325
326
327
328
329
330
331
332
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 325

def unpack_pointer(packed_pointer)
  if is_64bit
    # XXX: Only works if attacker and victim are like-endianed
    packed_pointer.unpack('Q')[0]
  else
    packed_pointer.unpack('V')[0]
  end
end