In January, my colleague Xusheng Li published a walkthrough on the trouble binary, which is the final boss from Jacob Baines’s Programming Linux Anti-Reversing Techniques. It’s a tiny 27 KB password-protected bind shell that exists specifically to trip up naive static analysis.

The ELF header flips the endianness field. There’s a fake _init overlapping the ELF header itself. The executable segment ends one byte before the real _start, while a .data section claims to extend through 0xffffffffffffffff. The body of the program is XOR-encoded with 0xaa. A critical authentication routine is RC4-encrypted on top of that. The control flow inside the decrypted blob has opaque predicates and an obfuscated jump through an RVA helper. The final secret is built as a stack string and XOR’d byte-by-byte before comparison.

Xusheng takes an educational approach as he works through segment editing, section editing, transformations, opaque-predicate patching, and stack-string outlining. He explains why each step is necessary, including an aside on Binary Ninja’s analysis heuristics. If you haven’t read it, read it; it’s the basis for this post.

There is exactly one place in that January walkthrough where Sidekick makes an appearance: it recognizes the RC4 function. That’s the entire role of Sidekick in the original post.

This post is what happened when I sat down with the same binary three months later and used the latest version of Sidekick.

The Setup

I opened trouble in Binary Ninja, let auto-analysis settle, and typed three words into Sidekick:

> Something's awry.

No address, no symptom, no theory. I wanted to see what triage looked like.

Binary Ninja’s recent improvements had already handled one of the original walkthrough’s first-act tricks: the bogus big-endian flag is now auto-corrected for x86/x86-64.

What was still there when I opened the binary was the fake _init overlapping the ELF header, the segment-gap trick around _start, the section-semantics suppression beyond 0x405e9d, and everything that came after.

”Something’s awry”

Sidekick’s first useful move was a kind of refusal.

It pulled up the current function — _init at 0x400000 — and looked at the HLIL: a forward jump into the middle of a struct field expression, undefined flag bits driving a branch, and a long run of *arg3 += arg3.b statements ending in lifter complaints (Could not Generate Flag IL @ 0x400066, Opcode "add" is partially unimplemented (LLIL)). It didn’t try to interpret any of that. Sidekick’s summary was that the decompilation was untrustworthy and that the most likely causes were a bad function boundary, intentionally malformed loader metadata, or analysis having followed junk as code.

When I suggested the ELF was probably bad, Sidekick walked the segment and section tables and produced a clear diagnosis:

Sidekick's diagnosis

The various tricks that were identified in the January walkthrough are recognized. But, Sidekick goes ahead and repairs the situation. Here’s more of the response:

Sidekick's remediation

The January walkthrough involved several steps and explanations to get to this point. All of that it is now collapsed into a single turn. Sidekick’s repair was a single database transaction with four modifications, executed in dependency order.

Unpacking, In Place

Once _start was readable, the loop body was unambiguous: *rbp ^= 0xaa over [0x400120, 0x405e9c). Standard single-byte XOR. So, I directed Sidekick to get moving:

Cleaning Up

At this point, same as in January, it became obvious where main was and Sidekick gave a thorough run down on what main was doing.

RC4 Decryption

I already mentioned that Sidekick previously identified RC4 during Xusheng’s session. In the interest of time, I am going skip the turns where Sidekick identified the RC4 action and also nicely annotated the code for me.

At this point, I’m ready to be done:

Decrypting RC4 Blob

The first eight bytes of plain text look like a real x86-64 prologue.

The contrast with January is worth higlighting. Three months ago, Sidekick recognized the RC4. Today, it recognizes, executes the decryption, writes the plaintext back to the database, and creates the decrypted function.

The Final Stretch

The decrypted auth_stub at 0x404c70 was small but mangled — opaque branch targets, junk call destinations, the kind of light obfuscation that suggests a control-flow pass on top of the encryption. The walkthrough offered two approaches: “right-click → Patch → Always Branch” on each opaque predicate, or just let the HLIL do its job and skip past them. Sidekick resolved the jump target through a trivial RVA-to-VA helper and created the function at 0x404cbf, read the recovered HLIL, and reported the structure:

  1. memcpy 32 encoded bytes from a constant blob into the stack.
  2. Call xor_buffer_in_place(buf, 0x20, 0xaa) — the same 0xaa key that was used at the segment level much earlier in the binary.
  3. Call memcmp(decoded_buf, user_buf, 0x20).
  4. Return the comparison result.

The RC4 stage was protecting the check in 0x404cbf. The check itself is a 32-byte string comparison against a stack-built secret, with the constant sitting in plain view inside the function body once you look at it. XOR’ing the 32-byte blob at 0x404d5a with 0xaa gives:

wulg2FZo17WKoZ6e5Eyyet2BNBP1ppRE

That’s the password. Same answer Xusheng got in January. A client connects to port 1270, sends those 32 ASCII bytes, and gets a /bin/sh on the other end of the socket.

Out of trouble

The Aftermath

A satisfying aspect of using Sidekick is what’s left in the database when the session ends. After this session, the database for trouble contains:

  • A repaired segment and a new code-semantics section, with a comment at _start explaining why both exist.
  • Plaintext bytes in the previously-XOR’d region, with the bogus functions over ciphertext removed.
  • A function called main at 0x404db0.
  • Functions called rc4_init_state and rc4_crypt, with comments describing what they do.
  • A function called auth_stub at 0x404c70 whose bytes are the actual decrypted x86-64 code, not the on-disk ciphertext.
  • A function called add_image_base at 0x400730.
  • A function called check_backdoor_secret at 0x404cbf whose comment names the recovered password.
  • Functions called xor_buffer_in_place and memcmp with one-line descriptions of their roles.

Annotated trouble

If you open that database tomorrow, you can read it. The names are right, the comments capture the reasoning and not just the result, and the binary’s own metadata reflects how the binary actually works. The artifact of the session is not a hard-to-find-again chat log. The binary itself is understandable.

When you and Sidekick are done with a session, the binary in front of you is closer to a thing a person can read than it was when you started, and the path you took to get there is annotated.

Conclusion

In January, Sidekick’s contribution to the trouble walkthrough was: “It’s RC4.” In April, it’s the rest of the post. The boundary is moving in the direction of “the analyst supervises and judges; the agent operates on the database.”