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

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



310
311
312
313
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 310

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

Instance Method Details

#calc_padding(offset) ⇒ Object

Bytes that needed to be added to be aligned



562
563
564
565
566
567
568
569
570
571
572
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 562

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)


594
595
596
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 594

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)


475
476
477
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 475

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

#is_null_pointer(pointer) ⇒ Object

Summary: Returns true if pointer will be considered a ‘null’ pointer

If given nil, returns true If given 0, returns true If given a string, if 0 after unpacking, returns true false otherwise



333
334
335
336
337
338
339
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 333

def is_null_pointer(pointer)
  if pointer.class == 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)


469
470
471
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 469

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)


480
481
482
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 480

def is_struct_type?(type)
  return type.class == Array
end

#judge_bit_field(value, mappings) ⇒ Object

Summary:

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, ... }


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

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

Summary: 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’



373
374
375
376
377
378
379
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 373

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



486
487
488
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 486

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



431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 431

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



387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 387

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



382
383
384
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 382

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

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

construct the data structure described in ‘definition’ from ‘buffer’ starting from the index ‘offset’



453
454
455
456
457
458
459
460
461
462
463
464
465
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 453

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

Summary: Reads null-terminated unicode strings from memory.

Given a pointer to a null terminated array of WCHARs, return a ruby string Null pointers cause an empty string to be returned



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 347

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



557
558
559
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 557

def required_alignment
  is_64bit ? 8 : 4
end

#sizeof_struct(struct) ⇒ Object

Calculates the size of the struct after alignment



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

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



491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 491

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

  if is_array_type?(type)
    element_type, length = split_array_type(type)

    return length * sizeof_type(element_type)
  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



577
578
579
580
581
582
583
584
585
586
587
588
589
590
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 577

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, length
  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



535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 535

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



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

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