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

Inherits:
Object
  • Object
show all
Includes:
LibraryHelper
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 Attribute Summary collapse

Instance Method Summary collapse

Methods included from LibraryHelper

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

Constructor Details

#initialize(railgun, arch) ⇒ Util

param 'railgun' is a Railgun instance. param 'arch' is the client.arch


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

def initialize(railgun, arch)
  @railgun = railgun
  @is_64bit = arch == ARCH_X64
end

Instance Attribute Details

#is_64bitObject (protected)

Returns the value of attribute is_64bit


707
708
709
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 707

def is_64bit
  @is_64bit
end

#railgunObject (protected)

Returns the value of attribute railgun


707
708
709
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 707

def railgun
  @railgun
end

Instance Method Details

#alloc_and_write_string(value) ⇒ Object

Write ASCII strings to memory.

Given a string, returns a pointer to a null terminated CHARs array. InitializeStr(&Str,“string”);


406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 406

def alloc_and_write_string(value)
  return nil if value == nil

  data = str_to_ascii_z(value)
  result = railgun.kernel32.VirtualAlloc(nil, data.length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
  if result['return'].nil?
    return nil
  end
  addr = result['return']
  if railgun.memwrite(addr, data, data.length)
    return addr
  else
    return nil
  end
end

#alloc_and_write_wstring(value) ⇒ Object

Write Unicode strings to memory.

Given a Unicode string, returns a pointer to a null terminated WCHARs array. InitializeUnicodeStr(&uStr, sL“string”);


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

def alloc_and_write_wstring(value)
  return nil if value == nil

  data = str_to_uni_z(value)
  result = railgun.kernel32.VirtualAlloc(nil, data.length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
  if result['return'].nil?
    return nil
  end
  addr = result['return']
  if railgun.memwrite(addr, data, data.length)
    return addr
  else
    return nil
  end
end

#calc_padding(offset) ⇒ Object

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


652
653
654
655
656
657
658
659
660
661
662
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 652

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_array_type?(type) ⇒ Boolean

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

Returns:

  • (Boolean)

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

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)

553
554
555
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 553

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)

564
565
566
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 564

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 Integer
+mappings+:: { 'WINAPI_CONSTANT_NAME' => :descriptive_symbol, ... }

Returns:

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

694
695
696
697
698
699
700
701
702
703
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 694

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.


448
449
450
451
452
453
454
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 448

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


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

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


513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 513

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


466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 466

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('V').first == 1
  when :LONG
    return raw.unpack('V').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


459
460
461
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 459

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.


429
430
431
432
433
434
435
436
437
438
439
440
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 429

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


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

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


645
646
647
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 645

def required_alignment
  is_64bit ? 8 : 4
end

#sizeof_struct(struct) ⇒ Object

Calculates the size of struct after alignment.


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

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


575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 575

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.


669
670
671
672
673
674
675
676
677
678
679
680
681
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb', line 669

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.


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

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
    # Assume little endian
    packed_pointer.unpack('Q<')[0]
  else
    packed_pointer.unpack('V')[0]
  end
end