<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Yen Wang</title>
    <subtitle>Security researcher reverse-engineering Rust malware. PhD researcher at UWE Bristol, based in London.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://yenwang.dev/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://yenwang.dev"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-05-28T00:00:00+00:00</updated>
    <id>https://yenwang.dev/atom.xml</id>
    <entry xml:lang="en">
        <title>What Happens to Rust Ownership When You Compile It?</title>
        <published>2026-05-28T00:00:00+00:00</published>
        <updated>2026-05-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yenwang.dev/blog/medium-rust-ownership-compiled/"/>
        <id>https://yenwang.dev/blog/medium-rust-ownership-compiled/</id>
        
        <content type="html" xml:base="https://yenwang.dev/blog/medium-rust-ownership-compiled/">&lt;p&gt;&lt;em&gt;A binary analysis of drop flags, drop glue, and the erasure of borrow semantics across debug and release builds&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Rust’s ownership system is the language’s defining feature. The borrow checker enforces, at compile time, that every value has exactly one owner, that references do not outlive the values they point to, and that mutable and immutable borrows cannot coexist. These guarantees are central to Rust’s memory safety story.&lt;&#x2F;p&gt;
&lt;p&gt;But what actually survives in the compiled binary? If you hand a stripped Rust executable to a reverse engineer, how much of the ownership structure can they recover?&lt;&#x2F;p&gt;
&lt;p&gt;This post answers that question with a concrete binary analysis. We compiled a controlled Rust programme exercising five ownership patterns, exported the Binary Ninja disassembly for both debug and release builds (Windows x64, MSVC toolchain), and traced each ownership concept from its MIR representation down to the final machine instructions. The results are specific and in some cases surprising.&lt;&#x2F;p&gt;
&lt;p&gt;The short answer: the drop sequence survives, drop flags partially survive in debug builds, and almost everything else is gone.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-test-programme&quot;&gt;The Test Programme&lt;&#x2F;h2&gt;
&lt;p&gt;The programme exercises five distinct ownership patterns explicitly:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn ownership_test() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Pattern 1: move semantics
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let a = String::from(&amp;quot;hello&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let b = a;              &#x2F;&#x2F; a is moved, borrow checker invalidates a
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;{}&amp;quot;, b);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Pattern 2: shared borrows
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let s = String::from(&amp;quot;world&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let r1 = &amp;amp;s;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let r2 = &amp;amp;s;            &#x2F;&#x2F; two shared borrows, legal
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;{} {}&amp;quot;, r1, r2);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Pattern 3: mutable borrow
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let mut t = String::from(&amp;quot;foo&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let m = &amp;amp;mut t;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    m.push_str(&amp;quot;bar&amp;quot;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Pattern 4: ownership transfer into function
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    takes_ownership(b);     &#x2F;&#x2F; b moved here, no longer valid after this line
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Pattern 5: clone preserves both values
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let orig = String::from(&amp;quot;original&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let copy = orig.clone();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;{} {}&amp;quot;, orig, copy);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn takes_ownership(s: String) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;{}&amp;quot;, s);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}   &#x2F;&#x2F; s dropped here
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We compiled this with --debug and --release, exported the Binary Ninja linear disassembly, and also captured the MIR output (-Z dump-mir=all) for both profiles. The platform is Windows x64 with the MSVC toolchain, so the calling convention is Windows fastcall (first four arguments in rcx&#x2F;rdx&#x2F;r8&#x2F;r9) and the allocator is MSVC HeapAlloc&#x2F;HeapFree rather than jemalloc.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-mir-layer-where-ownership-is-still-visible&quot;&gt;The MIR Layer: Where Ownership Is Still Visible&lt;&#x2F;h2&gt;
&lt;p&gt;Before examining the binary, it is worth pausing at MIR (Mid-level Intermediate Representation), which is the last stage of the Rust compiler where ownership semantics are fully explicit. Looking at the MIR output is instructive because it shows exactly what the compiler knows about ownership before codegen discards it.&lt;&#x2F;p&gt;
&lt;p&gt;The debug MIR for the move (let b = a) contains this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_54 = const false;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_1 = &amp;lt;String as From&amp;lt;&amp;amp;str&amp;gt;&amp;gt;::from(const &amp;quot;hello&amp;quot;) -&amp;gt; [return: bb1, ...];
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; bb1:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_54 = const true;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_2 = move _1;       &#x2F;&#x2F; b = move a
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;_54 is a &lt;strong&gt;drop flag&lt;&#x2F;strong&gt; -- a boolean that tracks whether _1 (bound to a, then moved into _2 which is b) needs to be dropped on an unwind path. When b is subsequently moved into takes_ownership, the flag is cleared:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_54 = const false;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_34 = move _2;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_33 = takes_ownership(move _34)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Drop flags exist because of panic unwinding. If an exception unwinds the stack mid-function, the runtime needs to know which values are currently live and need dropping. The flag encodes that information. In release mode, the compiler proves statically which values are live at every unwind point and eliminates the flags entirely.&lt;&#x2F;p&gt;
&lt;p&gt;The scope structure in the MIR also encodes the full nesting of borrows:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;scope 5 { debug s =&amp;gt; _10;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  scope 6 { debug r1 =&amp;gt; _11;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    scope 7 { debug r2 =&amp;gt; _12;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      scope 10 { debug t =&amp;gt; _22;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        scope 11 { debug m =&amp;gt; _23; }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Every borrow is named, scoped, and given a lifetime. r1 and r2 are both &amp;amp;std::string::String, distinguished from m which is &amp;amp;mut std::string::String. This information exists in MIR. It does not exist in the compiled binary.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;debug-build-pattern-1-move-semantics&quot;&gt;Debug Build: Pattern 1 — Move Semantics&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Source:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let a = String::from(&amp;quot;hello&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let b = a;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;**Binary (**&lt;strong&gt;0x140002440 region,&lt;&#x2F;strong&gt; &lt;strong&gt;sub_140002440 =&lt;&#x2F;strong&gt; &lt;strong&gt;ownership_test):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; String::from(&amp;quot;hello&amp;quot;) -- allocates heap, fills String struct
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea  rdx, [rel &amp;quot;hello&amp;quot;]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea  rcx, [rbp-0x18]         ; destination: a&amp;#39;s stack slot (var_1c0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  r8d, 0x5                ; length
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call sub_140003930            ; &amp;lt;String as From&amp;lt;&amp;amp;str&amp;gt;&amp;gt;::from
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; drop flag: a is now live
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  byte [rbp+0x167], 0x1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; move a into b -- three field copies (ptr, len, cap)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  rax, qword [rbp-0x8]    ; a.cap
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rbp+0x10], rax   ; b.cap
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;movups xmm0, xmmword [rbp-0x18]  ; a.ptr + a.len (16 bytes, SSE load)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;movaps xmmword [rbp], xmm0       ; b.ptr + b.len
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The move is a movups&#x2F;movaps pair for the first 16 bytes (ptr and len) and a separate mov for the cap field. Three field copies. The source slot at rbp-0x18 is not zeroed. No instruction marks a as invalid. The borrow checker&#x27;s enforcement that a cannot be read after this point is not present in the binary.&lt;&#x2F;p&gt;
&lt;p&gt;The only trace of the ownership transfer is the drop flag at rbp+0x167, which is 0x1 from this point until b is moved into takes_ownership.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;debug-build-pattern-4-ownership-transfer-into-a-function&quot;&gt;Debug Build: Pattern 4 — Ownership Transfer Into a Function&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Source:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;takes_ownership(b);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;**Binary (**&lt;strong&gt;0x1400025e1):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  byte [rbp+0x167], 0x0   ; drop flag cleared -- b is no longer owned here
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  rax, qword [rbp+0x10]   ; b.cap
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rbp+0xe0], rax   ; copy to argument slot
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;movaps xmm0, xmmword [rbp]   ; b.ptr + b.len
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;movaps xmmword [rbp+0xd0], xmm0  ; copy to argument slot
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea  rcx, [rbp+0xd0]         ; rcx = &amp;amp;arg (Windows ABI: by-value String as pointer)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call sub_140002800            ; takes_ownership(b)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The drop flag goes to zero immediately before the call. This is the binary encoding of “b is no longer the owner”. Inside sub_140002800 (which is takes_ownership), the function ends with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  rcx, qword [rbp-0x38]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call sub_140003a90            ; drop_in_place&amp;lt;String&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;sub_140003a90 calls sub_140003ab0, which calls sub_140003b10 (the dealloc path wrapping HeapFree). This is &lt;strong&gt;drop glue&lt;&#x2F;strong&gt; -- compiler-generated destructor code that runs at the end of the callee because the callee now owns the value.&lt;&#x2F;p&gt;
&lt;p&gt;Two things are observable here. First, the drop call in takes_ownership confirms that the function received ownership -- a function that merely borrowed would not produce this destructor. Second, the absence of a drop for b in ownership_test after the call confirms the transfer -- b&#x27;s stack slot is not cleaned up here because it no longer owns anything.&lt;&#x2F;p&gt;
&lt;p&gt;Both of these are indirect signals. Neither encodes the borrow checker’s actual constraint (“b cannot be used after this call”).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;debug-build-patterns-2-and-3-borrows-shared-and-mutable&quot;&gt;Debug Build: Patterns 2 and 3 — Borrows, Shared and Mutable&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Source:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let r1 = &amp;amp;s;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let r2 = &amp;amp;s;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let m = &amp;amp;mut t;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;m.push_str(&amp;quot;bar&amp;quot;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;**Binary for shared borrows (**&lt;strong&gt;0x1400024f4):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea  rax, [rbp+0x48]         ; address of s (var_160)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rbp+0x60], rax   ; r1 = &amp;amp;s
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rbp+0x68], rax   ; r2 = &amp;amp;s
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;**Binary for mutable borrow (**&lt;strong&gt;0x14000259a):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea  rcx, [rbp+0xb8]         ; address of t (var_f0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rbp+0x180], rcx  ; m = &amp;amp;mut t
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These are identical operations: take the address of a stack slot, store it in another slot. The lea for r1 and the lea for m produce the same instruction shape. The borrow checker enforced that r1 and r2 are shared (no mutation allowed), that m is exclusive (no other borrow can coexist), and that none of these references outlive s or t. None of that information is present in the binary.&lt;&#x2F;p&gt;
&lt;p&gt;A disassembler looking at mov qword [rbp+0x60], rax cannot determine whether rax is a shared borrow, a mutable borrow, or a raw pointer. The distinction is a compile-time property that has been fully erased.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;debug-build-drop-sequence-at-scope-exit&quot;&gt;Debug Build: Drop Sequence at Scope Exit&lt;&#x2F;h2&gt;
&lt;p&gt;**MIR (****bb23--**&lt;strong&gt;bb26):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;drop(_36)   &#x2F;&#x2F; copy
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;drop(_35)   &#x2F;&#x2F; orig
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;drop(_26)   &#x2F;&#x2F; t
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;drop(_12)   &#x2F;&#x2F; s
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;LIFO order — innermost scope drops first.&lt;&#x2F;p&gt;
&lt;p&gt;**Binary (****0x1400026ca--**&lt;strong&gt;0x1400026f8):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea  rcx, [rbp+0x108]    ; &amp;amp;copy
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call sub_140003a90        ; drop_in_place&amp;lt;String&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea  rcx, [rbp+0xf0]     ; &amp;amp;orig
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call sub_140003a90        ; drop_in_place&amp;lt;String&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea  rcx, [rbp+0xb8]     ; &amp;amp;t
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call sub_140003a90        ; drop_in_place&amp;lt;String&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea  rcx, [rbp+0x48]     ; &amp;amp;s
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call sub_140003a90        ; drop_in_place&amp;lt;String&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Four call sub_140003a90 instructions, each preceded by a lea loading a different stack offset. The LIFO order matches the MIR exactly.&lt;&#x2F;p&gt;
&lt;p&gt;This is the richest ownership signal in the entire debug binary. From this sequence alone, a reverse engineer can determine:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Four distinct heap-owning values were live in this function&lt;&#x2F;li&gt;
&lt;li&gt;They were heap-allocated (not borrowed — borrowed values do not get drop calls)&lt;&#x2F;li&gt;
&lt;li&gt;They were destroyed in LIFO order, consistent with Rust’s scoping rules&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;What cannot be determined from the drop sequence: the borrow structure (which values had borrows taken out against them), the lifetime relationships between the values, or whether any of the dropped values were ever borrowed mutably.&lt;&#x2F;p&gt;
&lt;p&gt;Note that b and a are notably absent from the drop sequence. a was moved into b, then b was moved into takes_ownership. Neither is dropped here -- the drop happened inside takes_ownership. This absence is itself an ownership signal: a missing drop implies a value was moved out rather than owned to scope end.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;drop-flags-the-debug-only-ownership-trace&quot;&gt;Drop Flags: The Debug-Only Ownership Trace&lt;&#x2F;h2&gt;
&lt;p&gt;The drop flag at rbp+0x167 toggles throughout the function. Here is the complete timeline:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;rust-ownership-compiled&#x2F;01.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The unwind handler sub_140002710 reads this flag with test byte [rbp+0x167], 0x1 and conditionally drops b only if the flag is set. This handles the case where a panic occurs after b is allocated but before it is moved into takes_ownership.&lt;&#x2F;p&gt;
&lt;p&gt;Drop flags are a direct artefact of Rust’s ownership model. No equivalent mechanism exists in C, because C has no compiler-enforced ownership and therefore no automatic destructor to conditionally invoke. The presence of these toggling booleans is a Rust-specific binary signature — though one that only appears in debug builds.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;&quot;&gt;Release Build: What Remains&lt;&#x2F;h2&gt;
&lt;p&gt;The release build (sub_140001000) is substantially more compact. Binary Ninja lifts most of it to HLIL pseudocode that resembles C. Here is the drop sequence equivalent:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Conditional HeapFree calls at function exit (0x140001234--0x140001276)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if (rdx_8 != 0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    j_sub_140001650(rax_2, rdx_8, 1)   &#x2F;&#x2F; free t (&amp;quot;foobar&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if (rdx_7 != 0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    j_sub_140001650(rax_5, rdx_7, 1)   &#x2F;&#x2F; free orig (&amp;quot;original&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if (rdx_6 != 0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    j_sub_140001650(rdx_6, ...)         &#x2F;&#x2F; free copy
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if (rdx_9 != 0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    j_sub_140001650(rax_1, rdx_9, 1)   &#x2F;&#x2F; free s (&amp;quot;world&amp;quot;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;j_sub_140001650 is a thunk to HeapFree. The null checks (!= 0 on the capacity field) are the release-mode equivalent of drop flags -- a static null check rather than a dynamic boolean. The LIFO ordering of the four frees is preserved.&lt;&#x2F;p&gt;
&lt;p&gt;The drop glue abstraction (drop_in_place&lt;String&gt;) is gone. There are no sub_140003a90 calls. Instead, the compiler inlined the destructor down to a direct conditional HeapFree.&lt;&#x2F;p&gt;
&lt;p&gt;Drop flags are entirely absent. The compiler proved statically that no value’s liveness is ambiguous at any unwind point in this function, so no runtime flags are needed.&lt;&#x2F;p&gt;
&lt;p&gt;For the borrows (r1, r2, m): they do not produce any binary output at all in the release build. The borrowed values are used transiently and optimised away -- no stack slots are allocated for them, no lea instructions appear. The entire s&#x2F;r1&#x2F;r2 pattern collapses to an allocation, a sub_140003b80 call (= println!), and a later HeapFree.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-1&quot;&gt;Comparison Table&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;rust-ownership-compiled&#x2F;02.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-2&quot;&gt;What This Means for Static Analysis&lt;&#x2F;h2&gt;
&lt;p&gt;The findings above have direct implications for anyone attempting to detect or characterise Rust malware through static binary analysis.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;What is recoverable without symbols:&lt;&#x2F;strong&gt; The drop sequence — both the count of owned heap values and their LIFO destruction order — is visible in both debug and release builds. The presence of drop_in_place calls (or inlined HeapFree sequences) is a Rust-characteristic pattern distinguishable from C&amp;#39;s manual free calls. In the debug build, drop flags are a reliable Rust-specific signal.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;What is not recoverable:&lt;&#x2F;strong&gt; Borrow relationships, lifetime scopes, and the exclusivity constraints enforced by the borrow checker are completely absent. A mutable borrow and a shared borrow compile to the same instruction. A move and a copy compile to the same field-copy sequence. The source-level ownership structure that a Rust programmer reasons about does not exist in the binary.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The asymmetry matters for detection:&lt;&#x2F;strong&gt; Rust’s drop glue density (number of drop_in_place calls per function) is measurably higher than equivalent C code for the same data structure manipulation. This is because Rust inserts destructors at every scope exit for every owned value, whereas C requires the programmer to insert free calls manually. This difference in destructor density is a detectable binary signature even in stripped, release-mode binaries.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Drop flags are a debug-only signal:&lt;&#x2F;strong&gt; If you are analysing a debug build (which some deployed malware is, particularly early variants during development), the presence of toggling boolean slots adjacent to String or Vec stack frames is a strong Rust indicator. In release builds, the flags are gone, but the conditional null-checked HeapFree pattern at function exits is structurally distinct from typical C cleanup code.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-3&quot;&gt;Connection to the Akira v2 Analysis&lt;&#x2F;h2&gt;
&lt;p&gt;The patterns identified here correspond directly to what was observed in the Akira v2 ransomware binary. In the lock function analysis, three of the same patterns appear:&lt;&#x2F;p&gt;
&lt;p&gt;The files.into_iter() move at the loop setup (0x4d398x region) produces three field copies of the Vec&lt;PathBuf&gt; fat pointer -- exactly the same three-field pattern seen here for String. No source invalidation. The IntoIter::drop call at 0x4d3a2a is the downstream evidence of the move, analogous to the sub_140003a90 calls at scope exit here.&lt;&#x2F;p&gt;
&lt;p&gt;The MutexGuard RAII drop at 0x4d3a0b and 0x4d3b40 is the same drop_in_place pattern. It confirms ownership of the guard, not the borrow constraints the guard enforced.&lt;&#x2F;p&gt;
&lt;p&gt;The Arc reference counting (lock inc qword [rax] at 0x4d3e7e) is a pattern that does not appear in the simple String programme above, because Arc introduces reference-counted shared ownership that has no equivalent here. In Akira, the lock inc is the only binary trace of the Thread&#x2F;JoinHandle shared ownership -- but it encodes only that a reference count was incremented, not the borrow rules on the Arc&amp;#39;s contents.&lt;&#x2F;p&gt;
&lt;p&gt;In both the controlled programme and the Akira binary, the same conclusion holds: the drop sequence survives, borrow semantics do not.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-4&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Rust’s ownership system is a compile-time abstraction. It exists fully at the source level, partially at MIR (where drop flags and scope annotations are still present), and residually at the binary level (where only the consequences of ownership — destructor calls and drop flags — survive).&lt;&#x2F;p&gt;
&lt;p&gt;The key findings from this analysis:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Moves leave no direct trace.&lt;&#x2F;strong&gt; The three-field copy pattern is identical to any struct copy. The only indirect evidence is a drop flag toggle (debug builds) or the absence of a subsequent drop (both builds).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Borrow semantics are completely absent.&lt;&#x2F;strong&gt; Shared borrows and mutable borrows compile to the same lea&#x2F;mov pattern. Lifetime bounds produce no binary output at all.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Drop glue is the primary ownership artefact.&lt;&#x2F;strong&gt; The call sequence to drop_in_place (or the inlined HeapFree equivalent in release) is the most reliable ownership signal available in the binary. Its presence confirms heap ownership; its absence at a call site implies a value was moved rather than owned to scope end.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Drop flags are a Rust-specific debug signal.&lt;&#x2F;strong&gt; The toggling boolean slots are generated specifically to support Rust’s unwind-safe ownership model and have no direct equivalent in C or C++ binaries.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The information loss is architectural, not a tooling limitation.&lt;&#x2F;strong&gt; Better decompilers will not recover borrow semantics, because the information does not exist in the binary. This is a fundamental consequence of ownership being a compile-time abstraction that the code generator does not need to preserve.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For static malware detection, this means that ownership-based characterisation of Rust binaries must focus on what survives: destructor density, drop flag patterns, Arc refcount operations, and the structural shape of scope-exit cleanup code. The richer ownership semantics that distinguish Rust from C at the source level are, by compilation, rendered invisible.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Reversing Akira&#x27;s Rust Experiment: A Breakdown, Part 3</title>
        <published>2026-05-27T00:00:00+00:00</published>
        <updated>2026-05-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yenwang.dev/blog/medium-akira-part-3/"/>
        <id>https://yenwang.dev/blog/medium-akira-part-3/</id>
        
        <content type="html" xml:base="https://yenwang.dev/blog/medium-akira-part-3/">&lt;blockquote&gt;
&lt;p&gt;This post is a breakdown and commentary on &lt;strong&gt;Ben Herzog’s Check Point Research&lt;&#x2F;strong&gt; (December 2024), &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;Inside Akira Ransomware’s Rust Experiment&lt;&#x2F;a&gt; &amp;amp; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.virusbulletin.com&#x2F;conference&#x2F;vb2025&#x2F;abstracts&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;his talk in 2025&lt;&#x2F;a&gt;. All technical claims are grounded in the linked primary sources below. Combined with some methodologies of &lt;strong&gt;Cindy Xiao&lt;&#x2F;strong&gt;’s amazing reports&#x2F;talks in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;rust-reversing-workshop-recon-2024&quot;&gt;2024&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;reconstructing-rust-types-talk-re-verse-2025&quot;&gt;2025&lt;&#x2F;a&gt; (mainly for reconstructing types).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;01.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you haven’t read &lt;a href=&quot;https:&#x2F;&#x2F;yenwang.dev&#x2F;blog&#x2F;medium-akira-part-1&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; and Part 2, it’s encouraged to read the threat reports at least (see &lt;strong&gt;References — Threat Reports&lt;&#x2F;strong&gt; below), so the activities of the binary are clear alongside the binary analysis, including the latest &lt;strong&gt;Tactics, Techniques, and Procedures (TTPs).&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;&quot;&gt;Table of Contents&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Info of the binary in this analysis&lt;&#x2F;li&gt;
&lt;li&gt;Section 5: lock and Thread Spawning&lt;&#x2F;li&gt;
&lt;li&gt;Section 6: Lock_Closure and Encryption&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;-1&quot;&gt;Further Reading&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;Why UAWAVAUATSH exists&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;REX — Register Extension.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Mutex&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;-2&quot;&gt;References&lt;&#x2F;h3&gt;
&lt;h2 id=&quot;-3&quot;&gt;Reversing Akira’s Rust Experiment: A Breakdown Part 1&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This post is a breakdown and commentary on &lt;strong&gt;Ben Herzog’s Check Point Research&lt;&#x2F;strong&gt; (December 2024), &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;Inside Akira Ransomware’s Rust Experiment&lt;&#x2F;a&gt; &amp;amp; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.virusbulletin.com&#x2F;conference&#x2F;vb2025&#x2F;abstracts&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;his talk in 2025&lt;&#x2F;a&gt;. All technical claims are grounded in the linked primary sources below. Combined with &lt;strong&gt;Cindy Xiao&lt;&#x2F;strong&gt;’s amazing reports&#x2F;talks in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;rust-reversing-workshop-recon-2024&quot;&gt;2024&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;reconstructing-rust-types-talk-re-verse-2025&quot;&gt;2025&lt;&#x2F;a&gt; (about reconstructing types and more).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;There will be a part 2 to break down the rest. I separate them so this article won’t be &lt;strong&gt;&lt;em&gt;_&lt;em&gt;too_long&lt;&#x2F;em&gt;&lt;&#x2F;em&gt;_&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;02.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For this analysis, Binary Ninja (v5.3.9434) is used without enabling any plugins. &lt;strong&gt;Binary:&lt;&#x2F;strong&gt; akirav2.elf , SHA25:60ee1d284ed663073872012c7bde7fac5ca1121403f1a5d2d5411317df282796c.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-4&quot;&gt;A note on source references&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The commit hash is updated on 10th Jun 2026. &lt;strong&gt;&lt;em&gt;79e9716c…&lt;&#x2F;em&gt; is rustc 1.74.0 stable&lt;&#x2F;strong&gt; (released 2023-11-16). Akirav2’s &lt;em&gt;.rodata&lt;&#x2F;em&gt; contains std panic-path strings of the form &lt;em&gt;&#x2F;rustc&#x2F;&lt;commit&gt;&#x2F;library&#x2F;...&lt;&#x2F;em&gt;.&lt;strong&gt;&lt;em&gt;db034cee…&lt;&#x2F;em&gt; is a&lt;&#x2F;strong&gt; &lt;strong&gt;&lt;em&gt;master&lt;&#x2F;em&gt;&#x2F;nightly commit from ~1 year later,&lt;&#x2F;strong&gt; which I was mistakenly pinned in the article. If you see any links related to &lt;em&gt;&lt;strong&gt;db034cee… ,&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; &lt;em&gt;please consider it was based on previosu wrong assumption but it works to some degree.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The Rust standard library is under active development and files move between releases. Source links in this post are pinned to commit 79e9716c980570bfd1f666e3b16ac583f0168962 — &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;releases.rs&#x2F;docs&#x2F;1.74.0&#x2F;&quot;&gt;&lt;strong&gt;rustc 1.74.0 stable&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;, the toolchain that compiled this Akira v2 binary. This is recoverable directly from the binary: its .rodata embeds &#x2F;rustc&#x2F;79e9716c…&#x2F;library&#x2F;... panic paths, and it is corroborated by version-pinned FunctionID analysis resolving the sample to 1.74.0.&lt;&#x2F;p&gt;
&lt;p&gt;This matters because stdlib code is inlined directly into the final binary at compile time: what you see in the disassembly reflects the stdlib at that specific commit, not the current main branch. Following a newer version of the same file risks misidentifying inlined boilerplate or drawing incorrect conclusions about what the malware authors wrote versus what the compiler contributed.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If a pinned link returns a 404, the file has likely been relocated. Navigate to the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;tree&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&quot;&gt;repository root&lt;&#x2F;a&gt; and search by filename.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-5&quot;&gt;Section 5: lock and Thread Spawning&lt;&#x2F;h2&gt;
&lt;p&gt;After flag processing, default_action calls lock, which is a thin wrapper. Its job is to spawn threads worker threads, each running lock_closure, and then join them. The structure mirrors yarnish.rs almost exactly, with the indicatif MultiProgress handle passed into each thread via Arc.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;03.webp&quot; alt=&quot;Xref panel of &lt;code&gt;lock&lt;&#x2F;code&gt;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Xref panel of `lock`&lt;&#x2F;p&gt;
&lt;p&gt;This binary is a bit different from &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;the CheckPoint report&lt;&#x2F;a&gt; that I didn’t find tick_chars(“⠁⠂⠄⡀⢀⠠⠐⠈ “) . Instead, it showed up as UTF-8 encoding.&lt;&#x2F;p&gt;
&lt;p&gt;If you check the source code of yarnish.rs :&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn main() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let mut rng = rand::rng();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let started = Instant::now();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let spinner_style = ProgressStyle::with_template(&amp;quot;{prefix:.bold.dim} {spinner} {wide_msg}&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .unwrap()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .tick_chars(&amp;quot;⠁⠂⠄⡀⢀⠠⠐⠈ &amp;quot;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;04.webp&quot; alt=&quot;In .rodata section, all the strings are combined without null-terminator&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;In .rodata section, all the strings are combined without null-terminator&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;05.webp&quot; alt=&quot;In &lt;code&gt;default_action&lt;&#x2F;code&gt;, the structure is clearer with the string slice&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;In `default_action`, the structure is clearer with the string slice&lt;&#x2F;p&gt;
&lt;p&gt;If you check the disassembly of default_action , you can see the pattern of source code yarnish.rs aligned in order.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Where are the&lt;&#x2F;strong&gt; &lt;strong&gt;tick_chars ?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;06.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The real situation&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The tick_chars(&amp;quot;⠁⠂⠄⡀⢀⠠⠐⠈ &amp;quot;) call is compiled in, but the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Braille&quot;&gt;Braille&lt;&#x2F;a&gt; spinner string is what is not finding. The figure above shows .rodata at 0x4414eb containing e2 a0 81 e2 a0 82 e2 a0 84 e2 a1 80 e2-a2 80 e2 a0 a0 e2 a0 90 followed by e2 a0 88 20 at 0x441500.&lt;&#x2F;p&gt;
&lt;p&gt;Decoding those as UTF-8:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;e2 a0 81 = ⠁   (U+2801)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;e2 a0 82 = ⠂   (U+2802)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;e2 a0 84 = ⠄   (U+2804)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;e2 a1 80 = ⡀   (U+2840)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;e2 a2 80 = ⢀   (U+2880)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;e2 a0 a0 = ⠠   (U+2820)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;e2 a0 90 = ⠐   (U+2810)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;e2 a0 88 = ⠈   (U+2808)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;20       = (space)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That is exactly &amp;quot;⠁⠂⠄⡀⢀⠠⠐⠈ &amp;quot;, the tick string from yarnish.rs, present verbatim in .rodata. Binary Ninja has labelled this region data_4414eb and is rendering the bytes as literal dots rather than recognising the Braille Unicode. The string is there; it is a display&#x2F;encoding issue in how Binary Ninja is showing that .rodata range.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;For&lt;&#x2F;strong&gt; &lt;strong&gt;yarnish.rs reconstruction&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The tick_chars call and its argument string are both present in the binary. Your reconstructed source should include .tick_chars(&amp;quot;⠁⠂⠄⡀⢀⠠⠐⠈ &amp;quot;). The chain is with_template(...).unwrap().tick_chars(...), matching yarnish.rs exactly.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;RE relevance:&lt;&#x2F;strong&gt; this is a clean example of a Unicode string literal surviving into &lt;em&gt;.rodata&lt;&#x2F;em&gt; with full fidelity, recoverable by UTF-8 decoding of the raw hex even when the decompiler renders it as garbage. Worth noting as a corpus evidence tier (&lt;em&gt;.rodata&lt;&#x2F;em&gt; string recovery) in your detection pipeline.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-6&quot;&gt;Reconstructing the lock function&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Before beginning reconstruction, the build environment is fixed from binary evidence.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;07.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The source file for lock is confirmed as akiranew&#x2F;src&#x2F;lock.rs from the .data.rel.ro log format metadata at 0x5e7028:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;quot;akiranew&#x2F;src&#x2F;lock.rs#&amp;gt;- File crypting... File lock started: , thread id: ...&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-7&quot;&gt;The Graphic Overview of lock&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;08.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-8&quot;&gt;The yarnish.rs template&lt;&#x2F;h2&gt;
&lt;p&gt;The structural origin of lock is based on&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;indicatif&amp;#39;s&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;yarnish.rs example&lt;&#x2F;a&gt;. The relevant section is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let handles: Vec&amp;lt;_&amp;gt; = (0..4u32)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    .map(|i| {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let count = rng.random_range(30..80);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let pb = m.add(ProgressBar::new(count));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        pb.set_style(spinner_style.clone());
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        pb.set_prefix(format!(&amp;quot;[{}&#x2F;?]&amp;quot;, i + 1));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        thread::spawn(move || {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            &#x2F;&#x2F; ... work ...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            pb.finish_with_message(&amp;quot;waiting...&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        })
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    })
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    .collect();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;for h in handles {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let _ = h.join();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;m.clear().unwrap();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The Akira authors adopted this skeleton and replaced the dummy work body with encryption logic, the fixed range (0..4u32) with an iterator over the file list, and the thread count with a runtime semaphore. The differences are documented in each step below.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-9&quot;&gt;Step 1 — Function prologue and argument layout&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d38a5 -- 0x4d38d2&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;09.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;push rbp, r15, r14, r13, r12, rbx
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;sub  rsp, 0x2a8
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The frame size is 0x2a8 (680 bytes). Six callee-saved registers are preserved, consistent with a function that holds many live values across multiple call sites. The following comments are based on Checkpoint report, if you was investigating without reviewing it, you will see more clues later.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;int64_t* akira_v2::lock::lock(
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    int64_t* arg1,   &#x2F;&#x2F; Vec&amp;lt;PathBuf&amp;gt; -- the file list
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    int64_t  arg2,   &#x2F;&#x2F; thread limit (u64)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    char*    arg3,   &#x2F;&#x2F; exclude regex (*const u8)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    int64_t* arg4    &#x2F;&#x2F; operator options struct
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The compiler saves the incoming registers to the stack immediately after the prologue. The System V AMD64 ABI, which governs all Linux x86–64 binaries including akirav2.elf, specifies that the first six integer or pointer arguments are passed in registers rdi, rsi, rdx, rcx, r8, r9 in that order (x86-psABIs project, 2024, §3.2.3).&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The authoritative primary source is the &lt;em&gt;&lt;strong&gt;S&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;yenwang.dev&#x2F;blog&#x2F;medium-akira-part-3&#x2F;gitlab.com&#x2F;x86-psABIs&#x2F;x86-64-ABI&quot;&gt;&lt;em&gt;&lt;strong&gt;ystem V Application Binary Interface AMD64 Architecture Processor Supplement&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;, version 1.0. Maintained at with the compiled PDF at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hjl-tools&#x2F;x86-psABI&#x2F;wiki&#x2F;x86-64-psABI-1.0.pdf&quot;&gt;github.com&#x2F;hjl-tools&#x2F;x86-psABI&#x2F;wiki&#x2F;x86–64-psABI-1.0.pdf&lt;&#x2F;a&gt;, section 3.2.3, Figure 3.4 (register usage table: &lt;em&gt;%rdi&lt;&#x2F;em&gt; = 1st argument, &lt;em&gt;%rsi&lt;&#x2F;em&gt; = 2nd, &lt;em&gt;%rdx&lt;&#x2F;em&gt; = 3rd, &lt;em&gt;%rcx&lt;&#x2F;em&gt; = 4th).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The save sequence in the binary is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  r12,  rdi                           ; r12    = arg1 (rdi)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rsp+0x188 {var_150}], rsi   ; var_150 = arg2 (rsi)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rsp+0x38  {var_2a0}], rdx   ; var_2a0 = arg3 (rdx)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rsp+0x40  {var_298}], rcx   ; var_298 = arg4 (rcx)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The compiler does not save in ABI order, it schedules saves however suits instruction throughput. The mapping from ABI position to stack slot must be read from the registers, not from the order of the mov instructions.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;arg1&lt;&#x2F;em&gt; is&lt;&#x2F;strong&gt; &lt;strong&gt;&lt;em&gt;Vec&lt;PathBuf&gt;&lt;&#x2F;em&gt; confirmed&lt;&#x2F;strong&gt; from the later &lt;em&gt;imul rcx, [r12+0x10], 0x18&lt;&#x2F;em&gt; (&lt;em&gt;capacity * 24&lt;&#x2F;em&gt;, where 24 = &lt;em&gt;sizeof(PathBuf)&lt;&#x2F;em&gt; = ptr + len + cap = 3 × 8 bytes).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The semantic labels for arg2 (rsi &#x2F; var_150), arg3 (rdx &#x2F; var_2a0), and arg4 (rcx &#x2F; var_298) are &lt;strong&gt;training-derived&lt;&#x2F;strong&gt;, which of these holds the thread limit, the exclude regex pointer, and the operator options struct requires tracing the read sites of each stack variable through the function body. Binary Ninja&amp;#39;s resolved signature labels them arg2: int64_t, arg3: char*, arg4: int64_t*, but the semantic assignment cannot be confirmed from the prologue alone.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-10&quot;&gt;Step 2 — ProgressStyle initialisation&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d38cb -- 0x4d390c&lt;&#x2F;p&gt;
&lt;p&gt;This is the direct binary equivalent of yarnish.rs:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let spinner_style = ProgressStyle::with_template(&amp;quot;...&amp;quot;).unwrap().tick_chars(&amp;quot;...&amp;quot;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea  rsi, [rel data_441490[0x24]]   ; template string in .rodata
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea  r15, [rsp+0x100 {var_1d8}]     ; destination on stack
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;push 0x37 &#x2F; pop rdx                 ; template length = 55 bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  rdi, r15
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call indicatif::style::ProgressStyle::with_template  ; 0x4d38e0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The with_template call returns Result&amp;lt;ProgressStyle, TemplateError&amp;gt;. The Result-returning signature confirms indicatif &amp;gt;= 0.17.0 (versions prior to 0.17 returned ProgressStyle directly). The .rodata panic path confirms 0.17.7 exactly.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call core::result::Result::unwrap    ; 0x4d38f4
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call indicatif::style::ProgressStyle::tick_chars  ; 0x4d3904
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The completed ProgressStyle lives in r15 &#x2F; var_1d8 for the duration of the function.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-11&quot;&gt;Step 3 — MultiProgress and Arc allocation&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d390c -- 0x4d3951&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;10.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Two consecutive exchange_malloc calls allocate the MultiProgress and its internal shared state. Both follow the same Arc field initialisation pattern:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call alloc::alloc::exchange_malloc  ; size=0x8, align=0x8 (first call)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rax],     r13  {0x1}    ; ArcInner.strong = 1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rax+0x8], r13  {0x1}    ; ArcInner.weak   = 1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;and  dword [rax+0x10], 0x0          ; ArcInner.data zeroed
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The field offsets are &lt;strong&gt;Verified&lt;&#x2F;strong&gt; from the pinned stdlib source at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;alloc&#x2F;src&#x2F;sync.rs&quot;&gt;library&#x2F;alloc&#x2F;src&#x2F;sync.rs&lt;&#x2F;a&gt;. ArcInner&lt;T&gt; is declared #[repr(C)], which guarantees field order matches declaration order. The fields in source order are:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#[repr(C)]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;struct ArcInner&amp;lt;T: ?Sized&amp;gt; {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    strong: atomic::AtomicUsize,   &#x2F;&#x2F; +0x00, 8 bytes on x86-64
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    weak:   atomic::AtomicUsize,   &#x2F;&#x2F; +0x08, 8 bytes on x86-64
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    data:   T,                     &#x2F;&#x2F; +0x10 (for T with align &amp;lt;= 8)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Arc::new initialises both counts to 1, confirmed in the same source file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let x: Box&amp;lt;_&amp;gt; = Box::new(ArcInner {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    strong: atomic::AtomicUsize::new(1),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    weak:   atomic::AtomicUsize::new(1),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    data,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The #[repr(C)] annotation is load-bearing: without it, Rust&amp;#39;s default repr(Rust) would permit field re-ordering and the offset claim would not be verifiable from source inspection alone.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call alloc::alloc::exchange_malloc   ; size=0x30, align=0x8 (second call)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; same initialisation pattern, larger payload
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The 48-byte second allocation is the MultiProgress inner state. These correspond to MultiProgress::new() in yarnish.rs. No MultiProgress::new symbol survives in the binary because the constructor is fully inlined.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-12&quot;&gt;Step 4 — File iterator construction&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d397d -- 0x4d39b0&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;11.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is the single most important structural divergence from yarnish.rs. The yarnish.rs example iterates (0..4u32) which is a fixed range. Akira iterates the file list directly:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  rax, qword [r12]           ; rax = files.ptr
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  rdx, qword [r12+0x8]       ; rdx = files.len
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;imul rcx, qword [r12+0x10], 0x18  ; rcx = files.cap * 24
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;add  rcx, rax                   ; rcx = end pointer (ptr + cap*stride)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [r15 {var_1d8}],       rax  ; IntoIter.buf   = ptr
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [r15+0x8 {var_1d0}],   rdx  ; IntoIter.len   = len
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [r15+0x10 {var_1c8}],  rax  ; IntoIter.start = ptr
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [r15+0x18 {i_1}],      rcx  ; IntoIter.end   = end ptr
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The imul multiplier 0x18 (24) equals sizeof(PathBuf) on x86-64: PathBuf = Vec&lt;u8&gt; = (ptr: *mut u8, len: usize, cap: usize) = 3 × 8 = 24 bytes. This construction is the IntoIter&lt;PathBuf&gt; struct being built on the stack.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Loop head at&lt;&#x2F;strong&gt; &lt;strong&gt;0x4d39b0:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cmp  rax, rcx   ; current_ptr == end_ptr?
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;je   0x4d3a22   ; yes → drop IntoIter and exit
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Loop exit at&lt;&#x2F;strong&gt; &lt;strong&gt;0x4d3a22:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call _$LTalloc..vec..into_it...op..Drop$GT$::drop
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The IntoIter destructor runs when the file list is exhausted. This is the outer boundary of the entire map(...).collect() equivalent.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-13&quot;&gt;Step 5 — Per-iteration seed generation (Akira-specific)&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d3c69 -- 0x4d3d1c&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;12.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There is no equivalent in yarnish.rs. This block is unique to Akira.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;SystemTime::now and&lt;&#x2F;strong&gt; &lt;strong&gt;duration_since&lt;&#x2F;strong&gt;(symbol names read directly from disassembly). Signatures are defined in the pinned stdlib at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;time.rs&quot;&gt;library&#x2F;std&#x2F;src&#x2F;time.rs&lt;&#x2F;a&gt;. SystemTime::now() returns the current wall-clock time. SystemTime::duration_since returns Result&amp;lt;Duration, SystemTimeError&amp;gt;; the jne 0x4d42de at 0x4d3c82 is the error branch for the SystemTimeError case (time went backwards).&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call std::time::SystemTime::now         ; 0x4d3c69 -- Verified
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rsp+0x50 {var_288}], rax    ; store seconds
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  dword [rsp+0x58 {var_280_1}], edx  ; store subsec_nanos
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  rdi, r12  {var_1d8}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  rsi, r13  {var_288}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call std::time::SystemTime::duration_since  ; 0x4d3c7d -- Verified
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cmp  qword [rsp+0x100 {var_1d8}], 0x0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;jne  0x4d42de                           ; error path
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;getpid&lt;&#x2F;strong&gt;(symbol name read directly from disassembly). getpid() is a POSIX system call returning the calling process&amp;#39;s PID as pid_t (a 32-bit integer on Linux x86-64), returned in eax (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man2&#x2F;getpid.2.html&quot;&gt;Linux man page: getpid(2)&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call qword [rel getpid]                 ; 0x4d3c99 -- Verified
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  dword [rsp+0x98 {var_240_1}], eax  ; pid stored as u32
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  byte  [rsp+0xa8 {var_230_1}], 0x1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rsp+0x9c {var_23c_1}], 0xde ; constant -- see note below
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;and  dword [rsp+0xa4 {var_234_1}], 0x0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note on&lt;&#x2F;strong&gt; &lt;strong&gt;&lt;em&gt;0xde&lt;&#x2F;em&gt;:&lt;&#x2F;strong&gt; the constant at &lt;em&gt;+0x9c&lt;&#x2F;em&gt; is binary-observed. Its semantic meaning whether it is a version tag, a seed format discriminant, or an unrelated constant is &lt;strong&gt;unknown&lt;&#x2F;strong&gt; and cannot be determined without source code for &lt;em&gt;akiranew&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;HashStruct::hash&lt;&#x2F;strong&gt; (mangled symbol name read from disassembly). This is an internal function in the akiranew workspace crate; no public source is available. The 64-byte output size is &lt;strong&gt;hand-calculated&lt;&#x2F;strong&gt; from the four consecutive movups instructions each loading 16 bytes (4 × 16 = 64):&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call akira_v2::prng::HashStruct::hash::h869e36ed471afafc  ; 0x4d3ce0 -- Verified (symbol)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;movups xmm0, xmmword [rsp+0x50 {var_288}]   ; bytes  0-15  -- Hand-calculated
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;movups xmm1, xmmword [rsp+0x60 {var_278}]   ; bytes 16-31
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;movups xmm2, xmmword [rsp+0x90 {var_248_1}] ; bytes 32-47
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;movups xmm3, xmmword [rsp+0xa0 {var_23c_1+0x4}] ; bytes 48-63
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The interpretation of these 64 bytes as encryption key material is &lt;strong&gt;functional inference&lt;&#x2F;strong&gt;, the output is passed into the closure capture struct (Step 8) and ultimately into lock_closure, but the claim that it constitutes a key rather than, say, an IV or a nonce requires further analysis of lock_closure to confirm.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Log string evidence&lt;&#x2F;strong&gt; (binary-observed from .data.rel.ro at data_5e7018 &#x2F; 0x5e7018): the string &amp;quot;File crypting... File lock started: , thread id: ...&amp;quot; is emitted from akiranew&#x2F;src&#x2F;lock.rs and references a thread ID, consistent with getpid() being used to identify threads. This is corroborating evidence, not proof of the key derivation mechanism.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-14&quot;&gt;Step 6 — ProgressBar construction and MultiProgress::add&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d3d29 -- 0x4d3d83&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;13.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call alloc::alloc::exchange_malloc  ; size=0x58 (88 bytes) -- ProgressBar Arc
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rax],     r15  {0x1}    ; strong = 1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rax+0x8], r15  {0x1}    ; weak   = 1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;and  dword [rax+0x10], 0x0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  byte  [rax+0x14], 0x0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea  rdi,  [rax+0x15]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;push 0x43 &#x2F; pop rdx  {0x43}         ; length = 67 -- the file path string length
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call qword [rel data_611b10] {memcpy}  ; copy file path into ProgressBar prefix
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The file path (67 bytes) is copied directly into the ProgressBar internal buffer. In yarnish.rs, the prefix is format!(&amp;quot;[{}&#x2F;?]&amp;quot;, i + 1). In Akira, it is the target file path, giving the operator a live view of which file is being encrypted on each progress bar line.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;14.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call indicatif::draw_target::...DrawTarget::stderr   ; 0x4d3d65
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call indicatif::progress_bar::...::with_draw_target  ; 0x4d3d73
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call indicatif::multi::MultiProgress::add            ; 0x4d3d83
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is let pb = m.add(ProgressBar::new(count)) from yarnish.rs, expanded into three calls because ProgressBar::new and the draw target setup are not merged by the compiler at this optimisation level. The result (the managed ProgressBar) is held in r13 &#x2F; var_288.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-15&quot;&gt;Step 7 — Shared mutex and thread-limit semaphore&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d3d91 -- 0x4d3dab&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;15.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-16&quot;&gt;Mutex&lt;T&gt;::lock and Result::unwrap&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The source for &lt;em&gt;Mutex::lock&lt;&#x2F;em&gt; at the pinned commit is at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sync&#x2F;mutex.rs&quot;&gt;&lt;em&gt;library&#x2F;std&#x2F;src&#x2F;sync&#x2F;mutex.rs&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; .&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call std::sync::mutex::Mutex&amp;lt;T&amp;gt;::lock   ; 0x4d3d91 -- Verified
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call core::result::Result::unwrap       ; 0x4d3da3 -- Verified
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The mutex at data_5ebe00 is a static object in .data.rel.ro. The claim that it is distinct from the MultiProgress internal mutex is a &lt;strong&gt;functional inference&lt;&#x2F;strong&gt; based on two different static addresses appearing at two different mutex call sites in the disassembly. What data_5ebe00 actually guards requires tracing its read sites, the thread-limit interpretation is not binary-confirmed.&lt;&#x2F;p&gt;
&lt;p&gt;Further into the loop body:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call std::thread::scoped::Sco...um_running_threads::hb34fb126c362d0f3  ; 0x4d4062
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;-17&quot;&gt;std::thread::scoped::Scope::num_running_threads.&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The function lives inside &lt;em&gt;ScopeData&lt;&#x2F;em&gt; in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;src&#x2F;std&#x2F;thread&#x2F;scoped.rs.html&quot;&gt;&lt;em&gt;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;scoped.rs&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub(super) struct ScopeData {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    num_running_threads: Atomic&amp;lt;usize&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    a_thread_panicked:   Atomic&amp;lt;bool&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    main_thread:         Thread,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Scoped threads were stabilised at Rust &lt;strong&gt;1.63.0&lt;&#x2F;strong&gt;, confirmed by the #[stable(feature = &amp;quot;scoped_threads&amp;quot;, since = &amp;quot;1.63.0&amp;quot;)] attribute visible in the same source file. The toolchain used to compile akirav2.elf is nightly-2024-10-06, which is well past 1.63, so the stable API is available.&lt;&#x2F;p&gt;
&lt;p&gt;The claim that num_running_threads is used as a semaphore enforcing arg2 as a thread limit is &lt;strong&gt;functional inference&lt;&#x2F;strong&gt;, the observation is that num_running_threads is called before Thread::new in the loop body, which is consistent with a throttle. The exact blocking mechanism (whether the loop spins, parks, or uses a condition variable, &lt;em&gt;A.K.A. condvar&lt;&#x2F;em&gt;) requires further disassembly of the code between 0x4d4062 and 0x4d3e7e.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-18&quot;&gt;Step 8 — Closure capture struct construction&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d406d -- 0x4d40a0&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;16.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;17.webp&quot; alt=&quot;Review &lt;code&gt;r13&lt;&#x2F;code&gt;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Review `r13`&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;exchange_malloc size and alignment&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call alloc::alloc::exchange_malloc   ; size=0x58 (88 bytes), align=0x8
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The field mapping for closure fields +0x00, +0x08, +0x10 is therefore:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;+0x00  r14  -- (ptr-sized value)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;+0x08  rbx  -- (ptr-sized value)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;+0x10  r13  -- output_capture: Option&amp;lt;Arc&amp;lt;Mutex&amp;lt;Vec&amp;lt;u8&amp;gt;&amp;gt;&amp;gt;&amp;gt; (return value of set_output_capture at 0x4d4026)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;+0x18  xmm0 -- 16 bytes from HashStruct::hash output
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;+0x28  rcx  -- pointer (var_268)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;+0x30  rcx  -- pointer (var_2b0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;+0x38  rcx  -- pointer (var_148_1)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;+0x10 resolved:&lt;&#x2F;strong&gt; At 0x4d4026, r13 is assigned the return value of std::io::stdio::set_output_capture, which returns Option&amp;lt;Arc&amp;lt;Mutex&amp;lt;Vec&lt;u8&gt;&amp;gt;&amp;gt;&amp;gt;, the previous stdout capture handle. The test rax, rax &#x2F; je 0x4d403e &#x2F; lock inc qword [r13] sequence immediately after (binary-observed) is the Option&lt;Arc&gt; handling: if Some, bump the refcount; if None, skip. This matches the output_capture variable captured by the main shim closure in the verified thread.rs source at the pinned commit:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;crate::io::set_output_capture(output_capture);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;-19&quot;&gt;PathBuf layout&lt;&#x2F;h3&gt;
&lt;p&gt;The layout of PathBuf on x86_64-unknown-linux-gnu at the pinned commit is (ptr, len, cap) = 3 × 8 = 24 bytes.&lt;&#x2F;p&gt;
&lt;p&gt;The proof chain is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;PathBuf { inner: OsString }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  → OsString { inner: Buf }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    → #[repr(transparent)] pub struct Buf { pub inner: Vec&amp;lt;u8&amp;gt; }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      → Vec&amp;lt;u8&amp;gt; = (ptr: *mut u8, len: usize, cap: usize)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;PathBuf { inner: OsString } (single-field, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;path.rs#L1174&quot;&gt;library&#x2F;std&#x2F;src&#x2F;path.rs&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;OsString { inner: Buf } (single-field, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;ffi&#x2F;os_str.rs#L1710&quot;&gt;library&#x2F;std&#x2F;src&#x2F;ffi&#x2F;os_str.rs&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;#[repr(transparent)] pub struct Buf { pub inner: Vec&lt;u8&gt; }&lt;br &#x2F;&gt;
(&lt;strong&gt;Verified&lt;&#x2F;strong&gt; from &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;nightly&#x2F;src&#x2F;std&#x2F;sys&#x2F;os_str&#x2F;bytes.rs.html&quot;&gt;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;os_str&#x2F;bytes.rs&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Vec&lt;u8&gt; = (ptr: *mut u8, len: usize, cap: usize) = 3 × 8 = 24 bytes.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The #[repr(transparent)] on Buf is the load-bearing guarantee: it makes the Vec&lt;u8&gt; equivalence a language specification rather than a compiler implementation detail. The two outer single-field structs (PathBuf, OsString) are not formally repr(transparent) and their layout is technically an implementation detail, but the chain is consistent with the imul rcx, [r12+0x10], 0x18 stride evidence in Step 4.&lt;&#x2F;p&gt;
&lt;p&gt;The field mapping for closure fields +0x00, +0x08, +0x10 is therefore:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;+0x00  r14  -- Vec&amp;lt;u8&amp;gt;.ptr  (PathBuf data pointer)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;+0x08  rbx  -- Vec&amp;lt;u8&amp;gt;.len  (PathBuf byte length)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;+0x10  r13  -- output_capture: Option&amp;lt;Arc&amp;lt;Mutex&amp;lt;Vec&amp;lt;u8&amp;gt;&amp;gt;&amp;gt;&amp;gt; (set_output_capture return value -- see above)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that +0x10 is &lt;strong&gt;not&lt;&#x2F;strong&gt; Vec&lt;u8&gt;.cap. The r13 register was loaded from set_output_capture at 0x4d4026, not from the PathBuf capacity field. The capacity field of PathBuf is not stored in the closure -- only ptr and len are needed to reference the file path slice inside lock_closure.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;+0x18 key material: Functional inference.&lt;&#x2F;strong&gt; The movups xmmword [rax+0x18], xmm0 stores the value in xmm0, which was loaded from the HashStruct::hash output region in Step 5. This is a register-trace inference across intervening instructions, not a direct binary observation of the key derivation path end-to-end.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Fields beyond&lt;&#x2F;strong&gt; &lt;strong&gt;+0x38.&lt;&#x2F;strong&gt; The exact field count and types beyond +0x38 require further disassembly of lock_closure.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-20&quot;&gt;Step 9 — First Thread::new: the encryption thread&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d3e6f -- 0x4d3e91&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;18.webp&quot; alt=&quot;Disassembly&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Disassembly&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;19.webp&quot; alt=&quot;HLIL in Binary Ninja&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;HLIL in Binary Ninja&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-21&quot;&gt;Starting from High Level IL&lt;&#x2F;h2&gt;
&lt;p&gt;**Thread::new call site (**&lt;strong&gt;0x4d3e7e):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;char const (** rax_24)[0x28] =
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    std::thread::Thread::new::h977b0e8df4ff7524()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Binary Ninja has typed rax_24 as char const (**)[0x28], a pointer to a pointer to a 0x28-byte (40-byte) array. This is Binary Ninja&amp;#39;s best-effort type inference for the return value. The key observation is that rax_24 is a &lt;strong&gt;pointer&lt;&#x2F;strong&gt;, not an integer. Thread::new is returning a pointer in rax.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;0x4d3e83:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;char const (** var_2c0)[0x28] = rax_24
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The return value is stored into var_2c0.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;0x4d3e8d:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;char const (* temp5_1)[0x28] = *rax_24
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;*rax_24 = &amp;amp;(**rax_24)[1]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the lock inc in HLIL form. Binary Ninja has lifted it as: dereference rax_24 to get temp5_1 (the current value), then write (**rax_24)[1] back, i.e., increment the qword at the address rax_24 points to. This is an atomic increment of a counter at the address returned by Thread::new.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;0x4d3e91:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if (temp5_1 s&amp;gt; -1)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the jle 0x4d42da overflow check. The s&amp;gt; -1 (signed greater than -1) is the jle taken when the incremented value was ≤ -1 (i.e., overflow&#x2F;underflow). This is exactly the Arc refcount overflow guard from &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;alloc&#x2F;src&#x2F;sync.rs&quot;&gt;sync.rs&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if old_size &amp;gt; MAX_REFCOUNT { abort(); }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;0x4d3e9a: the&lt;&#x2F;strong&gt; &lt;strong&gt;else branch (overflow path):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;uint64_t rax_25 = alloc::alloc::exchange_malloc::hc6dbbe43d4abbe5a .1241
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;*rax_25 = 1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;*(rax_25 + 8) = 1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;*(rax_25 + 0x10) = zx.o(0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;var_268 = rax_25
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A fresh allocation of size .1241 (some allocation size). Two fields set to 1, third field zeroed. This is &lt;strong&gt;another&lt;&#x2F;strong&gt; &lt;strong&gt;Arc allocation:&lt;&#x2F;strong&gt; strong=1, weak=1, data=0. The var_268 assignment is storing this new Arc pointer. This is the JoinHandle&amp;#39;s packet Arc being allocated in the success path of Thread::new.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-22&quot;&gt;Then Disassembly&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call std::sys_common::thread::min_stack   ; 0x4d3e6f
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rsp+0x1b0 {var_128_1}], rax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;xor  edi, edi  {0x0}                      ; stack = 0 (use default)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call std::thread::Thread::new             ; 0x4d3e7e
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;std::thread::Thread::new is observed at&lt;&#x2F;strong&gt; &lt;strong&gt;0x4d3e7e.&lt;&#x2F;strong&gt; The symbol name is read directly from the disassembly. The pinned source is at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&quot;&gt;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub unsafe fn new(stack: usize, p: Box&amp;lt;dyn FnOnce()&amp;gt;) -&amp;gt; io::Result&amp;lt;Thread&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The xor edi, edi setting stack = 0 is verified against the Thread::new source: when stack = 0 is passed, the implementation uses cmp::max(0, min_stack_size(&amp;amp;attr)), which collapses to min_stack_size. min_stack is called at 0x4d3e6f immediately before to cache that value.&lt;&#x2F;p&gt;
&lt;p&gt;The implementation details (double-boxing, Box::into_raw(Box::new(p)), pthread_create, thread_start) are &lt;strong&gt;Verified&lt;&#x2F;strong&gt; from &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&quot;&gt;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&lt;&#x2F;a&gt; at commit db034cee:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let p = Box::into_raw(Box::new(p));
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs#L99&quot;&gt;thread_start&lt;&#x2F;a&gt; is the extern &amp;quot;C&amp;quot; callback at the end of &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs#L48&quot;&gt;Thread::new&lt;&#x2F;a&gt; in the same file. After Thread::new returns:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rsp+0x18 {var_2c0}], rax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lock inc qword [rax]                ; Arc refcount bump
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;lock inc: Binary Ninja HLIL evidence.&lt;&#x2F;strong&gt; The HLIL at 0x4d3e7e--0x4d3e91 reads:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;char const (** rax_24)[0x28] =
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    std::thread::Thread::new::h977b0e8df4ff7524()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;char const (** var_2c0)[0x28] = rax_24
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;char const (* temp5_1)[0x28] = *rax_24
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;*rax_24 = &amp;amp;(**rax_24)[1]          &#x2F;&#x2F; ← this is the lock inc
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if (temp5_1 s&amp;gt; -1) { ... }        &#x2F;&#x2F; ← overflow guard
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The overflow guard if (temp5_1 s&amp;gt; -1) matches the Arc refcount overflow check in pasted &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;alloc&#x2F;src&#x2F;sync.rs&quot;&gt;alloc&#x2F;src&#x2F;sync.rs&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if old_size &amp;gt; MAX_REFCOUNT { abort(); }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;where MAX_REFCOUNT = (isize::MAX) as usize .&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;On x86–64, &lt;em&gt;isize::MAX = 2^63 - 1 = 9_223_372_036_854_775_807 = 0x7fff_ffff_ffff_ffff&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;At the pinned commit, Thread is defined in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;mod.rs&quot;&gt;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;mod.rs&lt;&#x2F;a&gt; as:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub struct Thread(Inner);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;enum Inner {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Main(&amp;amp;&amp;#39;static (ThreadId, Parker)),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Other(Pin&amp;lt;Arc&amp;lt;OtherInner&amp;gt;&amp;gt;),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The Arc being incremented by lock inc is therefore the Arc&lt;OtherInner&gt; inside Inner::Other, accessed via the Thread handle returned from Thread::new.&lt;&#x2F;p&gt;
&lt;p&gt;The subsequent basic block at 0x4d3e9a allocates JoinInner::packet :&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;struct JoinInner&amp;lt;&amp;#39;scope, T&amp;gt; {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    native: imp::Thread,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    thread: Thread,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    packet: Arc&amp;lt;Packet&amp;lt;&amp;#39;scope, T&amp;gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The Arc&lt;Packet&gt; is the communication channel between the spawned thread and its JoinHandle.&lt;&#x2F;p&gt;
&lt;p&gt;Then immediately:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;xor  edi, edi
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call std::io::stdio::set_output_capture   ; 0x4d3ec4
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is inside the inlined main shim closure body. The main shim is constructed in Builder::spawn_unchecked_ in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;mod.rs&quot;&gt;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;mod.rs&lt;&#x2F;a&gt; . The shim at db034cee is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let main = move || {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if let Err(_thread) = set_current(their_thread.clone()) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        rtabort!(&amp;quot;something here is badly broken!&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if let Some(name) = their_thread.cname() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        imp::Thread::set_name(name);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    crate::io::set_output_capture(output_capture);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let f = f.into_inner();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        crate::sys::backtrace::__rust_begin_short_backtrace(f)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;set_output_capture is defined at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;io&#x2F;stdio.rs&quot;&gt;library&#x2F;std&#x2F;src&#x2F;io&#x2F;stdio.rs&lt;&#x2F;a&gt;. It returns Option&amp;lt;Arc&amp;lt;Mutex&amp;lt;Vec&lt;u8&gt;&amp;gt;&amp;gt;&amp;gt;, the previous capture state. xor edi, edi passes None (null pointer as the first argument), confirmed by the SysV ABI (rdi = first argument, cited in Step 1 via x86-psABIs v1.0 §3.2.3).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-23&quot;&gt;Step 10 — Second Thread::new: the indicatif ticker thread&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d3fbf -- 0x4d4016&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;20.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;&lt;strong&gt;yarnish.rs&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; &lt;strong&gt;line&lt;&#x2F;strong&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs#L76&quot;&gt;&lt;strong&gt;pb.set_style(spinner_style.clone())&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;strong&gt;:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let handles: Vec&amp;lt;_&amp;gt; = (0..4u32)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .map(|i| {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            let count = rng.gen_range(30..80);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            let pb = m.add(ProgressBar::new(count));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            pb.set_style(spinner_style.clone());
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            pb.set_prefix(format!(&amp;quot;[{}&#x2F;?]&amp;quot;, i + 1));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            thread::spawn(move || {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                let mut rng = rand::thread_rng();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                let pkg = PACKAGES.choose(&amp;amp;mut rng).unwrap();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                for _ in 0..count {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    let cmd = COMMANDS.choose(&amp;amp;mut rng).unwrap();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    thread::sleep(Duration::from_millis(rng.gen_range(25..200)));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    pb.set_message(format!(&amp;quot;{pkg}: {cmd}&amp;quot;));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    pb.inc(1);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                pb.finish_with_message(&amp;quot;waiting...&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            })
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        })
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .collect();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This block maps directly to yarnish.rs lines 34--36 at commit &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs#L29&quot;&gt;0c037edc&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pb.set_style(spinner_style.clone());     &#x2F;&#x2F; yarnish.rs line 34
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pb.set_prefix(format!(&amp;quot;[{}&#x2F;?]&amp;quot;, i + 1)); &#x2F;&#x2F; yarnish.rs line 35
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;thread::spawn(move || { ... })           &#x2F;&#x2F; yarnish.rs line 36
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Ticker::drop_in_place at&lt;&#x2F;strong&gt; &lt;strong&gt;0x4d3f95&lt;&#x2F;strong&gt;. This is dropping the Ticker handle from the &lt;strong&gt;previous loop iteration&lt;&#x2F;strong&gt;, not spawning a new thread. The Ticker is an indicatif-internal struct managing the spinner animation.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call core::ptr::drop_in_place...ss_bar..Ticker$GT$  ; 0x4d3f95 -- Verified
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;pb.set_style(spinner_style.clone())&lt;&#x2F;strong&gt; (mapped to yarnish.rs line 34). ProgressStyle::clone produces the cloned style; set_style applies it to the ProgressBar:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call _$LT$indicatif::style::...Clone$GT$::clone     ; 0x4d3fbf -- Verified
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                                                     ; = spinner_style.clone()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  rdi, qword [rsp+0x50 {var_288}]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  rsi, r12  {var_1d8}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call indicatif::progress_bar::...ressBar::set_style  ; 0x4d3fd1 -- Verified
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                                                     ; = pb.set_style(...)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;-24&quot;&gt;&lt;strong&gt;thread::spawn(move || { ... }) -- the spawn call:&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This is the &lt;strong&gt;same encryption thread&lt;&#x2F;strong&gt; spawn as Step 9, not a separate ticker thread. The thread::spawn call chain is inlined; Thread::new at 0x4d3fe3 is its innermost call:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call std::sys_common::thread::min_stack              ; 0x4d3fd9 -- Verified
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  r15, rax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;xor  edi, edi                                        ; stack = 0 (default)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call std::thread::Thread::new                        ; 0x4d3fe3 -- Verified
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                                                     ; = thread::spawn(move || {...})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;ProgressStyle::clone + set_style before Thread::new does &lt;strong&gt;not&lt;&#x2F;strong&gt; indicate a separate enable_steady_tick ticker thread. It maps to pb.set_style(spinner_style.clone()) in yarnish.rs. There is only &lt;strong&gt;one&lt;&#x2F;strong&gt; encryption-related Thread::new call visible in the screenshots provided. Whether enable_steady_tick spawns a second thread cannot be confirmed without additional disassembly showing a second Thread::new call.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Arc::new and&lt;&#x2F;strong&gt; &lt;strong&gt;lock inc after&lt;&#x2F;strong&gt; &lt;strong&gt;Thread::new&lt;&#x2F;strong&gt; -- &lt;strong&gt;Verified&lt;&#x2F;strong&gt; as part of JoinInner construction (established from HLIL in Step 9):&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call alloc::sync::Arc&amp;lt;T&amp;gt;::new        ; 0x4d4008 -- Verified
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  qword [rsp+0x70 {var_268}], rax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lock inc qword [rax]                 ; 0x4d4012 -- Verified (Arc::clone)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Arc::clone confirmed from pasted &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;alloc&#x2F;src&#x2F;sync.rs&quot;&gt;alloc&#x2F;src&#x2F;sync.rs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-25&quot;&gt;Step 11 —Mutex-protected Vec::push and loop back-edge&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d39e0 -- 0x4d3a20&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;21.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The Vec::push at 0x4d39ff does not occur in isolation, it is wrapped in a Mutex::lock guard:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d39e0   call std::sync::mutex::Mutex&amp;lt;T&amp;gt;::lock    ; acquire Mutex&amp;lt;Vec&amp;lt;JoinHandle&amp;gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d39eb   mov  rsi, r12  {data_5e7080}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d39ee   call core::result::Result::unwrap
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d39f3   mov  r13, rax                            ; r13 = MutexGuard
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d39ff   call alloc::vec::Vec::push               ; push JoinHandle under guard
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d3a0b   call core::ptr::drop_in_place(...)       ; drop MutexGuard (unlock)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d3a10   mov  rax, qword [rsp+0x110 {var_1c8}]    ; IntoIter.start advance
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d3a18   mov  rcx, qword [rsp+0x118 {i_1}]        ; IntoIter.end
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d3a20   jmp  0x4d39b0                            ; loop back-edge
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;The&lt;&#x2F;strong&gt; &lt;strong&gt;Mutex is Akira&amp;#39;s own code, not from the stdlib -- Verified&lt;&#x2F;strong&gt; from the pasted &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;scoped.rs&quot;&gt;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;scoped.rs&lt;&#x2F;a&gt;. ScopeData contains no Mutex whatsoever:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub(super) struct ScopeData {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    num_running_threads: AtomicUsize,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    a_thread_panicked: AtomicBool,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    main_thread: Thread,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The scope() function waits for all threads using park&#x2F;unpark exclusively -&lt;strong&gt;(Verified&lt;&#x2F;strong&gt; from the pasted scoped.rs):&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;while scope.data.num_running_threads.load(Ordering::Acquire) != 0 {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    park();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each finishing thread calls self.main_thread.unpark() via decrement_num_running_threads. There is no Mutex, no Condvar, and no Vec anywhere in the scoped thread stdlib machinery at db034cee. The Mutex at data_5e7080 therefore belongs to akiranew&#x2F;src&#x2F;lock.rs.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;What the&lt;&#x2F;strong&gt; &lt;strong&gt;Mutex guards -- unknown.&lt;&#x2F;strong&gt; The Vec::push argument is var_d0 &#x2F; r15 (binary-observed). Tracing r15 back to its assignment site has not been completed from the available screenshots. The guarded Vec could hold encrypted file paths, progress records, ransom note entries, or something else entirely. The earlier label Vec&lt;JoinHandle&gt; was &lt;strong&gt;wrong&lt;&#x2F;strong&gt; and is retracted -- scoped threads do not need a Mutex&lt;Vec&gt; for handle management.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mutex::lock&lt;&#x2F;strong&gt; : Source at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sync&#x2F;mutex.rs&quot;&gt;library&#x2F;std&#x2F;src&#x2F;sync&#x2F;mutex.rs&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Vec::push&lt;&#x2F;strong&gt;: Source at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;alloc&#x2F;src&#x2F;vec&#x2F;mod.rs&quot;&gt;library&#x2F;alloc&#x2F;src&#x2F;vec&#x2F;mod.rs&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;MutexGuard drop: Verified&lt;&#x2F;strong&gt; (symbol binary-observed).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Loop back-edge&lt;&#x2F;strong&gt; — &lt;strong&gt;Verified&lt;&#x2F;strong&gt; (binary-observed). jmp 0x4d39b0 returns to the loop head from Step 4.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;yarnish.rs comparison:&lt;&#x2F;strong&gt; No mutex appears in the yarnish.rs template at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs#L29&quot;&gt;0c037edc&lt;&#x2F;a&gt;. The presence of a mutex-protected Vec::push in every loop iteration is an Akira-specific addition whose purpose requires further analysis of lock.rs.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;More details about tracing back mutex can be found in the sectin of &lt;strong&gt;Further Reading&lt;&#x2F;strong&gt; below.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Akira deviation from&lt;&#x2F;strong&gt; &lt;strong&gt;yarnish.rs:&lt;&#x2F;strong&gt; The yarnish.rs template at commit &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs#L29&quot;&gt;0c037edc&lt;&#x2F;a&gt; pushes handles into a plain Vec&amp;lt;_&amp;gt; with no mutex:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}).collect();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Akira wraps the Vec&lt;JoinHandle&gt; in a Mutex, acquiring it before each push. This is &lt;strong&gt;binary-observed&lt;&#x2F;strong&gt;, no such mutex appears in yarnish.rs. The mutex at data_5e7080 is distinct from the one at data_5ebe00 seen in Step 7.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-26&quot;&gt;Step 11b — Real Thread::new: sys::unix::thread::Thread::new&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d4062 -- 0x4d40d2&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;22.webp&quot; alt=&quot;High Level IL&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;High Level IL&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;23.webp&quot; alt=&quot;Disassembly&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Disassembly&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;24.webp&quot; alt=&quot;Graphi View&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Graphi View&lt;&#x2F;p&gt;
&lt;p&gt;The graph view reveals the &lt;strong&gt;actual&lt;&#x2F;strong&gt; pthread_create-invoking Thread::new, which was not visible in the earlier screenshots.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;num_running_threads and closure struct&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d405e   add  rdi, 0x10
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d4062   call std::thread::scoped::Sco...um_running_threads  ; semaphore check
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Immediately after the semaphore check, the closure capture struct is built:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d406d   push 0x58 &#x2F; pop rdi {0x58}              ; size = 88 bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d406c   push 0x8  &#x2F; pop rsi {0x8}               ; align = 8
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d406d   call alloc::alloc::exchange_malloc      ; allocate closure struct
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then 14 mov instructions fill the struct fields from stack variables (var_288, var_278, var_2b0, var_148_1, var_150, var_2a8 etc.) followed by two movups for the XMM key material, and at 0x4d40cb:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d40cb   lea  rcx, [rel data_5e66a8]              ; vtable pointer
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then the actual spawn:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d40d2   call std::sys::unix::thread::Thread::new::hd257ed4d46662134
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is a different mangled symbol from&lt;&#x2F;strong&gt; &lt;strong&gt;0x4d3e7e&lt;&#x2F;strong&gt; (h977b0e8df4ff7524).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The symbol hd257ed4d46662134 resolves to std::sys::unix::thread::Thread::new, the PAL-level function in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&quot;&gt;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&lt;&#x2F;a&gt; that calls pthread_create directly. This is imp::Thread::new in the spawn_unchecked_ closing lines (&lt;strong&gt;Verified&lt;&#x2F;strong&gt; from &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;mod.rs&quot;&gt;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;mod.rs&lt;&#x2F;a&gt;):&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Ok(JoinInner {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    native: unsafe { imp::Thread::new(stack_size, main)? },
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    thread: my_thread,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    packet: my_packet,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;JoinInner is defined in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;mod.rs&quot;&gt;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;mod.rs&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;struct JoinInner&amp;lt;&amp;#39;scope, T&amp;gt; {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    native: imp::Thread,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    thread: Thread,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    packet: Arc&amp;lt;Packet&amp;lt;&amp;#39;scope, T&amp;gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Thread at db034cee is pub struct Thread(Inner) where Inner is an enum with Other(Pin&amp;lt;Arc&lt;OtherInner&gt;&amp;gt;)&lt;strong&gt;(Verified&lt;&#x2F;strong&gt; from &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;mod.rs&quot;&gt;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;mod.rs&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Error path after&lt;&#x2F;strong&gt; &lt;strong&gt;Thread::new&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d40f2   call core::ptr::drop_in_place...P$$RPS$GT$  ; drop Box&amp;lt;dyn FnOnce()&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d40fc   call core::ptr::drop_in_place...Thread$GT$  ; drop partial Thread
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This matches the verified sys&#x2F;pal&#x2F;unix&#x2F;thread.rs source:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;return if ret != 0 {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    drop(Box::from_raw(p));   &#x2F;&#x2F; drop the double-boxed closure
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Err(io::Error::from_raw_os_error(ret))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;} else {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Ok(Thread { id: native })
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The data_5e66a8 vtable pointer at 0x4d40cb is the Box&amp;lt;dyn FnOnce()&amp;gt; vtable, the call_once slot pointing to the main shim, as described in the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;report&lt;&#x2F;a&gt; (Herzog, 2024, Figures 24--26).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-27&quot;&gt;Step 12 — Loop back-edge and IntoIter drop&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d3a10 -- 0x4d3a22&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;25.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  rax, qword [rsp+0x110 {var_1c8}]   ; load updated IntoIter.start
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov  rcx, qword [rsp+0x118 {i_1}]       ; load IntoIter.end
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;jmp  0x4d39b0                           ; back to loop head
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When start == end, execution falls through to:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call _$LTalloc..vec..into_it...op..Drop$GT$::drop  ; 0x4d3a2a
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The file list memory is freed. The Vec&lt;JoinHandle&gt; now contains one handle per file. A post-loop log call confirms completion before the join phase.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-28&quot;&gt;Step 13 — Join loop and Mutex cleanup&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Address range:&lt;&#x2F;strong&gt; 0x4d3b40 -- 0x4d3b9f&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;26.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call std::sync::mutex::Mutex&amp;lt;T&amp;gt;::lock    ; 0x4d3b43  (data_5e7028)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call core::result::Result::unwrap
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call std::sync::mutex::Mutex&amp;lt;T&amp;gt;::lock    ; 0x4d3b73  (data_5e7040)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call core::result::Result::unwrap
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call core::ptr::drop_in_place(...)       ; MutexGuard drop
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The two static addresses data_5e7028 and data_5e7040 are now fully identified from .data.rel.ro. Binary Ninja&amp;#39;s annotation shows both point into the log format vtable for &amp;quot;File lock started: , thread id: ...&amp;quot;, these are not user-visible mutex objects but the Debug formatter vtables for the log call arguments. The actual join loop (pthread_join via Thread::join) follows:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;call Thread::join  →  libc::pthread_join(id, ptr::null_mut())
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This corresponds to yarnish.rs:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;for h in handles { let _ = h.join(); }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-29&quot;&gt;Reconstructed source&lt;&#x2F;h2&gt;
&lt;p&gt;Combining all steps, the reconstructed lock function is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn lock(
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    files: Vec&amp;lt;PathBuf&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    thread_limit: u64,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    exclude: *const u8,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    opts: *const OperatorOptions,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Step 2: ProgressStyle
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let spinner_style = ProgressStyle::with_template(&amp;quot;...&amp;quot;).unwrap()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .tick_chars(&amp;quot;...&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Step 3: MultiProgress
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let m = MultiProgress::new();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Step 7: scoped thread scope with semaphore
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    std::thread::scope(|scope| {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &#x2F;&#x2F; Step 4: iterate files (NOT (0..n) as in yarnish.rs)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let handles: Vec&amp;lt;_&amp;gt; = files.into_iter().map(|path| {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            &#x2F;&#x2F; Step 5: per-file seed (Akira-specific)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            let now = SystemTime::now()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                .duration_since(UNIX_EPOCH)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                .unwrap();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            let pid = unsafe { libc::getpid() };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            let key = akira_v2::prng::HashStruct::hash(now, pid);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            &#x2F;&#x2F; key = 64 bytes stored in closure
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            &#x2F;&#x2F; Step 6: ProgressBar with file path as prefix
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            let pb = m.add(ProgressBar::new(...));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            pb.set_style(spinner_style.clone());  &#x2F;&#x2F; Step 10: yarnish.rs line 34
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            pb.set_prefix(path.display().to_string());  &#x2F;&#x2F; file path, not &amp;quot;[i&#x2F;?]&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            &#x2F;&#x2F; Note: enable_steady_tick presence unconfirmed from screenshots
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            &#x2F;&#x2F; Step 7: wait for semaphore slot
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            while scope.num_running_threads() &amp;gt;= thread_limit as usize {}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            &#x2F;&#x2F; Step 9&#x2F;10: spawn encryption thread
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            scope.spawn(move || {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                log::info!(&amp;quot;File lock started: , thread id: {}&amp;quot;, ...);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                lock_closure(path, key, pb, ...);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            })
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        }).collect();  &#x2F;&#x2F; Step 11: Mutex&amp;lt;Vec&amp;lt;??&amp;gt;&amp;gt; -- contents unknown
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &#x2F;&#x2F; Step 13: join all
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        for h in handles { h.join().unwrap(); }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    });
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-30&quot;&gt;Section 7: lock_closure and Encryption&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lock (user code, inlined)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  → Builder::spawn_unchecked_ (inlined)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    → main shim (inlined)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      → catch_unwind (inlined)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        → __rust_begin_short_backtrace (NOT inlined -- #[inline(never)])
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;          → lock_closure (the encryption function)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;27.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;28.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is the symmetric key generated by random number (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;rand&#x2F;latest&#x2F;src&#x2F;rand&#x2F;rngs&#x2F;thread.rs.html#137-148&quot;&gt;ThreadRng&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;29.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As identifed in CheckPoint report, Akirav2 malware author used &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dalek.rs&#x2F;&quot;&gt;dalek.rs&lt;&#x2F;a&gt;- &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dalek-cryptography&#x2F;curve25519-dalek&#x2F;tree&#x2F;main&#x2F;curve25519-dalek&quot;&gt;curve25519&lt;&#x2F;a&gt; to implement “a high-speed, secure elliptic curve used primarily for ECDH key exchange (X25519) and digital signatures (Ed25519), offering 128-bit security with 256-bit keys” (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@aditrizky052&#x2F;unlocking-the-power-of-curve25519-ed25519-x25519-the-modern-pillars-of-secure-and-high-speed-a3daefbad0a4&quot;&gt;source&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;30.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;31.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;32.webp&quot; alt=&quot;Ransom note&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Ransom note&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Correction (24 June 2026)&lt;&#x2F;strong&gt;. An earlier version of this section described the symmetric primitive as ChaCha20 and the key-wrapping as RSA. That is the scheme used by the older C++ Akira lineage, not the Rust v2 ESXi encryptor analysed here. The Rust v2 pipeline is X25519 (curve25519-dalek 4.1.1) for asymmetric key wrapping and SOSEMANUK (via rust-crypto 0.2.36) for symmetric per-file encryption, with the session key drawn from a ChaCha12 CSPRNG (rand 0.8.5 &#x2F; rand_chacha 0.3.1, surfaced as thread_rng &#x2F; ThreadRng). I independently confirmed SOSEMANUK in my own static recovery: the FSM multiplier 0x54655307 with a rotate-left-7 appears 20 times in the HLIL&#x2F;SSA, the Serpent24 key-schedule constant 0x9E3779B9 is present, and the literal string “sosemanuk” survives in .rodata. Crate versions come from my 29-crate recovery; algorithm roles are corroborated by Check Point. Apologies for the earlier conflation.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The actual encryption logic lives in lock_closure. Key points from Check Point&amp;#39;s analysis:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File targeting&lt;&#x2F;strong&gt;: the closure walks the filesystem, filtering by extension. ESXi-relevant extensions (.vmdk, .vmx, .vmsn, .nvram and others) are the primary targets.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Symmetric encryption&lt;&#x2F;strong&gt;: a per-file session key is generated per run, and the file payload is encrypted with SOSEMANUK, a software-oriented stream cipher chosen (like other stream ciphers) for throughput on targets without hardware AES acceleration. The implementation comes from the legacy rust-crypto 0.2.36 crate (DaGenix), where SOSEMANUK is heavily inlined and must be recovered from its characteristic constants rather than a named import. In this sample the fingerprints are unambiguous: the FSM multiplier 0x54655307 followed by a rotate-left-7 (the SOSEMANUK FSM update, recovered 20 times in the HLIL&#x2F;SSA), the Serpent key-schedule constant 0x9E3779B9, and the residual string “sosemanuk” in .rodata.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Randomness:&lt;&#x2F;strong&gt; the session key is drawn from ThreadRng (thread_rng), which in rand 0.8.5 is a ReseedingRng&amp;lt;ChaCha12Core, OsRng&amp;gt;. In other words the CSPRNG is ChaCha12 from rand_chacha 0.3.1, reseeded from the OS via getrandom 0.2.10. This is the only place ChaCha appears in the Rust v2 binary: as the RNG that produces key material, not as the file cipher. (See Part 2 for the thread_rng recovery.)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Key protection&lt;&#x2F;strong&gt;: the session key is wrapped with a per-binary hardcoded X25519 (curve25519) public key, the dalek curve25519-dalek 4.1.1 stack, not RSA. The public key is embedded in the binary and written alongside the encrypted files; only the operator’s matching private key can recover the session key, enabling decryption upon ransom payment.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;-31&quot;&gt;Further Reading&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;-32&quot;&gt;1. Why UAWAVAUATSH exists&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Source: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;torvalds&#x2F;linux&#x2F;blob&#x2F;v6.11&#x2F;arch&#x2F;x86&#x2F;lib&#x2F;x86-opcode-map.txt&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;torvalds&#x2F;linux&#x2F;blob&#x2F;v6.11&#x2F;arch&#x2F;x86&#x2F;lib&#x2F;x86-opcode-map.txt&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It is Binary Ninja’s hex view ASCII column rendering the raw machine code bytes of the HumanDuration::fmt function prologue. There is no string UAWAVAUATSH anywhere in the ELF.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;33.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-33&quot;&gt;The encoding mechanics&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The opcode map file is stable across kernel versions for x86–64, the encodings do not change. But I pin v6.11 rather than master, for the same reason the Rust stdlib commit pinned here: reproducibility.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The Linux kernel opcode map (canonical reference for x86 instruction encoding) lists: 0x55 = PUSH rBP&#x2F;r13, 0x56 = PUSH rSI&#x2F;r14, 0x57 = PUSH rDI&#x2F;r15, 0x54 = PUSH rSP&#x2F;r12 (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;torvalds&#x2F;linux&#x2F;blob&#x2F;v6.11&#x2F;arch&#x2F;x86&#x2F;lib&#x2F;x86-opcode-map.txt&quot;&gt;GitHub&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-34&quot;&gt;The REX prefix range (0x40–0x4f)&lt;&#x2F;h3&gt;
&lt;p&gt;In 64-bit mode (o64), opcodes 0x40–0x4f are REX prefix bytes, not instructions. Specifically: 0x41 = REX.B, 0x48 = REX.W &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;torvalds&#x2F;linux&#x2F;blob&#x2F;v6.11&#x2F;arch&#x2F;x86&#x2F;lib&#x2F;x86-opcode-map.txt&quot;&gt;(github&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.osdev.org&#x2F;X86-64_Instruction_Encoding&quot;&gt;OSDev Wiki&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;In 32-bit mode those same bytes were single-byte INC&#x2F;DEC instructions, but in 64-bit mode they are all repurposed as REX prefix encodings.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;top row:    INC eAX   INC eCX   INC eDX  ...   (i64)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;bottom row: REX       REX.B     REX.X    ...   (o64)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mode        0x41 means
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  --------    ----------
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  32-bit      INC eCX        (top row, superscript i64 = illegal in 64-bit)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  64-bit      REX.B prefix   (bottom row, superscript o64 = only in 64-bit)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;34.webp&quot; alt=&quot;Intel SDM Table A-2, row 4&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Intel SDM Table A-2, row 4&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;INC(i64) general register &#x2F; REX(o64) Prefixes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  40      41      42      43      44      45      46      47
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  eAX     eCX     eDX     eBX     eSP     eBP     eSI     eDI
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  REX     REX.B   REX.X   REX.XB  REX.R   REX.RB  REX.RX  REX.RXB
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The two superscripts do all the work:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;i64 = invalid in 64-bit mode: the INC eAX meaning of 0x40 is illegal in 64-bit mode&lt;&#x2F;li&gt;
&lt;li&gt;o64 = only in 64-bit mode: the REX meaning of 0x40 only exists in 64-bit mode&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And row 5 shows the PUSH encoding you need:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;PUSH(d64) general register
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  50      51      52      53      54      55      56      57
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  rAX&#x2F;r8  rCX&#x2F;r9  rDX&#x2F;r10 rBX&#x2F;r11 rSP&#x2F;r12 rBP&#x2F;r13 rSI&#x2F;r14 rDI&#x2F;r15
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &#x2F;r8–&#x2F;r15 disambiguation in row 5 is exactly what REX.B from row 4 provides. 0x55 alone = push rbp. 0x41 0x55 = REX.B prefix + 0x55 = push r13. Same opcode byte, extended register selected by the preceding REX.B.&lt;&#x2F;p&gt;
&lt;p&gt;This is the primary source. The Linux v6.11 opcode map entry 41: INC eCX (i64) | REX.B (o64) is a direct transcription of this SDM table. Both sources agree, and the SDM is the ground truth since it is the processor specification.&lt;&#x2F;p&gt;
&lt;p&gt;So the connection to UAWAVAUATSH is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;akirav2.elf is a 64-bit ELF
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    → CPU is in 64-bit long mode
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    → 0x41 = REX.B  (not INC eCX)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    → 0x41 0x57 = REX.B + PUSH rDI&#x2F;r15 = push r15
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    → Binary Ninja renders 0x41 as &amp;#39;A&amp;#39;, 0x57 as &amp;#39;W&amp;#39;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    → hence &amp;quot;AW&amp;quot; in the ASCII column
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-35&quot;&gt;2. REX — Register Extension.&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cdrdv2-public.intel.com&#x2F;671110&#x2F;325383-sdm-vol-2abcd.pdf&quot;&gt;&lt;strong&gt;REX&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; stands for &lt;strong&gt;Register Extension&lt;&#x2F;strong&gt;. It is a one-byte prefix introduced in x86–64 to extend the instruction set beyond what the original 32-bit encoding could address.&lt;&#x2F;p&gt;
&lt;p&gt;The byte has the fixed high nibble 0100 (hence 0x4_), with the low nibble encoding four flag bits:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;0100 W R X B
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;     │ │ │ └─ REX.B — extends the r&#x2F;m field, SIB base, or opcode reg field
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;     │ │ └─── REX.X — extends the SIB index field
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;     │ └───── REX.R — extends the ModRM reg field
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;     └─────── REX.W — promotes operand size to 64 bits
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Why it was needed&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The original x86 encoding only had 3 bits to identify a register, giving eight registers (rax–rdi). x86-64 added eight more (r8–r15). Rather than redesigning the entire encoding, Intel and AMD added the REX prefix to supply a fourth bit for each register field, doubling the addressable register count.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The four specific bits&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;REX.W (0x48) — use 64-bit operand size instead of the default 32-bit. Most mov, add, sub etc. instructions need this when operating on 64-bit values.&lt;&#x2F;p&gt;
&lt;p&gt;REX.B (0x41) — extends the register encoded in the opcode byte itself (e.g. the low 3 bits of a push&#x2F;pop opcode), or the r&#x2F;m field of ModRM. This is how 0x41 0x57 becomes push r15 instead of push rdi — the B bit flips the register selection from the base-8 register to the extended one.&lt;&#x2F;p&gt;
&lt;p&gt;REX.R (0x44) — extends the reg field of the ModRM byte.&lt;&#x2F;p&gt;
&lt;p&gt;REX.X (0x42) — extends the index field of the SIB byte.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Why&lt;&#x2F;strong&gt; &lt;strong&gt;0x41 renders as&lt;&#x2F;strong&gt; &lt;strong&gt;A in ASCII&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;0x41 is the ASCII code for A. Since REX prefixes occupy 0x40–0x4f, and that range maps directly onto uppercase Latin letters @ABCDEFGHIJKLMNO, every REX.B prefix byte in a hex viewer renders as A. This is purely coincidental — the x86-64 architects chose that range because it freed up the old INC&#x2F;DEC opcodes in 64-bit mode, not because of any ASCII consideration.&lt;&#x2F;p&gt;
&lt;p&gt;When pushing extended registers, 0x41 precedes the base push opcode. For example, to push r9 the encoding is 0x41 followed by 0x50 + (9 - 8) = 0x51. The original eight registers push with a single byte; extended registers require two bytes: the REX.B prefix plus the base opcode (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;eklitzke.org&#x2F;x86-register-encoding&quot;&gt;Eklitzke&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;Applying this to UAWAVAUATSH:&lt;&#x2F;p&gt;
&lt;p&gt;BytesASCIIInstruction0x55Upush rbp0x41 0x57AWpush r15 (REX.B + 0x57)0x41 0x56AVpush r14 (REX.B + 0x56)0x41 0x55AUpush r13 (REX.B + 0x55)0x41 0x54ATpush r12 (REX.B + 0x54)0x53Spush rbx0x48HREX.W prefix (start of next instruction, e.g. sub rsp, N)&lt;&#x2F;p&gt;
&lt;p&gt;All eleven bytes fall in the printable ASCII range (0x41–0x57 are uppercase letters, 0x53–0x55 likewise, 0x48 is H), so Binary Ninja&amp;#39;s hex view renders all of them as characters rather than dots.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-36&quot;&gt;Why this specific prologue&lt;&#x2F;h3&gt;
&lt;p&gt;Compilers save callee-preserved registers at function entry when the function needs to use them. The System V AMD64 ABI designates rbx, rbp, r12–r15 as callee-saved. A function that uses all six must push all six on entry. The HumanDuration::fmt function is complex enough (formatting a duration string requires multiple branches and temporaries) that LLVM assigned all six callee-saved registers, producing exactly this push sequence (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;eklitzke.org&#x2F;x86-register-encoding&quot;&gt;Eklitzke&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-37&quot;&gt;Why 0x41 renders as A&lt;&#x2F;h3&gt;
&lt;p&gt;0x41 is the ASCII code for uppercase A. The REX prefix range 0x40–0x4F was chosen by the x86-64 architects precisely because those opcodes were the legacy single-byte INC&#x2F;DEC instructions, which were retired in 64-bit mode to free the space. They happen to sit in the middle of the uppercase ASCII Latin alphabet. So every REX-prefixed push of an extended register produces an A in the ASCII column followed by the push target letter (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.osdev.org&#x2F;X86-64_Instruction_Encoding&quot;&gt;OSDev Wiki&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;The byte sequence is entirely mechanical and deterministic. Any function in any x86–64 ELF that saves rbp, r15, r14, r13, r12, rbx in that order will produce UAWAVAUATSH in a hex viewer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-38&quot;&gt;3. Mutex&lt;&#x2F;h2&gt;
&lt;p&gt;Looking back to (0x4d3c2b--0x4d3c63) we saw:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d3c2b   cmp  qword [rsp+0x48 {var_290}], 0x0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d3c31   lea  r13, [rsp+0x50 {var_288}]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004d3c36   je   0x4d4153
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And the three lock inc atomics immediately before SystemTime::now. The je 0x4d4153 target was never shown in the screenshots, that is where control goes when the count is zero.&lt;&#x2F;p&gt;
&lt;p&gt;From the figure below, we also see std::thread::scoped::Sco...um_running_threads at 0x4d4062.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;35.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;From&lt;&#x2F;strong&gt; &lt;strong&gt;thread&#x2F;mod.rs&lt;&#x2F;strong&gt; at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;mod.rs&quot;&gt;db034cee&lt;&#x2F;a&gt;, the exact lines in spawn_unchecked_:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if let Some(scope_data) = &amp;amp;my_packet.scope {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    scope_data.increment_num_running_threads();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;From&lt;&#x2F;strong&gt; &lt;strong&gt;scoped.rs&lt;&#x2F;strong&gt; at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;scoped.rs&quot;&gt;db034cee&lt;&#x2F;a&gt;, increment_num_running_threads is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub(super) fn increment_num_running_threads(&amp;amp;self) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if self.num_running_threads.fetch_add(1, Ordering::Relaxed) &amp;gt; usize::MAX &#x2F; 2 {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        self.overflow();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And from thread&#x2F;mod.rs, Packet contains:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;struct Packet&amp;lt;&amp;#39;scope, T&amp;gt; {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    scope: Option&amp;lt;Arc&amp;lt;scoped::ScopeData&amp;gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;ScopeData is in scoped.rs. From the earlier session search result, ScopeData contains:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub(super) struct ScopeData {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    num_running_threads: Atomic&amp;lt;usize&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    a_thread_panicked:   Atomic&amp;lt;bool&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    main_thread:         Thread,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And from the scoped.rs source at the pinned commit, scope() blocks until num_running_threads reaches zero using a Condvar:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;79e9716c980570bfd1f666e3b16ac583f0168962&#x2F;library&#x2F;std&#x2F;src&#x2F;thread&#x2F;scoped.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;-39&quot;&gt;&lt;strong&gt;The connection is:&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Mutex +&lt;&#x2F;strong&gt; &lt;strong&gt;Condvar is the standard Rust pattern for&lt;&#x2F;strong&gt; &lt;strong&gt;scope() to wait for all spawned threads to finish.&lt;&#x2F;strong&gt; The Condvar is used inside ScopeData to park the scope-owning thread until num_running_threads drops to zero. The Mutex guards the condition variable&amp;#39;s predicate check.&lt;&#x2F;p&gt;
&lt;p&gt;In the disassembly, the Mutex::lock at 0x4d3b40&#x2F;0x4d3b73 on data_5e7028 and data_5e7040, and the Mutex::lock at 0x4d39e0 on data_5e7080 are the Condvar-associated mutexes inside ScopeData. This is the scope() call blocking after all threads are spawned, waiting for them to complete via decrement_num_running_threads calling notify_all on the Condvar.&lt;&#x2F;p&gt;
&lt;p&gt;So the connection is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;std::thread::scope(|s| {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    for path in files {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        s.spawn(|| lock_closure(path, ...));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; scope() blocks here on Condvar::wait_while(Mutex, num_running == 0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The Mutex and Condvar pair are from scoped.rs , &lt;em&gt;&lt;strong&gt;not Akira&amp;#39;s own code.&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The Vec::push at 0x4d39ff question therefore changes: it may be pushing into a Vec inside ScopeData or a related structure managed by the scope machinery, not a user-visible Vec&lt;JoinHandle&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-40&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;p&gt;x86-psABIs project (2024) &lt;em&gt;System V application binary interface: AMD64 architecture processor supplement&lt;&#x2F;em&gt;, version 1.0. GitLab. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;x86-psABIs&#x2F;x86-64-ABI&quot;&gt;https:&#x2F;&#x2F;gitlab.com&#x2F;x86-psABIs&#x2F;x86-64-ABI&lt;&#x2F;a&gt; (PDF: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hjl-tools&#x2F;x86-psABI&#x2F;wiki&#x2F;x86-64-psABI-1.0.pdf&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;hjl-tools&#x2F;x86-psABI&#x2F;wiki&#x2F;x86-64-psABI-1.0.pdf&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;Herzog, B. (2024) &lt;em&gt;Inside Akira ransomware’s Rust experiment&lt;&#x2F;em&gt;. Check Point Research. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Klabnik, S. and Nichols, C. (2019) &lt;em&gt;The Rust programming language&lt;&#x2F;em&gt;. San Francisco: No Starch Press.&lt;&#x2F;p&gt;
&lt;p&gt;RalfJung (2024) &lt;em&gt;PathBuf incorrectly transmutes OsString to Vec — issue #124409&lt;&#x2F;em&gt;. GitHub. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;124409&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;124409&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;RalfJung (2024) &lt;em&gt;PathBuf: replace transmuting by accessor functions — PR #124410&lt;&#x2F;em&gt;. GitHub. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;pull&#x2F;124410&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;pull&#x2F;124410&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The Linux man-pages project (2024) &lt;em&gt;getpid(2)&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man2&#x2F;getpid.2.html&quot;&gt;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man2&#x2F;getpid.2.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Tompkins, W. et al. (2024) &lt;em&gt;Akira ransomware continues to evolve&lt;&#x2F;em&gt;. Cisco Talos Intelligence. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.talosintelligence.com&#x2F;akira-ransomware-continues-to-evolve&#x2F;&quot;&gt;https:&#x2F;&#x2F;blog.talosintelligence.com&#x2F;akira-ransomware-continues-to-evolve&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;console-rs (2024) &lt;em&gt;indicatif 0.17.7 progress_bar.rs source&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;src&#x2F;progress_bar.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;src&#x2F;progress_bar.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;console-rs (2024) &lt;em&gt;indicatif 0.17.7 style.rs source&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;src&#x2F;style.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;src&#x2F;style.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;console-rs (2024) &lt;em&gt;indicatif 0.17.7&lt;&#x2F;em&gt;. crates.io. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;indicatif&#x2F;0.17.7&quot;&gt;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;indicatif&#x2F;0.17.7&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;console-rs (2024) &lt;em&gt;indicatif yarnish.rs example&lt;&#x2F;em&gt;, commit 0c037edc86449d84aa457d7d5db80b4166c18d6b. GitHub. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs#L29&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs#L29&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Xiao, C. (2025) &lt;em&gt;Reconstructing Rust types&lt;&#x2F;em&gt;. RE&#x2F;&#x2F;verse 2025. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;recon.cx&#x2F;2025&#x2F;schedule&#x2F;events&#x2F;reverse.html&quot;&gt;https:&#x2F;&#x2F;recon.cx&#x2F;2025&#x2F;schedule&#x2F;events&#x2F;reverse.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;UAWAVAUATSH&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Linux kernel opcode map (ground truth for opcode assignments): &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;torvalds&#x2F;linux&#x2F;blob&#x2F;master&#x2F;arch&#x2F;x86&#x2F;lib&#x2F;x86-opcode-map.txt&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;torvalds&#x2F;linux&#x2F;blob&#x2F;master&#x2F;arch&#x2F;x86&#x2F;lib&#x2F;x86-opcode-map.txt&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;REX prefix encoding: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.osdev.org&#x2F;X86-64_Instruction_Encoding&quot;&gt;https:&#x2F;&#x2F;wiki.osdev.org&#x2F;X86-64_Instruction_Encoding&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Push&#x2F;pop register encoding arithmetic: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;eklitzke.org&#x2F;x86-register-encoding&quot;&gt;https:&#x2F;&#x2F;eklitzke.org&#x2F;x86-register-encoding&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-3&#x2F;36.webp&quot; alt=&quot;Thanks for reading&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Thanks for reading&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Reversing Akira&#x27;s Rust Experiment: A Breakdown, Part 2</title>
        <published>2026-05-20T00:00:00+00:00</published>
        <updated>2026-05-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yenwang.dev/blog/medium-akira-part-2/"/>
        <id>https://yenwang.dev/blog/medium-akira-part-2/</id>
        
        <content type="html" xml:base="https://yenwang.dev/blog/medium-akira-part-2/">&lt;blockquote&gt;
&lt;p&gt;This post is a breakdown and commentary on &lt;strong&gt;Ben Herzog’s Check Point Research&lt;&#x2F;strong&gt; (December 2024), &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;Inside Akira Ransomware’s Rust Experiment&lt;&#x2F;a&gt; &amp;amp; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.virusbulletin.com&#x2F;conference&#x2F;vb2025&#x2F;abstracts&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;his talk in 2025&lt;&#x2F;a&gt;. All technical claims are grounded in the linked primary sources below. Combined with some methodologies of &lt;strong&gt;Cindy Xiao&lt;&#x2F;strong&gt;’s amazing reports&#x2F;talks in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;rust-reversing-workshop-recon-2024&quot;&gt;2024&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;reconstructing-rust-types-talk-re-verse-2025&quot;&gt;2025&lt;&#x2F;a&gt; (mainly for reconstructing types).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;01.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you haven’t read &lt;a href=&quot;https:&#x2F;&#x2F;yenwang.dev&#x2F;blog&#x2F;medium-akira-part-1&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt;, it’s encouraged to read the threat reports at least (see &lt;strong&gt;References - Threat Reports&lt;&#x2F;strong&gt; below), so the activities of the binary are clear alongside the binary analysis, including the latest &lt;strong&gt;Tactics, Techniques, and Procedures (TTPs).&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;&quot;&gt;Table of Contents&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Info of the binary in this analysis&lt;&#x2F;li&gt;
&lt;li&gt;LLVM’s picks on scalar moves&lt;&#x2F;li&gt;
&lt;li&gt;Section 2: The CLI Framework (seahorse)&lt;&#x2F;li&gt;
&lt;li&gt;Section 3: The inlining cascade&lt;&#x2F;li&gt;
&lt;li&gt;Section 4: available_parallelism()&lt;&#x2F;li&gt;
&lt;li&gt;Section 5: lock and Thread Spawning&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;-1&quot;&gt;Further Reading&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;SIMD&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;How aho-corasick uses memchr and SIMD&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;-2&quot;&gt;References&lt;&#x2F;h3&gt;
&lt;h2 id=&quot;-3&quot;&gt;Info of the binary in this analysis&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;-4&quot;&gt;Reversing Akira’s Rust Experiment: A Breakdown Part 1&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This post is a breakdown and commentary on &lt;strong&gt;Ben Herzog’s Check Point Research&lt;&#x2F;strong&gt; (December 2024), &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;Inside Akira Ransomware’s Rust Experiment&lt;&#x2F;a&gt; &amp;amp; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.virusbulletin.com&#x2F;conference&#x2F;vb2025&#x2F;abstracts&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;his talk in 2025&lt;&#x2F;a&gt;. All technical claims are grounded in the linked primary sources below. Combined with &lt;strong&gt;Cindy Xiao&lt;&#x2F;strong&gt;’s amazing reports&#x2F;talks in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;rust-reversing-workshop-recon-2024&quot;&gt;2024&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;reconstructing-rust-types-talk-re-verse-2025&quot;&gt;2025&lt;&#x2F;a&gt; (about reconstructing types and more).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;There will be a part 2 to break down the rest. I separate them so this article won’t be &lt;strong&gt;&lt;em&gt;_&lt;em&gt;too_long&lt;&#x2F;em&gt;&lt;&#x2F;em&gt;_&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;02.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For this analysis, Binary Ninja (v5.3.9434) is used without enabling any plugins. &lt;strong&gt;Binary:&lt;&#x2F;strong&gt; akirav2.elf , SHA25:60ee1d284ed663073872012c7bde7fac5ca1121403f1a5d2d5411317df282796c.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-5&quot;&gt;A note on source references&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The commit hash is updated on 10th Jun 2026. &lt;strong&gt;&lt;em&gt;79e9716c…&lt;&#x2F;em&gt; is rustc 1.74.0 stable&lt;&#x2F;strong&gt; (released 2023-11-16). Akirav2’s &lt;em&gt;.rodata&lt;&#x2F;em&gt; contains std panic-path strings of the form &lt;em&gt;&#x2F;rustc&#x2F;&lt;commit&gt;&#x2F;library&#x2F;...&lt;&#x2F;em&gt;.&lt;strong&gt;&lt;em&gt;db034cee…&lt;&#x2F;em&gt; is a&lt;&#x2F;strong&gt; &lt;strong&gt;&lt;em&gt;master&lt;&#x2F;em&gt;&#x2F;nightly commit from ~1 year later,&lt;&#x2F;strong&gt; which I was mistakenly pinned in the article. If you see any links related to &lt;em&gt;&lt;strong&gt;db034cee… ,&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; &lt;em&gt;please consider it was based on previosu wrong assumption but it works to some degree.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The Rust standard library is under active development and files move between releases. Source links in this post are pinned to commit 79e9716c980570bfd1f666e3b16ac583f0168962 — &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;releases.rs&#x2F;docs&#x2F;1.74.0&#x2F;&quot;&gt;&lt;strong&gt;rustc 1.74.0 stable&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;, the toolchain that compiled this Akira v2 binary. This is recoverable directly from the binary: its .rodata embeds &#x2F;rustc&#x2F;79e9716c…&#x2F;library&#x2F;... panic paths, and it is corroborated by version-pinned FunctionID analysis resolving the sample to 1.74.0.&lt;&#x2F;p&gt;
&lt;p&gt;This matters because stdlib code is inlined directly into the final binary at compile time: what you see in the disassembly reflects the stdlib at that specific commit, not the current main branch. Following a newer version of the same file risks misidentifying inlined boilerplate or drawing incorrect conclusions about what the malware authors wrote versus what the compiler contributed.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If a pinned link returns a 404, the file has likely been relocated. Navigate to the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;tree&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&quot;&gt;repository root&lt;&#x2F;a&gt; and search by filename.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-6&quot;&gt;LLVM’s picks on scalar moves&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;SIMD stands for &lt;strong&gt;Single Instruction, Multiple Data&lt;&#x2F;strong&gt;. It is a CPU execution model where one instruction operates on multiple data elements in parallel, using wide registers.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;To understand the compiler’s decision, one must examine the specific arithmetic of the LLVM cost model and processor microarchitecture. The compiler does not merely guess; it evaluates the cost of instructions in bytes, micro-operations (&lt;em&gt;uOps&lt;&#x2F;em&gt;), and execution port pressure.&lt;&#x2F;p&gt;
&lt;p&gt;LLVM’s auto-vectorisation and scalar replacement of aggregates (SROA) pass recognises that zeroing or copying two adjacent 8-byte fields is a 16-byte operation and emits a single XMM store. This is purely a code-generation optimisation, the source code contains no SIMD intrinsics. When you see pxor xmm0, xmm0 followed by movdqu in Rust release builds, it is “likely” (&lt;em&gt;but not absolutely&lt;&#x2F;em&gt;) a struct&#x2F;array zero-initialisation artefact, not intentional vector computation (&lt;em&gt;more details in the&lt;&#x2F;em&gt; &lt;strong&gt;&lt;em&gt;Section 3: Inlin&lt;&#x2F;em&gt;ing Cascade&lt;&#x2F;strong&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;Recognising this code-generation artefact is crucial for reverse engineering, as it cleanly separates genuine mathematical or cryptographic SIMD workloads from routine structural initialisation. At the hardware level, this compiler decision reflects the purest form of systemic leverage: achieving maximal state change with minimal resource expenditure.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rust and SIMD.&lt;&#x2F;strong&gt; Rust’s standard library uses SIMD for things like memchr (which backs aho-corasick internally). When you see a tight loop in a Rust binary using pcmpeqb or vpcmpeqb, it is almost certainly memchr or a SIMD-accelerated pattern search, not custom malware logic. Relevant to your Akira analysis: if you see SIMD near the search routines, that is likely the aho-corasick crate doing pattern matching.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;More details can be found in the section &lt;strong&gt;Further Reading&lt;&#x2F;strong&gt;below.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-7&quot;&gt;Section 2: The CLI Framework (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ksk001100&#x2F;seahorse&quot;&gt;seahorse&lt;&#x2F;a&gt;)&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;03.webp&quot; alt=&quot;main function&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;main function&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;xmm0 is a 128-bit register in x86-64 assembly used for SSE&#x2F;AVX SIMD operations, frequently storing floating-point numbers (single&#x2F;double precision) or packed integers (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.cs.uaf.edu&#x2F;courses&#x2F;cs301&#x2F;2014-fall&#x2F;notes&#x2F;float-asm&#x2F;index.html&quot;&gt;resource&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What the threat intel establishes&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The complete Akira v2 CLI flag set is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;--path: Start path. Default value: &#x2F;vmfs&#x2F;volumes &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;(Check Point Research&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;--id: Build ID as a run condition, likely password&#x2F;anti-analysis (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unit42.paloaltonetworks.com&#x2F;threat-assessment-howling-scorpius-akira-ransomware&#x2F;&quot;&gt;Palo Alto Networks&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;--vmonly: encrypt VM files only &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unit42.paloaltonetworks.com&#x2F;threat-assessment-howling-scorpius-akira-ransomware&#x2F;&quot;&gt;(Palo Alto Networks&lt;&#x2F;a&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;--stopvm: turn off running VMs before encryption (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unit42.paloaltonetworks.com&#x2F;threat-assessment-howling-scorpius-akira-ransomware&#x2F;&quot;&gt;Palo Alto Networks&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Additionally, --fork controls process forking for the stopvm command, and a --threads flag controls thread count (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;Check Point Research&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;There is also a --logs flag for controlling log verbosity.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;04.webp&quot; alt=&quot;Default function in akirav2 in Disassembly Pt 1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Default function in akirav2 in Disassembly Pt 1&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;05.webp&quot; alt=&quot;Default function in akirav2 in Disassembly Pt 2, you can see another crate &lt;code&gt;Indicatif&lt;&#x2F;code&gt; in the following dsiassembly code&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Default function in akirav2 in Disassembly Pt 2, you can see another crate `Indicatif` in the following dsiassembly code&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;06.webp&quot; alt=&quot;Default function in akirav2 in disassembly in High Level IL&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Default function in akirav2 in disassembly in High Level IL&lt;&#x2F;p&gt;
&lt;p&gt;This single HLIL block does three things simultaneously due to inlining: starts a timer, initialises the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;indicatif&lt;&#x2F;a&gt; spinner, and as a side effect of that initialisation touching adjacent .rodata, exposes the entire operational string table of default_action in one place. Every log message, error string, shell command, and panic message that default_action can emit is visible here.&lt;&#x2F;p&gt;
&lt;p&gt;**1. Timing initialisation (**&lt;strong&gt;0x4dc44a)&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;rax, rdx = std::time::Instant::now::hbcf67d3c6dde57ee()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;int64_t var_f8 = rax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;int32_t var_2fc = rdx
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Records the wall-clock time at the start of default_action. This is used later to calculate elapsed time for the progress spinner display, the {elapsed} or {elapsed_precise} template keys in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;indicatif&lt;&#x2F;a&gt;. This is pure &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;indicatif&lt;&#x2F;a&gt; boilerplate from &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;e84863a94e9d161bff4d7e4f918ead726d481001&#x2F;examples&#x2F;yarnish.rs&quot;&gt;yarnish.rs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;**2. Progress spinner initialisation (**&lt;strong&gt;0x4dc473)&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;int32_t var_278[0x4]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;indicatif::style::ProgressStyle::with_template::hc10b2fc1f737695f(
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &amp;amp;var_278,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &amp;quot;{prefix:.bold.dim} {spinner} {wide_msg}akiranew&#x2F;src&#x2F;main.rs ...&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    0x27)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This calls ProgressStyle::with_template with a template string of length 0x27 = 39 characters. The template itself is {prefix:.bold.dim} {spinner} {wide_msg}, exactly the yarnish.rs spinner template confirmed from the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;indicatif&lt;&#x2F;a&gt; source. The result lands in var_278, which is the ProgressStyle struct on the stack.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;07.webp&quot; alt=&quot;From data_5e74e8, it’s clear a template of Indicatif yarnish.rs&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;From data_5e74e8, it’s clear a template of Indicatif yarnish.rs&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The core insight from Check Point (&lt;&#x2F;strong&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;e84863a94e9d161bff4d7e4f918ead726d481001&#x2F;examples&#x2F;yarnish.rs&quot;&gt;Indicatif&lt;&#x2F;a&gt;&lt;strong&gt;)&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The malware authors used indicatif to provide an operator-friendly visual representation of what the malware is doing: progress bars and spinners, and the source code they used was directly inspired by &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;yarnish.rs&lt;&#x2F;a&gt;, the canonical indicatif example.&lt;&#x2F;p&gt;
&lt;p&gt;The puzzle &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;Check Point&lt;&#x2F;a&gt; identified is that default_action begins with dozens of eyebrow-raising basic blocks invoking the C FFI and other low-level functionality, which has nothing to do with encryption. This is entirely explained by indicatif being inlined into default_action by the Rust release compiler.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;08.webp&quot; alt=&quot;A truncated overview of the graphs based on &lt;code&gt;default_action&lt;&#x2F;code&gt;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;A truncated overview of the graphs based on `default_action`&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;09.webp&quot; alt=&quot;First lines in &lt;code&gt;default_action&lt;&#x2F;code&gt; before jumping&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;First lines in `default_action` before jumping&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;10.webp&quot; alt=&quot;First few functions of &lt;code&gt;default_action&lt;&#x2F;code&gt;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;First few functions of `default_action`&lt;&#x2F;p&gt;
&lt;p&gt;This is directly relevant to the disassembly we have been analysing across this session. The std::time::Instant::now() call at prologue (0x4dc44a), the format string &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;{prefix:.bold.dim} {spinner} {wide_msg}&lt;&#x2F;a&gt; at 0x4dc45e, and the CStr::from_bytes_with_nul &#x2F; File::open_c calls in the early blocks of default_action are all &lt;strong&gt;inlined&lt;&#x2F;strong&gt; &lt;strong&gt;indicatif initialisation code&lt;&#x2F;strong&gt; (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;yarnish.rs&lt;&#x2F;a&gt;), not malware logic. The malware logic only starts later in default_action once the spinner is set up.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn main() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let mut rng = rand::rng();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let started = Instant::now();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let spinner_style = ProgressStyle::with_template(&amp;quot;{prefix:.bold.dim} {spinner} {wide_msg}&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .unwrap()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .tick_chars(&amp;quot;⠁⠂⠄⡀⢀⠠⠐⠈ &amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;11.webp&quot; alt=&quot;Xref — to see the corelation between main and default_action&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Xref — to see the corelation between main and default_action&lt;&#x2F;p&gt;
&lt;p&gt;After argument collection, the assembly performs many writes to stack memory before calling default_action. The function name itself is the clue: a web search for it leads directly to &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ksk001100&#x2F;seahorse&quot;&gt;seahorse&lt;&#x2F;a&gt;, a Rust CLI framework whose &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ksk001100&#x2F;seahorse&#x2F;blob&#x2F;master&#x2F;examples&#x2F;nested_multiple_app.rs&quot;&gt;examples&lt;&#x2F;a&gt; contains exactly this pattern:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;use seahorse::{App, Context, Command};
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;use std::env;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn main() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let args: Vec&amp;lt;String&amp;gt; = env::args().collect();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let app = App::new(env!(&amp;quot;CARGO_PKG_NAME&amp;quot;))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .description(env!(&amp;quot;CARGO_PKG_DESCRIPTION&amp;quot;))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .author(env!(&amp;quot;CARGO_PKG_AUTHORS&amp;quot;))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .version(env!(&amp;quot;CARGO_PKG_VERSION&amp;quot;))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .usage(&amp;quot;cli [name]&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .action(default_action)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .command(add_command())
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .command(sub_command());
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    app.run(args);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The string &amp;quot;cli [name]&amp;quot; also appears verbatim in the binary, confirming the match.&lt;&#x2F;p&gt;
&lt;p&gt;The seahorse &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ksk001100&#x2F;seahorse&#x2F;blob&#x2F;master&#x2F;src&#x2F;app.rs#L9&quot;&gt;App struct&lt;&#x2F;a&gt; has ten fields, most of them Option&lt;T&gt;. The stack writes in the assembly are initialising this struct field by field. Once you know the struct layout, each write maps to a named field.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F;&#x2F; Multiple action application entry point
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#[derive(Default)]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub struct App {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F;&#x2F; Application name
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub name: String,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F;&#x2F; Application author
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub author: Option&amp;lt;String&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F;&#x2F; Application description
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub description: Option&amp;lt;String&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F;&#x2F; Application usage
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub usage: Option&amp;lt;String&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F;&#x2F; Application version
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub version: Option&amp;lt;String&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F;&#x2F; Application commands
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub commands: Option&amp;lt;Vec&amp;lt;Command&amp;gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F;&#x2F; Application action
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub action: Option&amp;lt;Action&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F;&#x2F; Alternate application action that returns a Result
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub action_with_result: Option&amp;lt;ActionWithResult&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F;&#x2F; Application flags
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub flags: Option&amp;lt;Vec&amp;lt;Flag&amp;gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The flags initialised include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;--threads &lt;int&gt;: number of encryption threads (1-1000)&lt;&#x2F;li&gt;
&lt;li&gt;--stopvm: shut down ESXi VMs before encrypting&lt;&#x2F;li&gt;
&lt;li&gt;Others controlling targeting behaviour&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;12.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;movdqu&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Move Double Quadword Unaligned. Loads or stores 16 bytes (128 bits) between an XMM register and memory, with no alignment requirement on the memory address. The u suffix distinguishes it from movdqa (aligned, which faults if the address is not 16-byte aligned).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;movdqu xmm0, [rel data_4908f4[0x7c]] loads a 16-byte string literal; the inline annotation resolves it to &#x2F;proc&#x2F;self&#x2F;cgroupcpu&#x2F;sys&#x2F;fs&#x2F;cgroupcgroup.controllers... as a cgroup path read. This is the ESXi&#x2F;Linux container-escape or resource-limit probe pattern seen in Akira v2.&lt;&#x2F;li&gt;
&lt;li&gt;core::ffi::c_str::CStr::from_bytes_with_nul is called on the result , confirming a C-string literal being wrapped.&lt;&#x2F;li&gt;
&lt;li&gt;std::sys::unix::fs::File::open_c opens the resolved path.&lt;&#x2F;li&gt;
&lt;li&gt;cmp dword [rbx {var_2f8}], 0x0 &#x2F; je 0x4dc94c: checks whether File::open_c returned Ok or Err (the Result discriminant is at offset 0 of the Result&amp;lt;File, Error&amp;gt; stack slot).&lt;&#x2F;li&gt;
&lt;li&gt;core::ptr::drop_in_place...ror..Error$GT$$GT$ : the error path drops the io::Error on the branch where open failed.&lt;&#x2F;li&gt;
&lt;li&gt;The free call at 0x4dc57d via qword [rel free] (PLT slot) is dropping a heap allocation, consistent with an OsString&#x2F;PathBuf being freed after use.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;13.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;seahorse::context::Context::string_flag is called at 0x4dc8bd, this is the seahorse API call to retrieve a named string flag from the parsed CLI context. The flag name is loaded from data_439f64[8] which the inline annotation shows as a packed log-level string: &amp;quot;logswarn.logSlimMaskbluecyanboldLANG &amp;lt;= ZeroweekhournextWARNINFOcallhashHash =&amp;gt; FAILQuit...&amp;quot;. That is a seahorse flag-definition table: multiple flag names packed into a single data blob with offsets. The flag being retrieved here is at byte offset 8, which given the prefix &amp;quot;logs&amp;quot; is the --logs flag.&lt;&#x2F;li&gt;
&lt;li&gt;At 0x4dc8fb (highlighted in red), lea rdx, [rel data_4418a5[0x19]] loads a string annotated as &amp;quot;debugValid values for --logs: trace, debug, error, info, warn\nakiranew_Root path: Starting path: &#x2F;vmfs&#x2F;vo...&amp;quot;: this is an error&#x2F;help string emitted when the --logs value fails validation.&lt;&#x2F;li&gt;
&lt;li&gt;_$LT$$u5b$A$u5d$$u20$as$...T$B$GT$$GT$::equal is &amp;lt;[A] as PartialEq&amp;lt;[B]&amp;gt;&amp;gt;::eq: slice equality, comparing the user-supplied flag value against known valid strings (&amp;quot;trace&amp;quot;, &amp;quot;debug&amp;quot;, &amp;quot;error&amp;quot;, &amp;quot;info&amp;quot;, &amp;quot;warn&amp;quot;). This is done twice (at 0x4dc8ee and 0x4dc90b) with r12 = 0x4 and r13 = 0x5, i.e. comparing 4-byte and 5-byte slices&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;-8&quot;&gt;&lt;strong&gt;Why this matters for this research?&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The Check Point finding reframes the analysis problem entirely. The apparent complexity of default_action is not malware sophistication.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Instead, it is release-mode inlining of a third-party progress bar library.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-9&quot;&gt;Section 3: Inlining Cascade&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;-10&quot;&gt;Indicatif&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;-11&quot;&gt;The design motivation&lt;&#x2F;h3&gt;
&lt;p&gt;The malware authors used indicatif to provide an operator-friendly visual representation of what the malware is doing. In &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;checkpoint&lt;&#x2F;a&gt; report, Ben describe it as &amp;quot;true eye candy for CLI fans&amp;quot;, things like progress bars and spinners declared cheerfully as the ransomware destroys backups, generates a symmetric session key, encrypts VMs, and writes ransom notes.&lt;&#x2F;p&gt;
&lt;p&gt;The boilerplate source is yarnish.rs from the indicatif example directory. As shown in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;checkpoint&lt;&#x2F;a&gt; report, it’s observed that yarnish.rs was used as a prototype during the writing of this malware, specifically for the lock function, which was moved to a separate module (lock.rs)&lt;&#x2F;p&gt;
&lt;p&gt;This code is part of the example &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;yarnish.rs&lt;&#x2F;a&gt; in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;tree&#x2F;main&quot;&gt;Indicatif&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let m = MultiProgress::new();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let handles: Vec&amp;lt;_&amp;gt; = (0..4u32)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .map(|i| {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            let count = rng.random_range(30..80);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            let pb = m.add(ProgressBar::new(count));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            pb.set_style(spinner_style.clone());
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            pb.set_prefix(format!(&amp;quot;[{}&#x2F;?]&amp;quot;, i + 1));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            thread::spawn(move || {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                let mut rng = rand::rng();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                let pkg = PACKAGES.choose(&amp;amp;mut rng).unwrap();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                for _ in 0..count {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    let cmd = COMMANDS.choose(&amp;amp;mut rng).unwrap();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    thread::sleep(Duration::from_millis(rng.random_range(25..200)));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    pb.set_message(format!(&amp;quot;{pkg}: {cmd}&amp;quot;));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    pb.inc(1);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                pb.finish_with_message(&amp;quot;waiting...&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            })
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        })
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        .collect();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    for h in handles {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let _ = h.join();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let’s focus on rng.random_range... for now. The breakdown of thread spawning can be found in the section “The Inlining Cascade” below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;14.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;pxor xmm0, xmm0&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;XOR a 128-bit SSE register with itself. The result is always zero regardless of the prior contents, identical in intent to xor eax, eax for 32-bit registers, but operating on 16 bytes in one instruction.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;0x4dd16e : thread RNG initialisation and alphabet setup&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd174   call  core::ptr::drop_in_place...Box&amp;lt;str&amp;gt;...::hb14970c17f15bb5a
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Drops a previous Box&lt;str&gt;, cleanup of a prior string allocation (a log message or progress bar message that was displayed).&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd179   call  rand::rngs::thread::thread_rng::h463d5de9ac748823
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd17e   mov   r14, rax
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;In rand 0.9, fn rand::thread_rng() was renamed to rand::rng() and removed from the prelude.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-random&#x2F;rand&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md#api-changes-rng-trait-and-top-level-fns&quot;&gt;thread_rng()&lt;&#x2F;a&gt; initialises the per-thread &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-random&#x2F;rand&#x2F;blob&#x2F;master&#x2F;src&#x2F;rngs&#x2F;thread.rs#L137&quot;&gt;CSPRNG&lt;&#x2F;a&gt; from the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-random&#x2F;rand&#x2F;blob&#x2F;master&#x2F;src&#x2F;rngs&#x2F;thread.rs#L137&quot;&gt;rand&lt;&#x2F;a&gt; crate. The return value (ThreadRng handle) is stored in r14. This is the first direct evidence of cryptographic randomness being sourced. Concretely it is rand 0.8.5’s ThreadRng, a ReseedingRng&amp;lt;ChaCha12Core, OsRng&amp;gt; built on rand_chacha 0.3.1, which generates the SOSEMANUK session key later wrapped by X25519 (see Part 3).&lt;&#x2F;p&gt;
&lt;p&gt;The mangled symbol in the binary is &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-random&#x2F;rand&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md#api-changes-rng-trait-and-top-level-fns&quot;&gt;rand::rngs::thread::thread_rng&lt;&#x2F;a&gt; , this confirms Akira v2 compiled against rand pre-0.9 (where the function was still named thread_rng), not 0.9+.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-12&quot;&gt;Randomly-selected characters &amp;amp; Vec&lt;T&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Initialises a Vec&lt;T&gt; at var_278 with discriminant 0x1 (non-empty Some), zeroes the data&#x2F;len&#x2F;cap fields via xmm0, then reserves capacity for 10 elements (r12 = 0xa). This is building a Vec that will hold randomly-selected characters.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1b3   lea   rbx, [rel str.2[0x39]]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;           {&amp;quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.carg...&amp;quot;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Loads a pointer into the alphanumeric character set ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 , which has 62 characters. The &#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.carg... suffix is the adjacent .rodata (a cargo registry path string), the alphabet itself is just the 62 alphanumeric characters. This is the character pool for random ID generation.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1ba   mov   rax, qword [r14+0x110]  ; load Vec length counter
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1c1   cmp   rax, 0x40               ; compare to 64
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1c5   jb    0x4dd1dc                ; loop while length &amp;lt; 64
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The loop runs until 64 characters have been appended. This is generating a &lt;strong&gt;64-character random alphanumeric string&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;15.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;**0x4dd1c7–**&lt;strong&gt;0x4dd23f : the random character selection inner loop&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1c7   mov   rdi, rbp
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1ca   mov   rsi, r13
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1cd   call  _$LT$rand..rngs..adapter...Core$GT$::generate::hd875faf09d425bd0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1d2   and   qword [r14+0x110], 0x0   ; clear len field temporarily
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1da   xor   eax, eax
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Calls &amp;lt;rand::rngs::adapter::ReseedingRng&lt;Core&gt;&amp;gt;::generate, this is the ThreadRng backend calling the underlying CSPRNG block generator. The result populates a buffer.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1dc   mov   ecx, dword [r14+rax*4+0x10]  ; load random dword from buffer
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1e1   mov   edx, ecx
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1e3   shr   edx, 0x1b                    ; edx = ecx &amp;gt;&amp;gt; 27
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1e6   inc   rax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1e9   mov   qword [r14+0x110], rax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1f0   cmp   edx, 0x1e                    ; compare to 30
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1f3   ja    0x4dd1c1                     ; reject if edx &amp;gt; 30 (rejection sampling)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The pre-0.9 version that matches Akira v2’s compile-time rand version (the binary uses thread_rng, which was renamed to rng() only in 0.9&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;crate&#x2F;rand&#x2F;0.8.5&#x2F;source&#x2F;src&#x2F;distributions&#x2F;uniform.rs&quot;&gt;&lt;strong&gt;rejection sampling&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; to avoid modulo bias. The random dword is right-shifted by 27 bits, leaving 5 bits. If those 5 bits gives 0–31; rejecting &amp;gt; 30 keeps 0–30, which is 31 values. But 62 = 2 × 31, so this is selecting a value in [0, 30] and then using a further bit to select the upper or lower half of the alphabet. The next block confirms this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1f5   shr   ecx, 0x1a               ; ecx &amp;gt;&amp;gt; 26 (6 bits)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1f8   mov   r15b, byte [rcx+rbx]    ; index into alphabet[ecx]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1fc   test  r15b, r15b
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd1ff   js    0x4dd241               ; sign bit set → handle non-ASCII (encode_utf8 path)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After rejection, ecx &amp;gt;&amp;gt; 26 gives a 6-bit index into the 62-character alphabet. Since 62 &amp;lt; 64, the maximum 6-bit value (63) can still exceed the alphabet, the js branch at 0x4dd1ff handles the edge case via core::char::methods::encode_utf8_raw seen later. For ASCII characters (the common case), r15b is directly the selected character byte.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd201   mov   rsi, qword [rsp+0x220 {var_268}]   ; Vec len
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd209   cmp   rsi, qword [rsp+0x218 {var_278[2].q}]  ; compare len to cap
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd211   jne   0x4dd228                             ; if len &amp;lt; cap, fast path
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd213   lea   rdi, [rsp+0x210 {var_278}]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd21b   call  alloc::raw_vec::RawVec&amp;lt;...&amp;gt;::reserve_for_push  ; grow if needed
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Standard Vec::push capacity check, grow if full, otherwise fast-path the byte append.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd228   mov   rax, qword [rsp+0x210 {var_278[0].q}]  ; Vec data ptr
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd230   mov   byte [rax+rsi], r15b   ; append the character
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd234   inc   rsi
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd237   mov   qword [rsp+0x220 {var_268}], rsi  ; update len
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd23f   jmp   0x4dd26d               ; back to outer loop
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Appends the selected character to the Vec&lt;u8&gt; and increments length.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;16.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;0x4dd327:&lt;&#x2F;strong&gt; &lt;strong&gt;time-0.3.30 struct initialisation and&lt;&#x2F;strong&gt; &lt;strong&gt;Display formatting&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd327   __builtin_memcpy(dest: &amp;amp;rax_43[6],
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;               src: &amp;quot;\x05\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    &amp;quot;00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    &amp;quot;00\x05\x00\x00\x00\x00\x00\x00\x00\x0b&amp;quot;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;               count: 0x31)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is initialising the field layout of a time::PrimitiveDateTime or time::OffsetDateTime struct from the time-0.3.30 crate. The byte values correspond to date&#x2F;time fields: month=5 (May), day=1, hour=1, minute=0, second=4, etc. This is constructing a fixed or computed datetime value for use in the session record or ransom note timestamp.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd371   rax_43[0x10] = &amp;quot;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                          &amp;quot;io-6f17d22bba15001f&#x2F;time-0.3.30&#x2F;src&#x2F;date.rsjulian_day&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd385   rax_43[0x13] = &amp;quot;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                          &amp;quot;io-6f17d22bba15001f&#x2F;time-0.3.30&#x2F;src&#x2F;date.rsjulian_day&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These are &lt;strong&gt;panic location strings&lt;&#x2F;strong&gt; from &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;crate&#x2F;time&#x2F;0.3.30&#x2F;source&#x2F;src&#x2F;date.rs&quot;&gt;time-0.3.30&#x2F;src&#x2F;date.rs&lt;&#x2F;a&gt;, leaked into the binary as Rust panic metadata. The developer&amp;#39;s full filesystem path &#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;... is embedded. The &#x2F;mnt&#x2F;c&#x2F;Users&#x2F; prefix confirms the developer compiled on &lt;strong&gt;WSL2&lt;&#x2F;strong&gt; (Windows Subsystem for Linux): C:\Users... mounted as &#x2F;mnt&#x2F;c&#x2F;Users&#x2F;....&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd3f1   var_278[0].q = &amp;amp;data_5e7570
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd3f4   var_278[2].q = 2
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Sets up a &amp;amp;str fat pointer: data pointer + length 2, likely a two-character string used as a separator or suffix in the session ID string being assembled.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;004dd404   int64_t (* var_2f0_2)(int64_t* arg1) =
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;               _$LT$alloc..string..Stri....Display$GT$::fmt::h9352ad71406cfecb
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Stores the &lt;String as Display&gt;::fmt vtable function pointer into var_2f0_2. This is setting up a trait object for fmt::Display, the session ID String is about to be formatted for output (to the progress bar, log, or ransom note).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-13&quot;&gt;&lt;strong&gt;What this block does in total&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;(Likely) This is Akira v2’s &lt;strong&gt;session key &#x2F; encryption ID generation&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Reconstructed source
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let mut rng = thread_rng();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let charset: &amp;amp;[u8] = b&amp;quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789&amp;quot;;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let session_id: String = (0..64)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    .map(|_| {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &#x2F;&#x2F; rejection-sampled uniform selection from charset
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        charset[rng.gen_range(0..62)] as char
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    })
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    .collect();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; then: get current datetime via time-0.3.30
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; then: format session_id for display &#x2F; note writing
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The 64-character alphanumeric random string is the per-execution session key identifier, used to name the ransom note, tag encrypted files, or derive the symmetric encryption key depending on where it flows next.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The WSL2 developer path&lt;&#x2F;strong&gt; (&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;) is a OPSEC leak. The developer built this on a Windows machine running WSL2, using the default username user. This is consistent across all three panic strings visible in this session.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-14&quot;&gt;How is Malware Operatoring Indicatif&lt;&#x2F;h2&gt;
&lt;p&gt;The operator is shown the elapsed time of the current operation. In yarnish.rs, the relevant indicatif template string uses {elapsed} or {elapsed_precise} . SPARKLE is the static variable (emoji) used in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs#L29&quot;&gt;yarnish.rs&lt;&#x2F;a&gt; .&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;17.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;static LOOKING_GLASS: Emoji&amp;lt;&amp;#39;_, &amp;#39;_&amp;gt; = Emoji(&amp;quot;🔍  &amp;quot;, &amp;quot;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;static TRUCK: Emoji&amp;lt;&amp;#39;_, &amp;#39;_&amp;gt; = Emoji(&amp;quot;🚚  &amp;quot;, &amp;quot;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;static CLIP: Emoji&amp;lt;&amp;#39;_, &amp;#39;_&amp;gt; = Emoji(&amp;quot;🔗  &amp;quot;, &amp;quot;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;static PAPER: Emoji&amp;lt;&amp;#39;_, &amp;#39;_&amp;gt; = Emoji(&amp;quot;📃  &amp;quot;, &amp;quot;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;static SPARKLE: Emoji&amp;lt;&amp;#39;_, &amp;#39;_&amp;gt; = Emoji(&amp;quot;✨ &amp;quot;, &amp;quot;:-)&amp;quot;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-15&quot;&gt;The root cause&lt;&#x2F;h2&gt;
&lt;p&gt;The upper half of default_action contains dozens of confusing basic blocks that appear to call from_bytes_with_nul, std::fs::buffer_capacity_required, and other unrelated functions. One basic block runs for over 200 instructions. None of this is directly authored by the malware writers.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;18.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The Akira authors wanted their thread count to be intelligent. Rather than hardcoding a number, they wrote something equivalent to. The operator-supplied --threads flag has a default: the number of logical CPU cores. This is implemented as:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let threads: u32 = match threads_flag_value {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Some(n) =&amp;gt; n,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    None =&amp;gt; available_parallelism().unwrap() as u32,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;bd0826a4521a845f36cce1b00e1dd2918ba09e90&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs#L344&quot;&gt;available_parallelism()&lt;&#x2F;a&gt; is a deceptively simple call. Its Release-mode expansion is not. available_parallelism() may sound trivial, but on Linux, it is not.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-16&quot;&gt;What the Runtime Actually Has to Do&lt;&#x2F;h3&gt;
&lt;p&gt;To return a meaningful CPU count on a Linux system, the Rust standard library cannot simply call sysconf(_SC_NPROCESSORS_ONLN) and be done with it. That syscall returns the host&amp;#39;s physical CPU count, which is meaningless inside a container or a VM with a cgroup CPU quota applied. A process limited to 2 CPU-equivalents by cgroup policy on a 64-core host should not spin up 64 threads.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-17&quot;&gt;Section 4: available_parallelism()&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;-18&quot;&gt;Quick Recap&lt;&#x2F;h3&gt;
&lt;p&gt;Before diving deeper, Let’s recap what we had found so far:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Seahorse:&lt;&#x2F;strong&gt; CLI Framework, relevant function default_action&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Indicatif&lt;&#x2F;strong&gt;: Progress in CLI command, relevant file is yarnish.rs&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;-19&quot;&gt;What we are going to discover:&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;structure of the threading code&lt;&#x2F;strong&gt; in default_action matches yarnish.rs&amp;#39;s pattern: spawning N threads, collecting handles into a Vec, joining them&lt;&#x2F;li&gt;
&lt;li&gt;yarnish.rs : multithreaded source code, relevant function call available_parallelism&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;-20&quot;&gt;Now jump to yarnish.rs — Multithreading part&lt;&#x2F;h3&gt;
&lt;p&gt;As we saw earlier, the operator-facing UI is built using &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&quot;&gt;indicatif&lt;&#x2F;a&gt;, a Rust library for terminal progress bars and spinners. The malware displays a progress box with emojis labelled things like “destroying backups”, “generating symmetric session key”, “encrypting VM”, “writing ransom note”.&lt;&#x2F;p&gt;
&lt;p&gt;The source inspiration &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;yarnish.rs&lt;&#x2F;a&gt;, has an example in the indicatif repository. That example has a multithreaded outline:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;use std::thread;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let handles: Vec&amp;lt;_&amp;gt; = (0..4u32)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    .map(|i| thread::spawn(move || { ... }))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    .collect();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;It took me a bit time to realise how to connect yarnish.rs with &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&quot;&gt;available_parallelism()&lt;&#x2F;a&gt; . Basically, the function &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&quot;&gt;available_parallelism()&lt;&#x2F;a&gt; is the standard function of the &lt;strong&gt;Rust stdlib&lt;&#x2F;strong&gt;, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&quot;&gt;thread.rs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The link from yarnish.rs to a&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&quot;&gt;vailable_parallelism&lt;&#x2F;a&gt; is not direct. The chain is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;yarnish.rs&lt;&#x2F;a&gt; itself does &lt;strong&gt;not&lt;&#x2F;strong&gt; call &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&quot;&gt;available_parallelism()&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;You can confirm it hardcodes 0..4u32 with no CPU query at all. The authors added available_parallelism() themselves as a modification.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;yarnish.rs hardcodes 0..4u32
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ↓ authors modify it to be dynamic
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ↓ CLI flag --threads added via seahorse
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ↓ help text &amp;quot;Default: number of logical CPU cores&amp;quot; visible in binary
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ↓ None branch must query CPU count
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ↓ idiomatic stdlib call = available_parallelism()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;-21&quot;&gt;Binary Confirmation: Inlined Call Tree Artefacts&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;For the CPU_COUNT_S bitmask, that lives in the libc crate, not the rust stdlib. You need the correct libc crate commit that corresponds to what db034cee was depending on. Check &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;Cargo.lock&quot;&gt;the lockfile&lt;&#x2F;a&gt;, the version is listed below.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Because of Release + LTO inlining, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&quot;&gt;available_parallelism()&lt;&#x2F;a&gt; leaves these verifiable traces inside default_action:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs#L548&quot;&gt;&#x2F;proc&#x2F;self&#x2F;cgroup&lt;&#x2F;a&gt; string&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs#L364&quot;&gt;sched_getaffinity&lt;&#x2F;a&gt; syscall&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;libc&#x2F;blob&#x2F;0.2.159&#x2F;src&#x2F;unix&#x2F;linux_like&#x2F;linux&#x2F;mod.rs&quot;&gt;0x5555555555555555&lt;&#x2F;a&gt; bitmask&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs#L378&quot;&gt;sysconf(_SC_NPROCESSORS_ONLN)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of these is individually verifiable by reading the stdlib source and tracing what gets inlined. The bitmask constants are the strongest binary fingerprint because they are unique to the software popcnt expansion of CPU_COUNT_S operating on a cpu_set_t.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-22&quot;&gt;How to verify bitmask&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Verify the bitmask with correct&lt;&#x2F;strong&gt; &lt;strong&gt;libc version:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;[[package]]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;name = &amp;quot;libc&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;version = &amp;quot;0.2.159&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;source = &amp;quot;registry+https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;crates.io-index&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;checksum = &amp;quot;561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The libc crate uses git tags matching the version string exactly. The correct pinned URL for CPU_COUNT_S in the Linux GNU module is potentially in the link:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;libc&#x2F;blob&#x2F;0.2.159&#x2F;src&#x2F;unix&#x2F;linux_like&#x2F;linux&#x2F;gnu&#x2F;mod.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;However, I couldn’t find CPU_COUNT_S in the link, so I tried another method to find under the same version of libc :&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;libc&#x2F;search?q=CPU_COUNT&amp;amp;type=code&amp;amp;ref=0.2.159
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;19.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;20.webp&quot; alt=&quot;The most likely files for this inspection, but which one? Or none of them?&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;The most likely files for this inspection, but which one? Or none of them?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;teeos is a completely separate top-level target in the libc crate. It is gated by #[cfg(target_os = &amp;quot;teeos&amp;quot;)]. The Akira binary is an ELF compiled for x86_64-unknown-linux-gnu, which means target_os = &amp;quot;linux&amp;quot;. The teeos module is never compiled for a Linux target; the cfg gates are mutually exclusive.&lt;&#x2F;li&gt;
&lt;li&gt;linux_l4re_shared.rs by contrast is named and structured as a &lt;strong&gt;shared file&lt;&#x2F;strong&gt; between Linux and L4Re targets, sitting inside src&#x2F;unix&#x2F;linux_like&#x2F;. It is included via the Linux module hierarchy, which is the path taken for x86_64-unknown-linux-gnu.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;But I cannot verify which one is relevant, though their codes did the same thing basically…&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;libc&#x2F;blob&#x2F;0.2.159&#x2F;src&#x2F;unix&#x2F;linux_like&#x2F;linux&#x2F;mod.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The source code of &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;libc&#x2F;blob&#x2F;0.2.159&#x2F;src&#x2F;unix&#x2F;linux_like&#x2F;linux&#x2F;mod.rs&quot;&gt;mod.rs&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn CPU_COUNT_S(size: usize, cpuset: &amp;amp;cpu_set_t) -&amp;gt; ::c_int {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let mut s: u32 = 0;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let size_of_mask = ::mem::size_of_val(&amp;amp;cpuset.bits[0]);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        for i in cpuset.bits[..(size &#x2F; size_of_mask)].iter() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            s += i.count_ones();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        s as ::c_int
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub fn CPU_COUNT(cpuset: &amp;amp;cpu_set_t) -&amp;gt; ::c_int {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        CPU_COUNT_S(::mem::size_of::&amp;lt;cpu_set_t&amp;gt;(), cpuset)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;libc&#x2F;blob&#x2F;0.2.159&#x2F;src&#x2F;unix&#x2F;linux_like&#x2F;linux&#x2F;mod.rs#L4748&quot;&gt;&lt;strong&gt;CPU_COUNT&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; &lt;strong&gt;and&lt;&#x2F;strong&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;libc&#x2F;blob&#x2F;0.2.159&#x2F;src&#x2F;unix&#x2F;linux_like&#x2F;linux&#x2F;mod.rs#L4739&quot;&gt;&lt;strong&gt;CPU_COUNT_S&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; &lt;strong&gt;are defined directly in&lt;&#x2F;strong&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;libc&#x2F;blob&#x2F;0.2.159&#x2F;src&#x2F;unix&#x2F;linux_like&#x2F;linux&#x2F;mod.rs&quot;&gt;&lt;strong&gt;src&#x2F;unix&#x2F;linux_like&#x2F;linux&#x2F;mod.rs&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;, inside the f! macro. Not in linux_l4re_shared.rs, orteeos&#x2F;mod.rs.&lt;&#x2F;p&gt;
&lt;p&gt;linux_l4re_shared.rs contains a duplicate copy of the same functions, but for the x86_64-unknown-linux-gnu target used by the Akirav2 binary, the code that actually compiles in is from this file.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-23&quot;&gt;Back to the disassembly…&lt;&#x2F;h2&gt;
&lt;p&gt;The malware replaces the hardcoded 4u32 with the dynamic thread count from &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;thread.rs&quot;&gt;available_parallelism()&lt;&#x2F;a&gt;. That single substitution is what triggers the entire inlining cascade described above.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;21.webp&quot; alt=&quot;An immediate puzzl in &lt;code&gt;default_action&lt;&#x2F;code&gt;, linear mode in disassembly&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;An immediate puzzl in `default_action`, linear mode in disassembly&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;22.webp&quot; alt=&quot;An immediate puzzl in &lt;code&gt;default_action&lt;&#x2F;code&gt;, graph mode in disassembly&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;An immediate puzzl in `default_action`, graph mode in disassembly&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;23.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The original &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;main&#x2F;examples&#x2F;yarnish.rs&quot;&gt;yarnish.rs&lt;&#x2F;a&gt; hardcodes 4 threads. The binary does &lt;strong&gt;not&lt;&#x2F;strong&gt; hardcode 4. Instead, default_action begins with a large block of low-level calls.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The malware authors&lt;&#x2F;em&gt; &lt;em&gt;&lt;strong&gt;“probably”&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; &lt;em&gt;have replaced&lt;&#x2F;em&gt; &lt;em&gt;0..4u32 with a runtime value. That runtime value requires querying the CPU count. The standard Rust idiom for CPU count is&lt;&#x2F;em&gt; &lt;em&gt;available_parallelism().&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is &lt;strong&gt;abductive reasoning&lt;&#x2F;strong&gt; (inference to best explanation), not direct identification. The argument is:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Template originally had 0..4u32&lt;&#x2F;li&gt;
&lt;li&gt;Binary has a variable thread count&lt;&#x2F;li&gt;
&lt;li&gt;CLI flag --threads exists with explicit Default: number of logical CPU cores help text (this string survives in the binary as a literal)&lt;&#x2F;li&gt;
&lt;li&gt;Therefore the None branch of the match must call something that returns core count&lt;&#x2F;li&gt;
&lt;li&gt;The idiomatic stdlib call for that is available_parallelism()&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The compiler inlined the following call tree, five levels deep, directly into default_action:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;available_parallelism()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  -&amp;gt; quota()                     [reads &#x2F;proc&#x2F;self&#x2F;cgroup]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      -&amp;gt; quota_v1(path)           [cgroup v1 CPU quota]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      -&amp;gt; quota_v2(path)           [cgroup v2 CPU quota]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  -&amp;gt; libc::CPU_COUNT(&amp;amp;set)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      -&amp;gt; CPU_COUNT_S(size, set)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;          -&amp;gt; count_ones()         [LLVM popcnt intrinsic]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              -&amp;gt; BitPopulationCount  [expanded as magic-constant popcount]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  -&amp;gt; libc::sched_getaffinity(...)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  -&amp;gt; libc::sysconf(_SC_NPROCESSORS_ONLN)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;popcnt&quot;&gt;LLVM Instrinsic &lt;code&gt;popcnt&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;From the source code of &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;libc&#x2F;blob&#x2F;0.2.159&#x2F;src&#x2F;unix&#x2F;linux_like&#x2F;linux&#x2F;mod.rs&quot;&gt;mod.rs&lt;&#x2F;a&gt;(src&#x2F;unix&#x2F;linux_like&#x2F;linux&#x2F;mod.rs):&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn CPU_COUNT_S(size: usize, cpuset: &amp;amp;cpu_set_t) -&amp;gt; ::c_int {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let mut s: u32 = 0;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let size_of_mask = ::mem::size_of_val(&amp;amp;cpuset.bits[0]);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    for i in cpuset.bits[..(size &#x2F; size_of_mask)].iter() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        s += i.count_ones();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    s as ::c_int
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On x86–64, cpu_set_t.bits is [u64; 16] . Each iteration calls u64::count_ones().&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-24&quot;&gt;Proving the chain: count_ones() to llvm.ctpop.i64&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;A brief summary:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Step 1.&lt;&#x2F;strong&gt; u64::count_ones() calls intrinsics::ctpop(self). Source-confirmed, from pasted uint_macros.rs at commit db034cee.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Step 2.&lt;&#x2F;strong&gt; Rust commit db034cee uses LLVM 19.1.3, submodule pinned to b35599b. Source-confirmed, from your screenshot of github.com&#x2F;rust-lang&#x2F;rust&#x2F;tree&#x2F;db034cee...&#x2F;src.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Step 3.&lt;&#x2F;strong&gt; LLVM 19.1.3 registers ISD::CTPOP &#x2F; MVT::i64 as Custom when Subtarget.hasPOPCNT() is false. Source-confirmed, from pasted X86ISelLowering.cpp at llvmorg-19.1.3.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Step 4.&lt;&#x2F;strong&gt; The custom handler LowerCTPOP returns SDValue() for a full 64-bit operand with no statically known zero bits, signalling fallback to generic expansion. Source-confirmed, from pasted LowerCTPOP at llvmorg-19.1.3.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Step 5.&lt;&#x2F;strong&gt; The generic expansion is TargetLowering::expandCTPOP in llvm&#x2F;lib&#x2F;CodeGen&#x2F;SelectionDAG&#x2F;TargetLowering.cpp. It constructs Mask55, Mask33, and Mask0F via APInt::getSplat(64, APInt(8, 0x55&#x2F;0x33&#x2F;0x0F)), producing 0x5555555555555555, 0x3333333333333333, and 0x0f0f0f0f0f0f0f0f. The code cites its own source: Sean Anderson&amp;#39;s &amp;quot;Bit Twiddling Hacks&amp;quot;. Source-confirmed, from pasted TargetLowering.cpp at llvmorg-19.1.3.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Step 6.&lt;&#x2F;strong&gt; The same algorithm, same constants, appears in the Flang runtime header at LLVM commit 521c996. Source-confirmed, from pasted bit-population-count.h. This confirms the constants are a property of LLVM&amp;#39;s target-independent infrastructure, not of the source language.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;-25&quot;&gt;Now let’s breakdown step by step.&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Layer 1:&lt;&#x2F;strong&gt; &lt;strong&gt;count_ones() to&lt;&#x2F;strong&gt; &lt;strong&gt;core::intrinsics::ctpop&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;At commit 79e9716c980570bfd1f666e3b16ac583f0168962, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;79e9716c980570bfd1f666e3b16ac583f0168962&#x2F;library&#x2F;core&#x2F;src&#x2F;num&#x2F;uint_macros.rs&quot;&gt;library&#x2F;core&#x2F;src&#x2F;num&#x2F;uint_macros.rs&lt;&#x2F;a&gt; defines count_ones as:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#[stable(feature = &amp;quot;rust1&amp;quot;, since = &amp;quot;1.0.0&amp;quot;)]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#[rustc_const_stable(feature = &amp;quot;const_math&amp;quot;, since = &amp;quot;1.32.0&amp;quot;)]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#[doc(alias = &amp;quot;popcount&amp;quot;)]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#[doc(alias = &amp;quot;popcnt&amp;quot;)]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#[must_use = &amp;quot;this returns the result of the operation, \
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              without modifying the original&amp;quot;]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#[inline(always)]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub const fn count_ones(self) -&amp;gt; u32 {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return intrinsics::ctpop(self);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;For a u64 receiver, intrinsics::ctpop(self) compiles to the LLVM IR call llvm.ctpop.i64.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Two details from the attributes are significant.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;#[inline(always)] means the call is always inlined at the call site, so no call instruction is ever emitted for it.&lt;&#x2F;li&gt;
&lt;li&gt;#[doc(alias = &amp;quot;popcnt&amp;quot;)] is a documentation alias that the Rust compiler team added precisely because this function is the stable surface of the hardware popcount concept.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The body delegates unconditionally to intrinsics::ctpop, a Rust compiler intrinsic with no Rust-level implementation: it is a direct instruction to the compiler backend to emit the LLVM IR node ISD::CTPOP, which for a u64 operand produces llvm.ctpop.i64.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Layer 2:&lt;&#x2F;strong&gt; &lt;strong&gt;llvm.ctpop.i64 to machine instruction, conditional on&lt;&#x2F;strong&gt;**+popcnt**&lt;&#x2F;p&gt;
&lt;p&gt;It is very likely commit 79e9716 using &lt;strong&gt;rustc 1.74.0&lt;&#x2F;strong&gt;, LLVM 17.x, pinned to &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;llvm-project&#x2F;tree&#x2F;fef3d7b14ede45d051dc688aae0bb8c8b02a0566&quot;&gt;rust-lang&#x2F;llvm-project&lt;&#x2F;a&gt; at commit &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;llvm-project&#x2F;tree&#x2F;fef3d7b14ede45d051dc688aae0bb8c8b02a0566&quot;&gt;fef3d7b14ede45d051dc688aae0bb8c8b02a0566&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;24.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The LLVM x86 instruction selection file X86ISelLowering.cpp contains the following logic (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;llvm&#x2F;llvm-project&#x2F;refs&#x2F;tags&#x2F;llvmorg-19.1.3&#x2F;llvm&#x2F;lib&#x2F;Target&#x2F;X86&#x2F;X86ISelLowering.cpp&quot;&gt;llvmorg-19.1.3&#x2F;llvm&#x2F;lib&#x2F;Target&#x2F;X86&#x2F;X86ISelLowering.cpp&lt;&#x2F;a&gt;):&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if (Subtarget.hasPOPCNT()) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    setOperationPromotedToType(ISD::CTPOP, MVT::i8, MVT::i32);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; popcntw is longer to encode than popcntl and also has a false dependency
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; on the dest that popcntl hasn&amp;#39;t had since Cannon Lake.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    setOperationPromotedToType(ISD::CTPOP, MVT::i16, MVT::i32);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  } else {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    setOperationAction(ISD::CTPOP          , MVT::i8   , Custom);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    setOperationAction(ISD::CTPOP          , MVT::i16  , Custom);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    setOperationAction(ISD::CTPOP          , MVT::i32  , Custom);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    setOperationAction(ISD::CTPOP          , MVT::i64  , Custom);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the registration of how LLVM 19.1.3 will handle ISD::CTPOP for each integer width on x86. When Subtarget.hasPOPCNT() is false, MVT::i64 is marked Custom, meaning LLVM will call a custom lowering function rather than emitting a single popcnt instruction. This is the decision point that determines whether the bitmask sequence appears.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-26&quot;&gt;Where the constants come from&lt;&#x2F;h2&gt;
&lt;p&gt;They come from TargetLowering::expandCTPOP in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;llvm&#x2F;llvm-project&#x2F;refs&#x2F;tags&#x2F;llvmorg-19.1.3&#x2F;llvm&#x2F;lib&#x2F;CodeGen&#x2F;SelectionDAG&#x2F;TargetLowering.cpp&quot;&gt;llvm&#x2F;lib&#x2F;CodeGen&#x2F;SelectionDAG&#x2F;TargetLowering.cpp&lt;&#x2F;a&gt;. This is the &lt;strong&gt;target-independent&lt;&#x2F;strong&gt; generic expansion function in LLVM&amp;#39;s code generation infrastructure.&lt;&#x2F;p&gt;
&lt;p&gt;The call chain when +popcnt is absent is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;LowerCTPOP (X86ISelLowering.cpp)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    -&amp;gt; returns SDValue() for full 64-bit operands
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        -&amp;gt; LegalizeDAG calls TargetLowering::expandCTPOP (TargetLowering.cpp)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            -&amp;gt; emits the bitmask sequence
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The constants appear in expandCTPOP as the Hamming weight algorithm. The Flang header you pasted is a human-readable C++ expression of exactly the same algorithm. The constants are identical because both implement the same mathematical identity: to count set bits, partition the word into 2-bit fields, count each, then sum progressively wider groups.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The algorithm works by a divide-and-conquer strategy: count bits in groups of 2, then merge groups of 4, then 8, and so on until the whole word is summed. Each step uses a bitmask to isolate the relevant bits.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;SDValue TargetLowering::expandCTPOP(SDNode *Node, SelectionDAG &amp;amp;DAG) const {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  ...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  &#x2F;&#x2F; This is the &amp;quot;best&amp;quot; algorithm from
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  &#x2F;&#x2F; http:&#x2F;&#x2F;graphics.stanford.edu&#x2F;~seander&#x2F;bithacks.html#CountBitsSetParallel
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  SDValue Mask55 =
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      DAG.getConstant(APInt::getSplat(Len, APInt(8, 0x55)), dl, VT);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  SDValue Mask33 =
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      DAG.getConstant(APInt::getSplat(Len, APInt(8, 0x33)), dl, VT);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  SDValue Mask0F =
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      DAG.getConstant(APInt::getSplat(Len, APInt(8, 0x0F)), dl, VT);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;-27&quot;&gt;&lt;strong&gt;What the Akira disassembly tells you&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;The report&lt;&#x2F;a&gt; observes those exact bitmask constants in the Akira assembly. This confirms that akirav2.elf was compiled without the +popcnt target feature, so llvm.ctpop.i64 expanded to the software path rather than a single popcnt instruction.&lt;&#x2F;p&gt;
&lt;p&gt;u64::count_ones() at commit db034cee unconditionally delegates to core::intrinsics::ctpop, which maps to llvm.ctpop.i64. Whether that IR node produces a single popcnt rax, rdi or a multi-instruction software bitmask sequence depends solely on whether +popcnt is present in the compile-time target feature set. In akirav2.elf, the software path was taken, evidenced by the bitmask constants visible in the disassembly.&lt;&#x2F;p&gt;
&lt;p&gt;The popcount expansion is what produces the 200-instruction basic block. The standard software-popcount algorithm uses a sequence of bitmasks (0x5555555555555555, 0x3333333333333333, 0x0f0f0f0f0f0f0f0f) that are recognisable once you know what you are looking at:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;x = (x &amp;amp; 0x5555555555555555) + ((x &amp;gt;&amp;gt; 1) &amp;amp; 0x5555555555555555);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;x = (x &amp;amp; 0x3333333333333333) + ((x &amp;gt;&amp;gt; 2) &amp;amp; 0x3333333333333333);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The appearance of these constants in an otherwise opaque basic block is the forensic fingerprint that tells you the entire block is an inlined CPU_COUNT.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;&#x2F;strong&gt;: in Release-mode Rust binaries, a single innocuous standard library call can expand into hundreds of instructions through aggressive inlining. The correct analysis strategy is to identify the innermost inlined functions by their constants and patterns first, then work outward to reconstruct the call tree.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-28&quot;&gt;Section 5: VM Shutdown (--stopvm)&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;I have discovered this in the previous article &lt;a href=&quot;https:&#x2F;&#x2F;yenwang.dev&#x2F;blog&#x2F;medium-akira-part-1&#x2F;&quot;&gt;Akirav2 Analysis Part 1&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;25.webp&quot; alt=&quot;Some interesting strings found in the section of &lt;code&gt;.rodata&lt;&#x2F;code&gt;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Some interesting strings found in the section of `.rodata`&lt;&#x2F;p&gt;
&lt;p&gt;If --stopvm is passed, default_action forks a child process and executes:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;vim-cmd vmsvc&#x2F;getallvms | tail -n +2 | awk &amp;#39;{system(&amp;quot;vim-cmd vmsvc&#x2F;power.off &amp;quot; $1)}&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This uses ESXi’s built-in vim-cmd management utility to enumerate all registered VMs and power them off sequentially. The ransomware must run with sufficient ESXi privileges for this to succeed. This is a standard pre-encryption step in ESXi-targeting ransomware: powered-on VMs hold file locks that block encryption.&lt;&#x2F;p&gt;
&lt;p&gt;Looking at the strings section and it contains multiple critical findings:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;ESXi targeting confirmed:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;vmfs&#x2F;volumes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;vim-cmd vmsvc&#x2F;getallvms | tail -n +2 | awk &amp;#39;{system(&amp;quot;vim-cmd vmsvc&#x2F;power.off &amp;quot; $1)}&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the exact shell command to enumerate and power off all VMs on an ESXi host, confirming the Check Point analysis.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ESXi&lt;&#x2F;strong&gt; is VMware’s bare-metal hypervisor. It runs directly on server hardware (no host OS underneath) and hosts multiple virtual machines. In enterprise environments it is extremely common: one physical server running ESXi might host dozens of VMs for databases, domain controllers, file servers, etc.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;-29&quot;&gt;Why ransomware authors love targeting it?&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One infection, massive blast radius.&lt;&#x2F;strong&gt; Encrypt the ESXi host and every VM it manages goes down simultaneously, rather than having to compromise each machine individually.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Often poorly monitored.&lt;&#x2F;strong&gt; EDR agents are rarely deployed on hypervisor hosts the way they are on Windows endpoints.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Critical infrastructure.&lt;&#x2F;strong&gt; Organisations are under extreme pressure to pay because their entire virtualised environment is offline.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;NFS&#x2F;VMFS datastores.&lt;&#x2F;strong&gt; VM disk images (.vmdk files) live on shared storage volumes that ESXi mounts directly, so the ransomware can enumerate and encrypt them efficiently without touching individual guest OSes.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The --path &#x2F;vmfs&#x2F;volumes default you saw in the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;Check Point article&lt;&#x2F;a&gt; is the standard ESXi datastore mount point. That single directory contains the disk images for every hosted VM.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-30&quot;&gt;RE Methodology Takeaways&lt;&#x2F;h2&gt;
&lt;p&gt;Check Point’s report is as much about method as it is about this specific binary. The principles that made it possible:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Do not start by reading assembly top-down.&lt;&#x2F;strong&gt; Identify framework code first (seahorse, indicatif) by searching for function names and strings that appear in the binary.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Recognise inlining by constants, not call structure.&lt;&#x2F;strong&gt; Magic constants like popcount masks, cgroup path strings (&#x2F;proc&#x2F;self&#x2F;cgroup), and well-known errno values are fingerprints of inlined library functions even when no call instruction is present.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Know the type layouts.&lt;&#x2F;strong&gt; Understanding that IntoIter&lt;OsString&gt; is a 32-byte header with a 24-byte-strided heap buffer makes the argument-parsing assembly readable. Without that, it is just pointer arithmetic.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Follow the PAL boundary.&lt;&#x2F;strong&gt; Standard library types like Args and OsString have OS-specific implementations. The assembly reflects the PAL target, not the public API.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Release vs Debug mode.&lt;&#x2F;strong&gt; The same source code can produce thousands of additional instructions in Release mode due to inlining, loop unrolling, and LLVM intrinsic expansion.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;-31&quot;&gt;Further Reading&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;-32&quot;&gt;1. SIMD&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;SIMD stands for &lt;strong&gt;Single Instruction, Multiple Data&lt;&#x2F;strong&gt;. It is a CPU execution model where one instruction operates on multiple data elements in parallel, using wide registers.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;-33&quot;&gt;What SIMD means in hardware&lt;&#x2F;h3&gt;
&lt;p&gt;Instead of adding two 32-bit integers, a SIMD instruction can add eight 32-bit integers simultaneously, all in one clock cycle, using a 256-bit register (like ymm0).&lt;&#x2F;p&gt;
&lt;p&gt;Register families you will encounter:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MMX&lt;&#x2F;strong&gt; (64-bit, obsolete)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;SSE &#x2F; SSE2 &#x2F; SSE4&lt;&#x2F;strong&gt; (xmm0–xmm15, 128-bit)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;AVX &#x2F; AVX2&lt;&#x2F;strong&gt; (ymm0–ymm15, 256-bit)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;AVX-512&lt;&#x2F;strong&gt; (zmm0–zmm31, 512-bit)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;-34&quot;&gt;Why it matters in RE&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Compiler auto-vectorisation.&lt;&#x2F;strong&gt; Compilers (LLVM, GCC, MSVC) aggressively auto-vectorise loops. A simple memcpy, memcmp, or string scan in source becomes SIMD instructions in the binary. This makes hot loops look unfamiliar if you are not expecting it.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Obfuscation and packing.&lt;&#x2F;strong&gt; Packers and obfuscators sometimes use SIMD intentionally to complicate static taint analysis, because most taint engines either do not track xmm&#x2F;ymm registers at all or do so poorly.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Rust and SIMD.&lt;&#x2F;strong&gt; Rust’s standard library uses SIMD for things like memchr (which backs aho-corasick internally). When you see a tight loop in a Rust binary using pcmpeqb or vpcmpeqb, it is almost certainly memchr or a SIMD-accelerated pattern search, not custom malware logic. Relevant to your Akira analysis: if you see SIMD near the search routines, that is likely the aho-corasick crate doing pattern matching.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Cryptography.&lt;&#x2F;strong&gt; AES-NI (aesenc, aesdec) is technically a SIMD-adjacent extension. Much ransomware uses AES, usually via AES-NI (aesenc and friends), so spotting aesenc is normally a strong sign AES is present. This Akira v2 sample instead uses the SOSEMANUK stream cipher, implemented in software with no AES-NI, and indeed no aesenc, aesdec or related opcodes appear anywhere in the 428k-line disassembly. The asymmetry matters: absence of aesenc only rules out AES-NI-accelerated AES (software AES or any stream cipher would also lack it), so it is a useful negative filter, not a positive identification. The positive call rests on the SOSEMANUK constants (the 0x54655307 FSM multiplier with rotate-left-7, and the Serpent key-schedule constant 0x9E3779B9).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;-35&quot;&gt;Common SIMD instructions worth recognising&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;26.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;vzeroupper in particular is a useful marker: it almost always appears at the end of a function that used AVX, so it helps you identify function boundaries in stripped binaries.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-36&quot;&gt;2. How aho-corasick uses memchr and SIMD&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;-37&quot;&gt;The dependency chain&lt;&#x2F;h3&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;aho-corasick  →  memchr  →  SIMD intrinsics (platform-dependent)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;aho-corasick uses memchr as an accelerator for its &amp;quot;teddy&amp;quot; algorithm, which is its SIMD-accelerated literal search backend. The relevant source locations:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;aho-corasick calling&lt;&#x2F;strong&gt; &lt;strong&gt;memchr:&lt;&#x2F;strong&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;aho-corasick&#x2F;blob&#x2F;master&#x2F;src&#x2F;packed&#x2F;teddy&#x2F;generic.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;aho-corasick&#x2F;blob&#x2F;master&#x2F;src&#x2F;packed&#x2F;teddy&#x2F;generic.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The teddy backend uses 128-bit or 256-bit shuffles to match candidate bytes in parallel before running the full automaton. Look specifically at the find_at methods and the Teddy struct.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;memchr SIMD implementation:&lt;&#x2F;strong&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;memchr&#x2F;blob&#x2F;master&#x2F;src&#x2F;arch&#x2F;x86_64&#x2F;sse2&#x2F;memchr.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;memchr&#x2F;blob&#x2F;master&#x2F;src&#x2F;arch&#x2F;x86_64&#x2F;sse2&#x2F;memchr.rs&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;memchr&#x2F;blob&#x2F;master&#x2F;src&#x2F;arch&#x2F;x86_64&#x2F;avx2&#x2F;memchr.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;memchr&#x2F;blob&#x2F;master&#x2F;src&#x2F;arch&#x2F;x86_64&#x2F;avx2&#x2F;memchr.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The SSE2 path uses _mm_cmpeq_epi8 (which compiles to pcmpeqb) and _mm_movemask_epi8 (which compiles to pmovmskb). The Advanced Vector Extensions (AVX2) path uses the ymm equivalents.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;www&#x2F;us&#x2F;en&#x2F;docs&#x2F;intrinsics-guide&#x2F;index.html&quot;&gt;Advanced Vector Extensions&lt;&#x2F;a&gt;, &lt;strong&gt;AVX,&lt;&#x2F;strong&gt; is Intel’s extension to the x86 ISA that widens SIMD registers from 128-bit (SSE) to 256-bit, and later 512-bit (AVX-512).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-38&quot;&gt;What this looks like in a stripped binary&lt;&#x2F;h2&gt;
&lt;p&gt;In Binary Ninja, when you find the aho-corasick search loop (recoverable from your panic strings since you already confirmed crate aho-corasick 1.1.2), expect to see a structure like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; Outer loop: advance through the haystack in 16 or 32-byte chunks
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;vmovdqu  ymm0, [rsi + rax]       ; load 32 bytes of haystack
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;vpshufb  ymm1, ymm2, ymm0        ; teddy shuffle: match low nibbles
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;vpshufb  ymm3, ymm4, ymm0        ; teddy shuffle: match high nibbles
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;vpand    ymm1, ymm1, ymm3        ; AND the masks together
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;vptest   ymm1, ymm1              ; test if any candidates found
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;jz       .next_chunk             ; no match, advance
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; ... fallthrough to verification logic
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;vpshufb (SIMD byte shuffle) is the &lt;strong&gt;fingerprint of the teddy algorithm&lt;&#x2F;strong&gt; specifically. If you see vpshufb in a tight loop over a memory region, that is almost certainly teddy, not custom logic.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-39&quot;&gt;What version 1.1.2 specifically uses&lt;&#x2F;h2&gt;
&lt;p&gt;The recovered crate version is aho-corasick 1.1.2. At that version:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Source tag: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;aho-corasick&#x2F;tree&#x2F;1.1.2&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;aho-corasick&#x2F;tree&#x2F;1.1.2&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Teddy implementation: src&#x2F;packed&#x2F;teddy&#x2F; directory&lt;&#x2F;li&gt;
&lt;li&gt;The Fat (256-bit AVX2) and Slim (128-bit SSE2) variants are both present; which one your binary uses depends on what the compile target supported&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Check src&#x2F;packed&#x2F;teddy&#x2F;generic.rs at that tag for the exact loop structure: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;aho-corasick&#x2F;blob&#x2F;1.1.2&#x2F;src&#x2F;packed&#x2F;teddy&#x2F;generic.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;aho-corasick&#x2F;blob&#x2F;1.1.2&#x2F;src&#x2F;packed&#x2F;teddy&#x2F;generic.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-40&quot;&gt;How to confirm in the Akira binary&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;In Binary Ninja, search for vpshufb or pcmpeqb instructions in the disassembly&lt;&#x2F;li&gt;
&lt;li&gt;If found in a loop iterating over a buffer, cross-reference against the panic string offsets you already recovered for aho-corasick&lt;&#x2F;li&gt;
&lt;li&gt;The function containing this loop is the teddy search kernel; it will be called from the automaton search dispatch&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Though I cannot tell you the exact virtual address or function offset because that requires your specific binary. The panic string Relative Virtual Addresses (RVAs) that have already been recovered are the anchor points for finding the call sites.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-2&#x2F;27.webp&quot; alt=&quot;Thanks for reading&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Thanks for reading&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-41&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Source Code &amp;amp; API Documentation&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Rust-lang (no date) &lt;em&gt;core::ffi::c_str — CStr implementation&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;main&#x2F;library&#x2F;core&#x2F;src&#x2F;ffi&#x2F;c_str.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;main&#x2F;library&#x2F;core&#x2F;src&#x2F;ffi&#x2F;c_str.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Rust-lang (no date) &lt;em&gt;std::ffi::c_str — re-export layer&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;main&#x2F;library&#x2F;std&#x2F;src&#x2F;ffi&#x2F;c_str.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;main&#x2F;library&#x2F;std&#x2F;src&#x2F;ffi&#x2F;c_str.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Rust-lang (2023) &lt;em&gt;std::sys::unix::fs — historical unix filesystem implementation including open_c&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;5a7834050f3a0ebcd117b4ddf0bc1e8459594309&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;unix&#x2F;fs.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;5a7834050f3a0ebcd117b4ddf0bc1e8459594309&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;unix&#x2F;fs.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Rust-lang (no date) &lt;em&gt;std::sys::fs::unix — current unix filesystem implementation&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;main&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;fs&#x2F;unix.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;main&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;fs&#x2F;unix.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Rust-lang (no date) &lt;em&gt;core::ptr::mod — drop_in_place definition&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;main&#x2F;library&#x2F;core&#x2F;src&#x2F;ptr&#x2F;mod.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;main&#x2F;library&#x2F;core&#x2F;src&#x2F;ptr&#x2F;mod.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Rust-lang (no date) &lt;em&gt;std::fs — public File and OpenOptions API&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;main&#x2F;library&#x2F;std&#x2F;src&#x2F;fs.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;main&#x2F;library&#x2F;std&#x2F;src&#x2F;fs.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Rust-lang (no date) &lt;em&gt;core::ptr::drop_in_place — stable API documentation&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;beta&#x2F;core&#x2F;ptr&#x2F;fn.drop_in_place.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;beta&#x2F;core&#x2F;ptr&#x2F;fn.drop_in_place.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;ksk001100 (no date) &lt;em&gt;seahorse::app — App struct and run_with_result implementation&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ksk001100&#x2F;seahorse&#x2F;blob&#x2F;master&#x2F;src&#x2F;app.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;ksk001100&#x2F;seahorse&#x2F;blob&#x2F;master&#x2F;src&#x2F;app.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;console-rs (no date) &lt;em&gt;indicatif — progress bar and spinner library&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Threat Intelligence &amp;amp; Research Reports&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Check Point Research (2024) &lt;em&gt;Inside Akira Ransomware’s Rust Experiment&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;CISA, FBI, EC3 and NCSC-NL (2024) &lt;em&gt;#StopRansomware: Akira Ransomware&lt;&#x2F;em&gt; [Joint Cybersecurity Advisory AA24–109A]. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.cisa.gov&#x2F;news-events&#x2F;cybersecurity-advisories&#x2F;aa24-109a&quot;&gt;https:&#x2F;&#x2F;www.cisa.gov&#x2F;news-events&#x2F;cybersecurity-advisories&#x2F;aa24-109a&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;CISA, FBI, EC3 and NCSC-NL (2024) &lt;em&gt;#StopRansomware: Akira Ransomware&lt;&#x2F;em&gt; [PDF version]. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ic3.gov&#x2F;CSA&#x2F;2024&#x2F;240418.pdf&quot;&gt;https:&#x2F;&#x2F;www.ic3.gov&#x2F;CSA&#x2F;2024&#x2F;240418.pdf&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Cisco Talos (2024) &lt;em&gt;Akira Ransomware Continues to Evolve&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.talosintelligence.com&#x2F;akira-ransomware-continues-to-evolve&#x2F;&quot;&gt;https:&#x2F;&#x2F;blog.talosintelligence.com&#x2F;akira-ransomware-continues-to-evolve&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Palo Alto Unit 42 (2024) &lt;em&gt;Threat Assessment: Howling Scorpius (Akira Ransomware)&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unit42.paloaltonetworks.com&#x2F;threat-assessment-howling-scorpius-akira-ransomware&#x2F;&quot;&gt;https:&#x2F;&#x2F;unit42.paloaltonetworks.com&#x2F;threat-assessment-howling-scorpius-akira-ransomware&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;MITRE ATT&amp;amp;CK (no date) &lt;em&gt;Akira_v2, Software S1194&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;attack.mitre.org&#x2F;software&#x2F;S1194&#x2F;&quot;&gt;https:&#x2F;&#x2F;attack.mitre.org&#x2F;software&#x2F;S1194&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Security Online (2024) &lt;em&gt;Akira v2 Emerges: Rust-Based Ransomware Raises the Stakes&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;securityonline.info&#x2F;akira-v2-emerges-rust-based-ransomware-raises-the-stakes&#x2F;&quot;&gt;https:&#x2F;&#x2F;securityonline.info&#x2F;akira-v2-emerges-rust-based-ransomware-raises-the-stakes&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;aho-corasick &#x2F; Teddy&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Burntsushi (no date) &lt;em&gt;aho-corasick 1.1.2 — packed::teddy::builder&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;aho-corasick&#x2F;1.1.2&#x2F;src&#x2F;aho_corasick&#x2F;packed&#x2F;teddy&#x2F;builder.rs.html&quot;&gt;https:&#x2F;&#x2F;docs.rs&#x2F;aho-corasick&#x2F;1.1.2&#x2F;src&#x2F;aho_corasick&#x2F;packed&#x2F;teddy&#x2F;builder.rs.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Intel ISA and SIMD architecture&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Intel 64 and IA-32 Architectures Software Developer’s Manual: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;www&#x2F;us&#x2F;en&#x2F;developer&#x2F;articles&#x2F;technical&#x2F;intel-sdm.html&quot;&gt;https:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;www&#x2F;us&#x2F;en&#x2F;developer&#x2F;articles&#x2F;technical&#x2F;intel-sdm.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Intel Intrinsics Guide (instruction-level reference): &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;www&#x2F;us&#x2F;en&#x2F;docs&#x2F;intrinsics-guide&#x2F;index.html&quot;&gt;https:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;www&#x2F;us&#x2F;en&#x2F;docs&#x2F;intrinsics-guide&#x2F;index.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;SSE &#x2F; AVX register sets&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Felix Cloutier’s x86 instruction reference (community mirror, widely used): &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.felixcloutier.com&#x2F;x86&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.felixcloutier.com&#x2F;x86&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Compiler auto-vectorisation&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;LLVM auto-vectorisation documentation: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;llvm.org&#x2F;docs&#x2F;Vectorizers.html&quot;&gt;https:&#x2F;&#x2F;llvm.org&#x2F;docs&#x2F;Vectorizers.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;MSVC auto-vectorisation reference: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;cpp&#x2F;parallel&#x2F;auto-parallelization-and-auto-vectorization&quot;&gt;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;cpp&#x2F;parallel&#x2F;auto-parallelization-and-auto-vectorization&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Rust memchr and SIMD&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;memchr crate (Andrew Gallant): &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;memchr&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;memchr&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;aho-corasick crate SIMD internals: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;aho-corasick&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;aho-corasick&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;AES-NI&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Intel AES-NI white paper: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;dam&#x2F;doc&#x2F;white-paper&#x2F;advanced-encryption-standard-new-instructions-set-paper.pdf&quot;&gt;https:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;dam&#x2F;doc&#x2F;white-paper&#x2F;advanced-encryption-standard-new-instructions-set-paper.pdf&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;vzeroupper behaviour&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Agner Fog optimisation manuals (function boundary and transition costs): &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.agner.org&#x2F;optimize&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.agner.org&#x2F;optimize&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;AVX&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Intel Intrinsics Guide (AVX&#x2F;AVX2 filter): &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;www&#x2F;us&#x2F;en&#x2F;docs&#x2F;intrinsics-guide&#x2F;index.html&quot;&gt;https:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;www&#x2F;us&#x2F;en&#x2F;docs&#x2F;intrinsics-guide&#x2F;index.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Agner Fog: transition penalties and vzeroupper behaviour: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.agner.org&#x2F;optimize&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.agner.org&#x2F;optimize&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Felix Cloutier x86 reference (vzeroupper): &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.felixcloutier.com&#x2F;x86&#x2F;vzeroupper&quot;&gt;https:&#x2F;&#x2F;www.felixcloutier.com&#x2F;x86&#x2F;vzeroupper&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;popcnt&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The Rust Project Developers (n.d.) core&#x2F;num&#x2F;uint_macros.rs source, &lt;em&gt;Rust Standard Library Source&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;src&#x2F;core&#x2F;num&#x2F;uint_macros.rs.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;src&#x2F;core&#x2F;num&#x2F;uint_macros.rs.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;LLVM Project (2024) llvm&#x2F;lib&#x2F;Target&#x2F;X86&#x2F;X86ISelLowering.cpp at tag llvmorg-19.1.3. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;llvm&#x2F;llvm-project&#x2F;refs&#x2F;tags&#x2F;llvmorg-19.1.3&#x2F;llvm&#x2F;lib&#x2F;Target&#x2F;X86&#x2F;X86ISelLowering.cpp&quot;&gt;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;llvm&#x2F;llvm-project&#x2F;refs&#x2F;tags&#x2F;llvmorg-19.1.3&#x2F;llvm&#x2F;lib&#x2F;Target&#x2F;X86&#x2F;X86ISelLowering.cpp&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;LLVM Project (2024) llvm&#x2F;lib&#x2F;CodeGen&#x2F;SelectionDAG&#x2F;TargetLowering.cpp at tag llvmorg-19.1.3, function expandCTPOP. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;llvm&#x2F;llvm-project&#x2F;refs&#x2F;tags&#x2F;llvmorg-19.1.3&#x2F;llvm&#x2F;lib&#x2F;CodeGen&#x2F;SelectionDAG&#x2F;TargetLowering.cpp&quot;&gt;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;llvm&#x2F;llvm-project&#x2F;refs&#x2F;tags&#x2F;llvmorg-19.1.3&#x2F;llvm&#x2F;lib&#x2F;CodeGen&#x2F;SelectionDAG&#x2F;TargetLowering.cpp&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;LLVM Project (2024) flang&#x2F;include&#x2F;flang&#x2F;Common&#x2F;bit-population-count.h at commit 521c996. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;llvm&#x2F;llvm-project&#x2F;blob&#x2F;521c99627690e09ba25383c83232f94ff457f00c&#x2F;flang&#x2F;include&#x2F;flang&#x2F;Common&#x2F;bit-population-count.h&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;llvm&#x2F;llvm-project&#x2F;blob&#x2F;521c99627690e09ba25383c83232f94ff457f00c&#x2F;flang&#x2F;include&#x2F;flang&#x2F;Common&#x2F;bit-population-count.h&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Anderson, S. (n.d.) ‘Bit Twiddling Hacks: Count bits set in parallel’. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;graphics.stanford.edu&#x2F;~seander&#x2F;bithacks.html#CountBitsSetParallel&quot;&gt;http:&#x2F;&#x2F;graphics.stanford.edu&#x2F;~seander&#x2F;bithacks.html#CountBitsSetParallel&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;lock function reconstruction&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The Rust Project Developers (2024) &lt;em&gt;std::thread::available_parallelism&lt;&#x2F;em&gt;. The Rust Standard Library. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;thread&#x2F;fn.available_parallelism.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;thread&#x2F;fn.available_parallelism.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Tompkins, W. et al. (2024) &lt;em&gt;Akira ransomware continues to evolve&lt;&#x2F;em&gt;. Cisco Talos Intelligence. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.talosintelligence.com&#x2F;akira-ransomware-continues-to-evolve&#x2F;&quot;&gt;https:&#x2F;&#x2F;blog.talosintelligence.com&#x2F;akira-ransomware-continues-to-evolve&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;console-rs (2024) &lt;em&gt;indicatif yarnish.rs example&lt;&#x2F;em&gt;. GitHub. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;ksk001100 (2024) &lt;em&gt;seahorse CLI framework&lt;&#x2F;em&gt;. GitHub. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ksk001100&#x2F;seahorse&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;ksk001100&#x2F;seahorse&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Decoding UTF-8 Hex Strings in Python</title>
        <published>2026-05-17T00:00:00+00:00</published>
        <updated>2026-05-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yenwang.dev/blog/medium-utf8-hex-python/"/>
        <id>https://yenwang.dev/blog/medium-utf8-hex-python/</id>
        
        <content type="html" xml:base="https://yenwang.dev/blog/medium-utf8-hex-python/">&lt;p&gt;When reverse engineering binaries, you often encounter raw hex bytes in .rodata that represent Unicode strings. Binary Ninja and similar tools sometimes render these as dots or garbage rather than the intended characters. This script bridges that gap.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Source code can be found in my Github repo &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Yen-Coder&#x2F;The-State-of-Rust-in-Malware-Programming&#x2F;blob&#x2F;main&#x2F;tools&#x2F;UTF8_decoder.py&quot;&gt;&#x2F; tools&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;utf8-hex-python&#x2F;01.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;&quot;&gt;The Code&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;import sys
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;def decode_hex(hex_input: str) -&amp;gt; str:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    hex_clean = hex_input.replace(&amp;quot; &amp;quot;, &amp;quot;&amp;quot;).replace(&amp;quot;-&amp;quot;, &amp;quot;&amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    raw = bytes.fromhex(hex_clean)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return raw.decode(&amp;quot;utf-8&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if __name__ == &amp;quot;__main__&amp;quot;:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if len(sys.argv) &amp;gt; 1:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        print(decode_hex(&amp;quot; &amp;quot;.join(sys.argv[1:])))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    else:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        for line in sys.stdin:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            line = line.strip()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            if line:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                try:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    print(decode_hex(line))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                except (ValueError, UnicodeDecodeError) as e:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    print(f&amp;quot;Error: {e}&amp;quot;, file=sys.stderr)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-1&quot;&gt;Step 1: Imports&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;import sys
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;sys gives access to sys.argv (command-line arguments) and sys.stdin (piped input). No third-party dependencies are needed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-2&quot;&gt;Step 2: The decode_hex function&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;def decode_hex(hex_input: str) -&amp;gt; str:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    hex_clean = hex_input.replace(&amp;quot; &amp;quot;, &amp;quot;&amp;quot;).replace(&amp;quot;-&amp;quot;, &amp;quot;&amp;quot;).replace(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    raw = bytes.fromhex(hex_clean)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return raw.decode(&amp;quot;utf-8&amp;quot;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Normalisation.&lt;&#x2F;strong&gt; bytes.fromhex requires a clean hex string with no separators. The three replace calls strip spaces (Binary Ninja copy format), dashes (Wireshark&#x2F;hex dump format), and stray newlines. The result is a contiguous hex string like &amp;quot;e2a081&amp;quot;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;bytes.fromhex.&lt;&#x2F;strong&gt; This built-in converts a hex string directly to a bytes object. Each pair of hex digits becomes one byte. &amp;quot;e2a081&amp;quot; becomes b&amp;#39;\xe2\xa0\x81&amp;#39;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;.decode(&amp;quot;utf-8&amp;quot;).&lt;&#x2F;strong&gt; UTF-8 is a variable-width encoding. Single ASCII characters occupy one byte; characters in the range U+0080 to U+07FF use two bytes; U+0800 to U+FFFF (which covers the Braille Patterns block, U+2800 to U+28FF) use three bytes. The three bytes e2 a0 81 decode to U+2801, which is ⠁. Python&amp;#39;s str.decode handles the multi-byte sequencing automatically.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-3&quot;&gt;Step 3: Entry point and argument handling&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if __name__ == &amp;quot;__main__&amp;quot;:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if len(sys.argv) &amp;gt; 1:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        print(decode_hex(&amp;quot; &amp;quot;.join(sys.argv[1:])))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When arguments are passed on the command line, sys.argv[1:] collects everything after the script name. &amp;quot; &amp;quot;.join(...) reconstructs the hex string if the shell split it across multiple tokens (e.g. python decode_utf8.py e2 a0 81 rather than quoting it). The joined string is passed directly to decode_hex.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-4&quot;&gt;Step 4: Stdin fallback&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;else:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        for line in sys.stdin:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            line = line.strip()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            if line:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                try:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    print(decode_hex(line))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                except (ValueError, UnicodeDecodeError) as e:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                    print(f&amp;quot;Error: {e}&amp;quot;, file=sys.stderr)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When no arguments are supplied, the script reads from stdin line by line, which enables piping from other tools. line.strip() removes trailing whitespace and newlines before passing to decode_hex. Two exceptions are caught explicitly:&lt;&#x2F;p&gt;
&lt;p&gt;ValueError is raised by bytes.fromhex if the input contains non-hex characters or an odd number of digits. UnicodeDecodeError is raised by .decode(&amp;quot;utf-8&amp;quot;) if the byte sequence is not valid UTF-8. Both are reported to stderr so they do not contaminate piped output.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;utf8-hex-python&#x2F;02.webp&quot; alt=&quot;Thank you for reading it&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Thank you for reading it&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-5&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;UTF-8 encoding specification.&lt;&#x2F;strong&gt; The canonical description of UTF-8’s variable-width byte layout, including the three-byte sequences used by the Braille Patterns block, is in the IETF standard: Yergeau, F. (2003) &lt;em&gt;UTF-8, a transformation format of ISO 10646&lt;&#x2F;em&gt;. RFC 3629. Internet Engineering Task Force. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc3629&quot;&gt;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc3629&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Unicode Braille Patterns block.&lt;&#x2F;strong&gt; The code point assignments for U+2800 to U+28FF are defined in: The Unicode Consortium (2024) &lt;em&gt;The Unicode Standard, Version 16.0&lt;&#x2F;em&gt;, Chapter 22: Symbols. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.unicode.org&#x2F;versions&#x2F;Unicode16.0.0&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.unicode.org&#x2F;versions&#x2F;Unicode16.0.0&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;bytes.fromhex.&lt;&#x2F;strong&gt; Python Software Foundation (2024) &lt;em&gt;Built-in Types: bytes.fromhex&lt;&#x2F;em&gt;. Python 3 Documentation. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;stdtypes.html#bytes.fromhex&quot;&gt;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;stdtypes.html#bytes.fromhex&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;bytes.decode.&lt;&#x2F;strong&gt; Python Software Foundation (2024) &lt;em&gt;Built-in Types: bytes.decode&lt;&#x2F;em&gt;. Python 3 Documentation. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;stdtypes.html#bytes.decode&quot;&gt;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;stdtypes.html#bytes.decode&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;sys.argv and&lt;&#x2F;strong&gt; &lt;strong&gt;sys.stdin.&lt;&#x2F;strong&gt; Python Software Foundation (2024) &lt;em&gt;sys -- System-specific parameters and functions&lt;&#x2F;em&gt;. Python 3 Documentation. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;sys.html&quot;&gt;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;sys.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;UnicodeDecodeError.&lt;&#x2F;strong&gt; Python Software Foundation (2024) &lt;em&gt;Built-in Exceptions: UnicodeDecodeError&lt;&#x2F;em&gt;. Python 3 Documentation. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;exceptions.html#UnicodeDecodeError&quot;&gt;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;exceptions.html#UnicodeDecodeError&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Demangling the Symbol in Rust Binary: &lt;alloc::string::FromUtf8Error as …&gt;</title>
        <published>2026-05-15T00:00:00+00:00</published>
        <updated>2026-05-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yenwang.dev/blog/medium-demangling-rust-symbol/"/>
        <id>https://yenwang.dev/blog/medium-demangling-rust-symbol/</id>
        
        <content type="html" xml:base="https://yenwang.dev/blog/medium-demangling-rust-symbol/">&lt;blockquote&gt;
&lt;p&gt;This is the string discovered from the previous article: &lt;a href=&quot;https:&#x2F;&#x2F;yenwang.dev&#x2F;blog&#x2F;medium-akira-part-1&#x2F;&quot;&gt;Reversing Akira’s Rust Experiment: A Breakdown Part 1&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Welcome, Low Level Wizard 🧙‍♂️&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;demangling-rust-symbol&#x2F;01.webp&quot; alt=&quot;A good time to learn the binary magic spells…&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;A good time to learn the binary magic spells…&lt;&#x2F;p&gt;
&lt;h2 id=&quot;&quot;&gt;Intro&lt;&#x2F;h2&gt;
&lt;p&gt;The original article has been way too long, my goal for for my blog is to offer as many details as I could, so I won’t try to make it professional, neat and brief (I believe you can find many good threat reports without going through the messay process).&lt;&#x2F;p&gt;
&lt;p&gt;This is my journey of reverse engineering, I enjoy sharing both acdemic insights and the learning strugglings. As a beginner, I found it hard to learn assembly without practicing and guidance when I tried to reverse the samples, whether it’s for testing or at production level.&lt;&#x2F;p&gt;
&lt;p&gt;By all means, it’s imperfect and might have some mistakes, feel free to leave comments down below, I will update the content. Meanwhile, hope you enjoy the interesting things I found.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-1&quot;&gt;Mangling Scheme&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;I didn’t use any demangler in this analysis (in Binary Ninja), so it helps me to discover the native binary landscape (and it looks very scary too).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;First, we can see the raw symbol is using the &lt;strong&gt;legacy Rust v0 mangling scheme&lt;&#x2F;strong&gt; (pre-v0, actually, the $ encoding). Demangled, it reads:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;lt;alloc::string::FromUtf8Error as core::fmt::Debug&amp;gt;::fmt::h510500198db9042f
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Breaking it apart:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Component Meaning alloc::string::FromUtf8Error The type this impl is for as core::fmt::Debug The trait being implemented ::fmt The specific trait method h510500198db9042f rustc hash (disambiguator for monomorphisation &#x2F; codegen unit)&lt;&#x2F;p&gt;
&lt;p&gt;This is a &lt;strong&gt;trait method symbol&lt;&#x2F;strong&gt;, not a free function. The angle-bracket form &lt;Type as Trait&gt;::method is the standard Rust way to name a trait impl&amp;#39;s method.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-2&quot;&gt;Why Does debug Appear in a Release Build?&lt;&#x2F;h2&gt;
&lt;p&gt;This is the key misconception to correct: &lt;strong&gt;fmt::Debug has nothing to do with debug vs release compilation mode.&lt;&#x2F;strong&gt; The word &amp;quot;debug&amp;quot; here refers to the std::fmt::Debug &lt;em&gt;formatting trait&lt;&#x2F;em&gt;, which controls how a type prints when you use the {:?} format specifier. It is entirely orthogonal to rustc&amp;#39;s optimisation profile.&lt;&#x2F;p&gt;
&lt;p&gt;The trait is defined in core::fmt:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub trait Debug {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    fn fmt(&amp;amp;self, f: &amp;amp;mut Formatter&amp;lt;&amp;#39;_&amp;gt;) -&amp;gt; Result;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;alloc::string::FromUtf8Error derives or implements this trait so it can be pretty-printed with {:?}. The stdlib implementation lives in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;master&#x2F;library&#x2F;alloc&#x2F;src&#x2F;string.rs&quot;&gt;library&#x2F;alloc&#x2F;src&#x2F;string.rs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The derived impl for FromUtf8Error looks roughly like this (simplified):&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#[derive(Debug)]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub struct FromUtf8Error {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    bytes: Vec&amp;lt;u8&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    error: Utf8Error,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;#[derive(Debug)] causes rustc to auto-generate a fmt method. That generated method is what this symbol represents.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-3&quot;&gt;Why Is This Symbol Present in a Stripped Release Binary?&lt;&#x2F;h2&gt;
&lt;p&gt;This is worth understanding carefully for RE purposes.&lt;&#x2F;p&gt;
&lt;p&gt;In a release build with strip = true, &lt;strong&gt;named symbols are removed from the symbol table&lt;&#x2F;strong&gt;, but the &lt;strong&gt;code still exists&lt;&#x2F;strong&gt; if it is reachable. The presence of this string suggests one of:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The binary is not fully stripped&lt;&#x2F;strong&gt; (common with Rust: strip = &amp;quot;symbols&amp;quot; removes .symtab but may leave strings in .rodata or panic metadata).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;This is a panic string &#x2F; type name&lt;&#x2F;strong&gt;, not a symbol table entry. Rust embeds type names as string literals for panic formatting. If the code path does unwrap() or expect() on a Result&amp;lt;_, FromUtf8Error&amp;gt;, the type name can be embedded in the binary as part of the panic payload even in release mode.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;It survived as a function name in&lt;&#x2F;strong&gt;**.dynstr** if the binary is dynamically linked (unlikely for Rust malware but possible).&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The $LT$ &#x2F; $GT$ &#x2F; $u20$ encoding is the &lt;strong&gt;legacy mangling scheme&lt;&#x2F;strong&gt; (used before the v0 scheme introduced in Rust 1.37). If you are seeing this exact encoding rather than the newer _R prefix scheme, the binary was compiled with an older toolchain or with symbol-mangling-version = &amp;quot;legacy&amp;quot; in Cargo config.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-4&quot;&gt;What Does This Tell You About the Binary?&lt;&#x2F;h2&gt;
&lt;p&gt;From a malware RE perspective:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;String::from_utf8 is being used somewhere in the code.&lt;&#x2F;strong&gt; The Debug impl for FromUtf8Error is only monomorphised and retained if the error type is actually used, which means either it is returned as part of a Result, or it is displayed&#x2F;printed via {:?}.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The error path is reachable.&lt;&#x2F;strong&gt; If the compiler dead-code-eliminated the fmt call entirely, this symbol would not appear even as a string.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Possible use patterns:&lt;&#x2F;strong&gt; parsing raw bytes into strings (network data, file content, C interop buffers) and either propagating or logging the error. This is common in malware doing C2 protocol parsing, config decryption output handling, or filename&#x2F;path reconstruction.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;-5&quot;&gt;Quick Reference: Source Location&lt;&#x2F;h2&gt;
&lt;p&gt;The actual Debug impl is in the Rust stdlib:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;library&#x2F;alloc&#x2F;src&#x2F;string.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Look for:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;impl fmt::Debug for FromUtf8Error { ... }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or, in older versions, it is derived via #[derive(Debug)] on the struct definition itself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-6&quot;&gt;&lt;strong&gt;References&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Klabnik, S., Nichols, C. and The Rust Project Developers (n.d.) &lt;em&gt;FromUtf8Error in std::string&lt;&#x2F;em&gt;. The Rust Programming Language. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;string&#x2F;struct.FromUtf8Error.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;string&#x2F;struct.FromUtf8Error.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Klabnik, S., Nichols, C. and The Rust Project Developers(n.d.) &lt;em&gt;Debug in core::fmt&lt;&#x2F;em&gt;. The Rust Programming Language. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;core&#x2F;fmt&#x2F;trait.Debug.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;core&#x2F;fmt&#x2F;trait.Debug.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;rust-lang (2017) &lt;em&gt;Non well-formed symbol mangling and bad symbol tracking issue&lt;&#x2F;em&gt;, GitHub issue #46552. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;46552&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;46552&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Interesting instructions in Rust binary: lock cmpxchg</title>
        <published>2026-05-14T00:00:00+00:00</published>
        <updated>2026-05-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yenwang.dev/blog/medium-lock-cmpxchg/"/>
        <id>https://yenwang.dev/blog/medium-lock-cmpxchg/</id>
        
        <content type="html" xml:base="https://yenwang.dev/blog/medium-lock-cmpxchg/">&lt;p&gt;The story starts when I reverse engineer a “normal” Rust binary (release build, MCVC x64 PE), also inspected a tab “Tags” in Binary Ninja.&lt;&#x2F;p&gt;
&lt;p&gt;It says…&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;lock-cmpxchg&#x2F;01.webp&quot; alt=&quot;Tabs — Unresolved Indirect Control Flow (13)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Tabs — Unresolved Indirect Control Flow (13)&lt;&#x2F;p&gt;
&lt;p&gt;As we may (or not) know, function boundary identification is notoriously hard in Rust binaries (I think it’s still is in C++ binaries too). Not surprised I saw them. For the Reverse Engineering workflow, &lt;strong&gt;the unresolved indirect jumps&lt;&#x2F;strong&gt; can be the most interesting targets. Each one is a potential trait object dispatch site. To resolve them:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to the jmp rax site&lt;&#x2F;li&gt;
&lt;li&gt;Look backwards for where rax was loaded: typically mov rax, [some_ptr + offset] where some_ptr points into a vtable&lt;&#x2F;li&gt;
&lt;li&gt;Find the vtable in .data.rel.ro and read the function pointer array&lt;&#x2F;li&gt;
&lt;li&gt;Cross-reference with panic strings or symbol hashes to identify the trait&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;But what attracts me and grabbed my attention is the unusal instruction lock cmpxchg (see the figure below)…&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;lock-cmpxchg&#x2F;02.webp&quot; alt=&quot;Ummm… interesting.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Ummm… interesting.&lt;&#x2F;p&gt;
&lt;p&gt;You can have a look at some disucssion and resources, like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;59020823&#x2F;how-does-lock-cmpxchg-work-in-assembly&quot;&gt;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;59020823&#x2F;how-does-lock-cmpxchg-work-in-assembly&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@m-ibrahim.research&#x2F;atomic-moves-lock-free-programming-straight-from-the-cpu-446f0ffe83c6&quot;&gt;https:&#x2F;&#x2F;medium.com&#x2F;@m-ibrahim.research&#x2F;atomic-moves-lock-free-programming-straight-from-the-cpu-446f0ffe83c6&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;heather.cs.ucdavis.edu&#x2F;matloff&#x2F;public_html&#x2F;50&#x2F;PLN&#x2F;lock.pdf&quot;&gt;https:&#x2F;&#x2F;heather.cs.ucdavis.edu&#x2F;matloff&#x2F;public_html&#x2F;50&#x2F;PLN&#x2F;lock.pdf&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As described in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;heather.cs.ucdavis.edu&#x2F;matloff&#x2F;public_html&#x2F;50&#x2F;PLN&#x2F;lock.pdf&quot;&gt;this course material&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Operation of the ‘cmpxchg’ instruction is described (on 3 pages) in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;www&#x2F;us&#x2F;en&#x2F;developer&#x2F;articles&#x2F;technical&#x2F;intel-sdm.html&quot;&gt;Volume 2A&lt;&#x2F;a&gt; of Intel Manual.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;First thing first, this is nothing to do with the source code. It comes entirely from the stdlib’s futex::Once::call, triggered indirectly by println! initialising the stdout singleton on its first call.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;use std::hint::black_box;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;trait Speak {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    fn say_hello(&amp;amp;self);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;struct Human;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;struct Robot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;impl Speak for Human {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    fn say_hello(&amp;amp;self) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        println!(&amp;quot;Hi!&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;impl Speak for Robot {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    fn say_hello(&amp;amp;self) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        println!(&amp;quot;Beep boop!&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#[inline(never)]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn greet(speaker: &amp;amp;dyn Speak) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    speaker.say_hello();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn main() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let h = Human;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let r = Robot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    greet(&amp;amp;h);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    greet(&amp;amp;r);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    black_box(&amp;amp;h);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    black_box(&amp;amp;r);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;&quot;&gt;Lock-Free Programming&lt;&#x2F;h2&gt;
&lt;p&gt;Let’s have a quick recap before discussing this instruction…&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-1&quot;&gt;The problem it solves&lt;&#x2F;h3&gt;
&lt;p&gt;In concurrent programming, multiple threads share data. The naive solution is a mutex:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let data = Mutex::new(0u64);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Thread A:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let mut guard = data.lock().unwrap();  &#x2F;&#x2F; blocks if Thread B holds it
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;*guard += 1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; lock released on drop
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Mutexes work, but they have costs:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Blocking:&lt;&#x2F;strong&gt; a thread that cannot acquire the lock goes to sleep, requiring OS scheduler involvement (syscall, context switch)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Priority inversion:&lt;&#x2F;strong&gt; a low-priority thread holding the lock blocks a high-priority thread&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Deadlock:&lt;&#x2F;strong&gt; two threads each waiting for a lock the other holds&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Convoying:&lt;&#x2F;strong&gt; threads queue behind the lock holder even if the critical section is trivially short&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Lock-free programming eliminates the lock entirely. Instead of serialising access through mutual exclusion, it uses &lt;strong&gt;hardware atomic operations&lt;&#x2F;strong&gt; that the CPU guarantees are indivisible.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-2&quot;&gt;The hardware foundation: atomicity&lt;&#x2F;h3&gt;
&lt;p&gt;On x86–64, certain operations are guaranteed atomic by the ISA:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Aligned reads and writes up to 64 bits are naturally atomic (no tearing)&lt;&#x2F;li&gt;
&lt;li&gt;The lock prefix makes a read-modify-write sequence atomic: lock add, lock xchg, lock cmpxchg&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;“Atomic” means: no other core can observe the memory location in an intermediate state. The operation either has happened completely or not at all, from every core’s perspective.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;-3&quot;&gt;The key primitive: Compare-and-Swap (CAS)&lt;&#x2F;h3&gt;
&lt;p&gt;Everything in lock-free programming is built on CAS. In Rust:&lt;&#x2F;p&gt;
&lt;p&gt;rust&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;use std::sync::atomic::{AtomicU64, Ordering};
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let counter = AtomicU64::new(0);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; CAS: &amp;quot;if counter == expected, set it to desired, return Ok(desired)&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F;      &amp;quot;if counter != expected, return Err(actual_value)&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;counter.compare_exchange(expected, desired, success_ord, fail_ord);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The canonical lock-free update pattern:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;loop {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let current = counter.load(Ordering::Acquire);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let next = current + 1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    match counter.compare_exchange(current, next, Ordering::Release, Ordering::Relaxed) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        Ok(_) =&amp;gt; break,       &#x2F;&#x2F; CAS succeeded, we won the race
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        Err(_) =&amp;gt; continue,   &#x2F;&#x2F; another thread changed it first, retry
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is exactly the loop you saw earlier in sub_140003b10. No mutex, no OS call, no sleeping.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-4&quot;&gt;What lock cmpxchg is&lt;&#x2F;h2&gt;
&lt;p&gt;cmpxchg = &lt;strong&gt;Compare and Exchange&lt;&#x2F;strong&gt;. It is the x86 atomic compare-and-swap primitive.&lt;&#x2F;p&gt;
&lt;p&gt;The semantics in one line:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If&lt;&#x2F;em&gt; &lt;em&gt;[mem] equals&lt;&#x2F;em&gt; &lt;em&gt;rax, write&lt;&#x2F;em&gt; &lt;em&gt;rdx into&lt;&#x2F;em&gt; &lt;em&gt;[mem] and set ZF=1. Otherwise load&lt;&#x2F;em&gt; &lt;em&gt;[mem] into&lt;&#x2F;em&gt; &lt;em&gt;rax and set ZF=0. Atomically.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The lock prefix makes the entire operation a single atomic bus transaction: it asserts the cache-line lock on the target memory address for the duration, preventing any other core from reading or writing that address between the compare and the exchange.&lt;&#x2F;p&gt;
&lt;p&gt;Without lock, on a multicore system, another thread could modify [mem] between the compare and the exchange, creating a race. With lock, the entire read-modify-write is indivisible.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-5&quot;&gt;The full instruction here&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lock cmpxchg qword [rel data_140020208], rdx
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;-6&quot;&gt;U&lt;strong&gt;npacked:&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Component Meaning lock Atomically hold the cache line cmpxchg Compare-and-swap qword 64-bit operation [rel data_140020208] RIP-relative address: a global variable in .data&#x2F;.bss rdx The new value to write if the compare succeeds (implicit) rax The expected current value (loaded earlier)&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;So the operation is: &lt;strong&gt;if&lt;&#x2F;strong&gt; *&lt;strong&gt;data_140020208 == rax, then store&lt;&#x2F;strong&gt; &lt;strong&gt;rdx there atomically.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-7&quot;&gt;What the full loop is doing&lt;&#x2F;h2&gt;
&lt;p&gt;Looking at both images together, the HLIL makes it clear. Let me annotate the disassembly against the HLIL (High Level IL in Binary Ninja):&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; ── Load current value of the global ──────────────────────────
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b50   mov rax, qword [rel data_140020208]   ; rax_2 = *global
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; ── Spin loop top ─────────────────────────────────────────────
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b60   cmp rax, 0xffffffffffffffff           ; if rax_2 == -1 (sentinel)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b64   je  0x140003bd3                       ;   → call sub_140016790 (noreturn: panic&#x2F;abort)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b66   lea rdx, [rax+0x1]                    ; rdx = rax_2 + 1  (desired new value)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b6a   lock cmpxchg qword [rel data_140020208], rdx
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            ; attempt: if *global == rax, write rdx, ZF=1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            ; else:    rax = *global (reload), ZF=0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b73   jne 0x140003b60                       ; if ZF=0 (CAS failed), retry
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; ── CAS succeeded ─────────────────────────────────────────────
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b75   mov qword [rcx], rdx                  ; *(rax_1 + 0x38) = rdx
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a &lt;strong&gt;compare-and-swap spin loop&lt;&#x2F;strong&gt;: a standard lock-free increment.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In pseudocode:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;lock-cmpxchg&#x2F;03.webp&quot; alt=&quot;HIL in Binary Ninja&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;HIL in Binary Ninja&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;lock-cmpxchg&#x2F;04.webp&quot; alt=&quot;Pseudo C in Binary Ninja&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Pseudo C in Binary Ninja&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;loop {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let current = global.load(Relaxed);   &#x2F;&#x2F; rax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if current == -1 { panic!(); }        &#x2F;&#x2F; sentinel: poisoned&#x2F;uninitialized
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let next = current + 1;               &#x2F;&#x2F; rdx
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if global.compare_exchange(current, next).is_ok() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        break;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; else: another thread changed global, reload rax and retry
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The jne after lock cmpxchg is checking the &lt;strong&gt;zero flag&lt;&#x2F;strong&gt;: if ZF=0, the CAS failed (someone else modified the global between our load and our CAS attempt), so we jump back to reload and retry.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-8&quot;&gt;What this pattern represents in Rust&lt;&#x2F;h2&gt;
&lt;p&gt;The context gives it away. Look at what happens after the loop succeeds:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b78   mov qword [rel data_140020200], rdx   ; store thread count &#x2F; handle
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b88   call AddVectoredExceptionHandler       ; VEH registration
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b8e   mov dword [rbp-0x38], 0x5000          ; StackSizeInBytes = 0x5000
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b99   call SetThreadStackGuarantee
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b9f   call GetCurrentThread
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003bac   lea rdx, [rel data_14001907a]         ; u&amp;quot;main&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The TLS access at the top (gs:0x58 → ThreadLocalStoragePointer), the CAS on a global counter, followed by AddVectoredExceptionHandler and stack guarantee setup, and the string u&amp;quot;main&amp;quot; — this is the &lt;strong&gt;Rust runtime initialisation sequence&lt;&#x2F;strong&gt;. Specifically this is std::rt::lang_start or its inner worker, which runs once per thread and uses a CAS-protected counter to track initialisation state.&lt;&#x2F;p&gt;
&lt;p&gt;The specific pattern (global atomically incremented, sentinel value -1 triggers a noreturn) is Rust&amp;#39;s &lt;strong&gt;once-cell &#x2F; lazy initialisation&lt;&#x2F;strong&gt; pattern, likely std::sync::Once or the internal lang_start_internal synchronisation that ensures the runtime (panic handler, VEH, stack guarantee) is initialised exactly once even under concurrent entry.&lt;&#x2F;p&gt;
&lt;p&gt;The HLIL renders it cleanly:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;rax_2 = data_140020208   &#x2F;&#x2F; load global
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;do {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if (rax_2 == -1) { sub_140016790(); noreturn }  &#x2F;&#x2F; sentinel check
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    rdx = rax_2 + 1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if (rax_2 == data_140020208) {                  &#x2F;&#x2F; CAS success path
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        data_140020208 = rdx
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        z_1 = true
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    } else {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        rax_2 = data_140020208                      &#x2F;&#x2F; CAS failure: reload
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        z_1 = false
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;} while (not z_1)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Binary Ninja’s HLIL has correctly identified the lock cmpxchg + jne as a do-while CAS loop and modelled the success&#x2F;failure branches.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-9&quot;&gt;Why this is a release build indicator (supporting evidence)&lt;&#x2F;h2&gt;
&lt;p&gt;A debug build would not have this pattern here in the same way. In a debug build:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The std::sync::Once or equivalent would appear as an explicit call to the unoptimised Once::call_once function, not as an inlined CAS loop&lt;&#x2F;li&gt;
&lt;li&gt;The TLS access would go through several more indirection layers with intervening stack spills&lt;&#x2F;li&gt;
&lt;li&gt;The whole initialisation sequence would be spread across multiple non-inlined call frames&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The fact that the entire CAS loop, VEH registration, stack guarantee, and thread naming is all inlined into a single flat function body is a strong optimisation signal consistent with a release build at opt-level 2 or 3.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;So, where does it come from…?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-10&quot;&gt;Where lock cmpxchg comes from in your binary&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;-11&quot;&gt;Step 1: mod.rs routes x86_64-pc-windows-msvc to futex.rs&lt;&#x2F;h3&gt;
&lt;p&gt;The dispatch table in library&#x2F;std&#x2F;src&#x2F;sys&#x2F;sync&#x2F;once&#x2F;mod.rs uses cfg_select!:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cfg_select! {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    any(
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        all(target_os = &amp;quot;windows&amp;quot;, not(target_vendor=&amp;quot;win7&amp;quot;)),  &#x2F;&#x2F; modern Windows
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        target_os = &amp;quot;linux&amp;quot;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        ...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        mod futex;   &#x2F;&#x2F; x86_64-pc-windows-msvc lands here
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    any(
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        windows,     &#x2F;&#x2F; older&#x2F;fallback Windows
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        ...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        mod queue;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;x86_64-pc-windows-msvc has target_os = &amp;quot;windows&amp;quot; and target_vendor = &amp;quot;pc&amp;quot;. Since &amp;quot;pc&amp;quot; != &amp;quot;win7&amp;quot;, the condition not(target_vendor=&amp;quot;win7&amp;quot;) is true, so the first arm matches and the binary links against futex.rs.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Reference: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;src&#x2F;std&#x2F;sys&#x2F;sync&#x2F;once&#x2F;mod.rs.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;src&#x2F;std&#x2F;sys&#x2F;sync&#x2F;once&#x2F;mod.rs.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;-12&quot;&gt;Step 2: futex.rs contains compare_exchange_weak, which compiles to lock cmpxchg&lt;&#x2F;h3&gt;
&lt;p&gt;Inside futex::Once::call(), the slow path for first initialisation:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;INCOMPLETE | POISONED =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Try to register the current thread as the one running.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let next = RUNNING + if queued { QUEUED } else { 0 };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if let Err(new) = self.state_and_queued.compare_exchange_weak(
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        state_and_queued,   &#x2F;&#x2F; expected: current state (INCOMPLETE = 3)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        next,               &#x2F;&#x2F; desired: RUNNING (= 1)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        Acquire,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        Acquire,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        state_and_queued = new;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        continue;           &#x2F;&#x2F; CAS failed, retry
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; ... run the initialiser closure
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On x86–64, LLVM lowers compare_exchange_weak on an atomic integer with Acquire ordering to lock cmpxchg. The surrounding retry loop (continue) is the spin you see in the disassembly.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Reference: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;src&#x2F;std&#x2F;sys&#x2F;sync&#x2F;once&#x2F;futex.rs.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;src&#x2F;std&#x2F;sys&#x2F;sync&#x2F;once&#x2F;futex.rs.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;-13&quot;&gt;Step 3: the call chain from your source code&lt;&#x2F;h3&gt;
&lt;p&gt;The source code has println!(&amp;quot;Hi!&amp;quot;) inside Human::say_hello. That expands to a call to std::io::_print, which calls std::io::stdout(). Inside stdout():&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; library&#x2F;std&#x2F;src&#x2F;io&#x2F;stdio.rs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;static STDOUT: OnceLock&amp;lt;ReentrantLock&amp;lt;RefCell&amp;lt;LineWriter&amp;lt;StdoutRaw&amp;gt;&amp;gt;&amp;gt;&amp;gt; = OnceLock::new();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn stdout() -&amp;gt; Stdout {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Stdout { inner: STDOUT.get_or_init(|| { ... }) }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;OnceLock::get_or_init delegates to Once::call_once, which on x86_64-pc-windows-msvc routes to futex::Once::call. On the very first call to println! in the process, the state is INCOMPLETE (= 3), so the INCOMPLETE | POISONED arm fires and executes compare_exchange_weak, producing the lock cmpxchg at 140003b6a.&lt;&#x2F;p&gt;
&lt;p&gt;Reference: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;src&#x2F;std&#x2F;io&#x2F;stdio.rs.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;src&#x2F;std&#x2F;io&#x2F;stdio.rs.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-14&quot;&gt;What the assembly maps to?&lt;&#x2F;h3&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; rax = load of Once state (INCOMPLETE = 0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b50   mov rax, [rel data_140020208]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; cmp rax, -1 = check for POISONED sentinel
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b60   cmp rax, 0xffffffffffffffff
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b64   je  0x140003bd3            ; → panic (poisoned)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; rdx = rax + 1 = RUNNING (0 + 1 = 1)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b66   lea rdx, [rax+0x1]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; compare_exchange(INCOMPLETE, RUNNING, SeqCst)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b6a   lock cmpxchg [rel data_140020208], rdx
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; if ZF=0 (CAS failed, state changed), retry
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;140003b73   jne 0x140003b60
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This can be:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; queue.rs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let old = self.state.compare_and_swap(state, RUNNING, Ordering::SeqCst);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if old != state { state = old; continue }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The -1 &#x2F; 0xffffffffffffffff check is the POISONED sentinel (or a waiter-pointer sentinel in the queue.rs encoding; the upper bits of the AtomicUsize encode the waiter linked list head in the RUNNING state, so values larger than COMPLETE that are not a valid pointer trigger the abort path).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-15&quot;&gt;Summary of lock cmpxchg mechanics&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Before:          rax = expected_value
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                 rdx = new_value
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                 mem = some_current_value
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Case A (success): mem == rax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                 → mem becomes rdx
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                 → ZF = 1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                 → jne not taken: loop exits
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Case B (failure): mem != rax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                 → rax loaded with actual mem value
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                 → ZF = 0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                 → jne taken: loop retries with fresh rax
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The loop continues retrying until the CAS succeeds, i.e. until no other thread has modified the global between this thread’s load and its attempted store. Under low contention this succeeds on the first or second attempt. Under high contention it spins. This is the foundation of all lock-free data structures on x86.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Assembly Breakdown: x86-64 structure alignment examples</title>
        <published>2026-05-07T00:00:00+00:00</published>
        <updated>2026-05-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yenwang.dev/blog/medium-struct-alignment/"/>
        <id>https://yenwang.dev/blog/medium-struct-alignment/</id>
        
        <content type="html" xml:base="https://yenwang.dev/blog/medium-struct-alignment/">&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;struct-alignment&#x2F;01.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;&quot;&gt;x64 Structure Alignment in MSVC: What the Compiler Does to Your Structs&lt;&#x2F;h2&gt;
&lt;p&gt;When you declare a struct in C, the compiler does not simply pack the members back-to-back in memory. It inserts padding bytes to ensure each member sits at an address that is a multiple of its own size. The x64 ABI formalises this as natural alignment: a short must be 2-byte aligned, an int 4-byte aligned, a double or pointer 8-byte aligned. The struct&amp;#39;s total size is then rounded up to a multiple of its largest member&amp;#39;s alignment, so that arrays of structs remain correctly aligned at every element.&lt;&#x2F;p&gt;
&lt;p&gt;The Microsoft documentation illustrates this with four concrete examples. A struct containing only a short needs no padding at all -- two bytes, done. Add an int followed by a double followed by a short, and the picture changes: four bytes of padding appear between the int and the double to push the double to an 8-byte boundary, and six bytes of trailing padding follow the short to bring the total to 24. A struct with interleaved char, short, char, and int members accumulates internal padding in two places, wasting 4 bytes out of 12. The union example shows the other side of the coin: all members start at offset 0, the total size is simply the largest member, and smaller members leave the high bytes unused.&lt;&#x2F;p&gt;
&lt;p&gt;Two practical points follow from this.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;First, member ordering is not neutral. Declaring fields largest-to-smallest eliminates most internal padding; the interleaved-type example shrinks from 12 bytes to 8 if reordered.&lt;&#x2F;li&gt;
&lt;li&gt;Second, Rust’s default #[repr(Rust)] layout allows the compiler to reorder fields freely to minimise padding, so a Rust struct in a binary will not necessarily match the source declaration order. Only #[repr(C)] guarantees the C-compatible layout described above, which matters when reconstructing types during reverse engineering.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The four examples demonstrate the two core alignment rules: each member sits at an offset that is a multiple of its own size, and the total struct size is padded to a multiple of the struct’s alignment (which equals the largest member’s alignment).&lt;&#x2F;p&gt;
&lt;p&gt;Let’s review the examples from Microdoft Doc.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Part one is based on Microsoft Doc. Check it out: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;cpp&#x2F;build&#x2F;x64-software-conventions?view=msvc-170&quot;&gt;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;cpp&#x2F;build&#x2F;x64-software-conventions?view=msvc-170&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-1&quot;&gt;Example 1: trivial, no padding&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_declspec(align(2)) struct {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    short a;      &#x2F;&#x2F; +0; size = 2 bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Total: 2 bytes, alignment: 2 bytes
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;short requires 2-byte alignment. Placed at offset 0, which satisfies that. Total size 2 is already a multiple of 2. No padding needed anywhere.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-2&quot;&gt;Example 2 : internal and trailing padding&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_declspec(align(8)) struct {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    int    a;     &#x2F;&#x2F; +0;  size = 4 bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    double b;     &#x2F;&#x2F; +8;  size = 8 bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    short  c;     &#x2F;&#x2F; +16; size = 2 bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Total: 24 bytes, alignment: 8 bytes
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;a is an int (4 bytes, 4-byte aligned) — placed at offset 0, no issue. b is a double (8 bytes, &lt;strong&gt;8-byte aligned&lt;&#x2F;strong&gt;) — the next available offset after a is 4, which is not a multiple of 8, so 4 bytes of padding are inserted at offsets 4–7 to push b to offset 8. c is a short (2 bytes, 2-byte aligned) — placed at offset 16, aligned correctly. That leaves offsets 18–23 as trailing padding: the struct&amp;#39;s alignment is 8 (driven by double b), so total size must be a multiple of 8. 18 rounded up to the next multiple of 8 = 24. Six bytes of trailing padding are added.&lt;&#x2F;p&gt;
&lt;p&gt;The trailing padding matters when the struct is used in an array: arr[1] must also start at an 8-byte boundary, which requires each element to be 24 bytes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-3&quot;&gt;Example 3: interleaved padding from mixed types&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_declspec(align(4)) struct {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    char  a;      &#x2F;&#x2F; +0;  size = 1 byte
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    short b;      &#x2F;&#x2F; +2;  size = 2 bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    char  c;      &#x2F;&#x2F; +4;  size = 1 byte
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    int   d;      &#x2F;&#x2F; +8;  size = 4 bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Total: 12 bytes, alignment: 4 bytes
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Offset │ 0    1    2    3
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;───────┼────────────────────
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;       │  a   [p]   b    b
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Offset │ 4    5    6    7
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;───────┼────────────────────
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;       │  c   [p]  [p]  [p]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Offset │ 8    9   10   11
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;───────┼────────────────────
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;       │  d    d    d    d
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;a is a char at offset 0 : 1-byte aligned, no padding needed.&lt;&#x2F;li&gt;
&lt;li&gt;b is a short : requires 2-byte alignment; offset 1 is odd, so 1 byte of padding inserted before b, placing it at offset 2.&lt;&#x2F;li&gt;
&lt;li&gt;c is a char at offset 4 .&lt;&#x2F;li&gt;
&lt;li&gt;d is an int : requires 4-byte alignment; the next offset after c is 5, which is not a multiple of 4, so 3 bytes of padding inserted before d, placing it at offset 8.&lt;&#x2F;li&gt;
&lt;li&gt;Total size is 12, which is a multiple of 4 (the struct&amp;#39;s alignment). No trailing padding needed.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This example illustrates why member ordering matters: reordering to {int d; short b; char a; char c;} would produce a 8-byte struct with no internal padding at all.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-4&quot;&gt;Example 4: union, all members at offset 0&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_declspec(align(8)) union {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    char *p;      &#x2F;&#x2F; +0; size = 8 bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    short  s;     &#x2F;&#x2F; +0; size = 2 bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    long   l;     &#x2F;&#x2F; +0; size = 4 bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Total: 8 bytes, alignment: 8 bytes
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Offset │ 0    1    2    3    4    5    6    7
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;───────┼────────────────────────────────────
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  p    │  p    p    p    p    p    p    p    p   (all 8 bytes)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  s    │  s    s   [p]  [p]  [p]  [p]  [p]  [p] (2 bytes + 6 padding)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  l    │  l    l    l    l   [p]  [p]  [p]  [p] (4 bytes + 4 padding)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All union members share offset 0 — that is the defining property of a union. The total size is determined by the largest member (char* = 8 bytes on x64), and the alignment is determined by the largest alignment requirement, also 8 (char* is a pointer = quadword). s and l occupy only the low bytes of the 8-byte slot; the remainder is unused padding within the union&amp;#39;s footprint.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-5&quot;&gt;RE Relevance&lt;&#x2F;h2&gt;
&lt;p&gt;Struct layout rules are directly relevant to binary analysis of Rust code. Rust’s compiler (via LLVM) applies the same natural alignment rules by default for #[repr(C)] types. For #[repr(Rust)] types, the compiler is free to reorder fields to minimise padding — which means a Rust struct in a malware sample will not necessarily have the same field ordering as the declared source, complicating type reconstruction in Binary Ninja or IDA. Recognising padding bytes versus data bytes is essential when recovering struct layouts from MLIL&#x2F;HLIL.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Microsoft Corporation (2023) x64 ABI conventions. Microsoft Learn. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;cpp&#x2F;build&#x2F;x64-software-conventions&quot;&gt;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;cpp&#x2F;build&#x2F;x64-software-conventions&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Reversing Akira&#x27;s Rust Experiment: A Breakdown, Part 1</title>
        <published>2026-05-06T00:00:00+00:00</published>
        <updated>2026-05-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yenwang.dev/blog/medium-akira-part-1/"/>
        <id>https://yenwang.dev/blog/medium-akira-part-1/</id>
        
        <content type="html" xml:base="https://yenwang.dev/blog/medium-akira-part-1/">&lt;blockquote&gt;
&lt;p&gt;This post is a breakdown and commentary on &lt;strong&gt;Ben Herzog’s Check Point Research&lt;&#x2F;strong&gt; (December 2024), &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;Inside Akira Ransomware’s Rust Experiment&lt;&#x2F;a&gt; &amp;amp; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.virusbulletin.com&#x2F;conference&#x2F;vb2025&#x2F;abstracts&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;his talk in 2025&lt;&#x2F;a&gt;. All technical claims are grounded in the linked primary sources below. Combined with &lt;strong&gt;Cindy Xiao&lt;&#x2F;strong&gt;’s amazing reports&#x2F;talks in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;rust-reversing-workshop-recon-2024&quot;&gt;2024&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;reconstructing-rust-types-talk-re-verse-2025&quot;&gt;2025&lt;&#x2F;a&gt; (about reconstructing types and more).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The technique of recovering the rustc compiler version from embedded&lt;br &#x2F;&gt;
&#x2F;rustc&#x2F;&lt;commit&gt;&#x2F;strings and recovering third-party crates from&lt;br &#x2F;&gt;
registry&#x2F;src&#x2F;index.crates.io-…&#x2F;&lt;crate&gt;-&lt;ver&gt;&#x2F; panic paths was&lt;br &#x2F;&gt;
independently developed by Juan Andrés Guerrero-Saade and Nicole Fishbein&lt;br &#x2F;&gt;
in their Proj-0xA11c toolset, presented at Recon 2024:&lt;br &#x2F;&gt;
&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;juanandresgs&#x2F;Proj-0xA11c&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;juanandresgs&#x2F;Proj-0xA11c&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;There will be a part 2 to break down the rest. I separate them so this article won’t be &lt;strong&gt;&lt;em&gt;_&lt;em&gt;too_long&lt;&#x2F;em&gt;&lt;&#x2F;em&gt;_&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;01.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For this analysis, Binary Ninja (v5.3.9434) is used without enabling any plugins. &lt;strong&gt;Binary:&lt;&#x2F;strong&gt; akirav2.elf , SHA25:60ee1d284ed663073872012c7bde7fac5ca1121403f1a5d2d5411317df282796c.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;&quot;&gt;&lt;strong&gt;A note on source references&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The commit hash is updated on 10th Jun 2026. &lt;strong&gt;&lt;em&gt;79e9716c…&lt;&#x2F;em&gt; is rustc 1.74.0 stable&lt;&#x2F;strong&gt; (released 2023-11-16). Akirav2’s &lt;em&gt;.rodata&lt;&#x2F;em&gt; contains std panic-path strings of the form &lt;em&gt;&#x2F;rustc&#x2F;&lt;commit&gt;&#x2F;library&#x2F;...&lt;&#x2F;em&gt;.&lt;strong&gt;&lt;em&gt;db034cee…&lt;&#x2F;em&gt; is a&lt;&#x2F;strong&gt; &lt;strong&gt;&lt;em&gt;master&lt;&#x2F;em&gt;&#x2F;nightly commit from ~1 year later,&lt;&#x2F;strong&gt; which I was mistakenly pinned in the article. If you see any links related to &lt;em&gt;&lt;strong&gt;db034cee… ,&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; &lt;em&gt;please consider it was based on previosu wrong assumption but it works to some degree.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The Rust standard library is under active development and files move between releases. Source links in this post are pinned to commit 79e9716c980570bfd1f666e3b16ac583f0168962 — &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;releases.rs&#x2F;docs&#x2F;1.74.0&#x2F;&quot;&gt;&lt;strong&gt;rustc 1.74.0 stable&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;, the toolchain that compiled this Akira v2 binary. This is recoverable directly from the binary: its .rodata embeds &#x2F;rustc&#x2F;79e9716c…&#x2F;library&#x2F;... panic paths, and it is corroborated by version-pinned FunctionID analysis resolving the sample to 1.74.0.&lt;&#x2F;p&gt;
&lt;p&gt;This matters because stdlib code is inlined directly into the final binary at compile time: what you see in the disassembly reflects the stdlib at that specific commit, not the current main branch. Following a newer version of the same file risks misidentifying inlined boilerplate or drawing incorrect conclusions about what the malware authors wrote versus what the compiler contributed.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If a pinned link returns a 404, the file has likely been relocated. Navigate to the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;tree&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&quot;&gt;repository root&lt;&#x2F;a&gt; and search by filename.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-1&quot;&gt;Table of Contents&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Background&lt;&#x2F;li&gt;
&lt;li&gt;Summary for Part 1&lt;&#x2F;li&gt;
&lt;li&gt;VM Setup for malware analysis&lt;&#x2F;li&gt;
&lt;li&gt;Binja plugins for Rust binary&lt;&#x2F;li&gt;
&lt;li&gt;Is this a Rust binary?&lt;&#x2F;li&gt;
&lt;li&gt;(Updated) It’s a release build&lt;&#x2F;li&gt;
&lt;li&gt;.rodata strings are extraordinarily revealing&lt;&#x2F;li&gt;
&lt;li&gt;The Control Flow Map&lt;&#x2F;li&gt;
&lt;li&gt;Section 1: How args().collect() Works&lt;&#x2F;li&gt;
&lt;li&gt;Further Reading&lt;&#x2F;li&gt;
&lt;li&gt;References&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;-2&quot;&gt;Background&lt;&#x2F;h2&gt;
&lt;p&gt;In early 2024, Akira ransomware-as-a-service operators circulated a new variant, internally called “Akira v2”, written in Rust and aimed specifically at ESXi bare-metal hypervisor servers. Cisco Talos first documented the variant in their &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.talosintelligence.com&#x2F;akira-ransomware-continues-to-evolve&#x2F;&quot;&gt;ongoing Akira evolution tracking&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;Check Point Research&lt;&#x2F;a&gt; published a detailed assembly-level analysis in December 2024. Unlike most ransomware reports that answer only targeted questions (“what encryption is used?”, “what extensions are targeted?”).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.talosintelligence.com&#x2F;akira-ransomware-continues-to-evolve&#x2F;&quot;&gt;Talos&lt;&#x2F;a&gt; documented Akira v2 from a March 2024 incident response engagement, where they observed the Rust ESXi variant deployed alongside a Windows encryptor called Megazord. The v2 sample they analysed is version 2024.1.30 and uses rust-crypto 0.2.36 for encryption, replacing the Crypto++ library used in the original C++ variant. Here rust-crypto supplies the SOSEMANUK symmetric stream cipher (not ChaCha20 or AES); the asymmetric key wrapping is X25519 via curve25519-dalek 4.1.1. See the Part 3 correction note for the full pipeline.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-3&quot;&gt;Summary for Part 1&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Because this article and following ones in the series will be &lt;strong&gt;fairly_long&lt;&#x2F;strong&gt;, so I put the summary before the in-depth breakdown.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-4&quot;&gt;Layer 1: Crate&#x2F;Dependency Recovery via .rodata Panic Strings&lt;&#x2F;h2&gt;
&lt;p&gt;Every entry here was recovered purely from static string analysis, no execution, no DWARF.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Recovery mechanism:&lt;&#x2F;strong&gt; Cargo embeds full filesystem paths into panic messages at compile time. Format is always:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;&amp;lt;crate-name&amp;gt;-&amp;lt;version&amp;gt;&#x2F;src&#x2F;&amp;lt;file&amp;gt;.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;02.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;03.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;In conclusion, Indicatif version is &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;tree&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&quot;&gt;0.17.7&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Updated 17 May 2026:&lt;&#x2F;strong&gt; I’m still writing the analysis part 2 at the moment, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;Indicatif&lt;&#x2F;a&gt; version has actually found but I was drown in the piles of strings, so accidentally ignored it.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Internal workspace crates&lt;&#x2F;strong&gt; (not on crates.io, custom):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;04.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Developer platform also recoverable: WSL on Windows, username user, consistent registry hash 6f17d22bba15001f across all paths confirming single build environment.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Struct names leaked via aho-corasick SIMD backends&lt;&#x2F;strong&gt; (panic strings referencing internal type names):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;SlimSSSE3 : 128-bit SSSE3 Teddy searcher&lt;&#x2F;li&gt;
&lt;li&gt;SlimAVX2: slim 256-bit AVX2 variant&lt;&#x2F;li&gt;
&lt;li&gt;FatAVX2 : fat 256-bit AVX2 Teddy searcher&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These tell you the SIMD capability branch taken at runtime even without executing.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-5&quot;&gt;Layer 2: Internal Module Map (recovered from panic strings + format literals)&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;akiranew&#x2F;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;├── src&#x2F;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;│   ├── main.rs        — CLI parsing, fork&#x2F;stopvm, interactive prompt
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;│   ├── lock.rs        — encryption logic (akira_v2::lock namespace confirmed)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;│   ├── path_finder.rs — filesystem enumeration (akira_v2::path_finder confirmed)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;│   └── prng.rs        — custom PRNG
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;└── Cargo.toml
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    (workspace member: akira_utils)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Top-level call chain confirmed: main → default_action → lock → lock_closure&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-6&quot;&gt;Layer 3: Type Layout Recovery via args().collect() Unwrapping Chain&lt;&#x2F;h2&gt;
&lt;p&gt;This is another type-unwrapping chain showing how a single source call maps to assembly. The document traces five layers:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;05.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;IntoIter&lt;OsString&gt; concrete layout on x64 Linux (32 bytes):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;06.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;phantom (PhantomData) and alloc (Global) are ZSTs: zero bytes in binary.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;OsString on Linux (24 bytes, stride value for heap iteration):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;07.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;RE implication directly stated in document:&lt;&#x2F;strong&gt; the confusing pointer arithmetic in the binary is stride-8 (reading argv[]) then stride-24 (OsString iteration). Knowing those two values makes the assembly readable.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Linux vs Windows&lt;&#x2F;strong&gt; &lt;strong&gt;OsString stride difference&lt;&#x2F;strong&gt; (relevant if you ever need cross-platform comparison in the paper):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Linux: OsString = Vec&lt;u8&gt; internally = 24 bytes&lt;&#x2F;li&gt;
&lt;li&gt;Windows: OsString = Wtf8Buf = 32 bytes (ptr, len, cap, is_known_utf8 bool padded to 8 bytes)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;-7&quot;&gt;What‘s still uncertain for Part 1&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;rust-crypto 0.2.36 comes from the Talos report reference, not from .rodata recovery in this document.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;tree&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&quot;&gt;indicatif&lt;&#x2F;a&gt; version is not discovered (in Part 1), only confirmed by format strings, not a panic path.&lt;&#x2F;li&gt;
&lt;li&gt;Source links in this post are pinned to commit 79e9716c980570bfd1f666e3b16ac583f0168962 — &lt;strong&gt;rustc 1.74.0 stable&lt;&#x2F;strong&gt;, the toolchain that compiled this Akira v2 binary. This is recoverable directly from the binary: its .rodata embeds &#x2F;rustc&#x2F;79e9716c…&#x2F;library&#x2F;... panic paths, and it is corroborated by version-pinned FunctionID analysis resolving the sample to 1.74.0 &lt;strong&gt;(&lt;em&gt;Updated on 10th Jun 2026&lt;&#x2F;em&gt;)&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;unicode-width version is unconfirmed (the path is truncated to unicode-wi...&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There will be more to be discovered in Part 2…&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-8&quot;&gt;VM setup for malware analysis&lt;&#x2F;h2&gt;
&lt;p&gt;To ensure this is an &lt;strong&gt;air-gapped VM&lt;&#x2F;strong&gt; (Windows 10)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-9&quot;&gt;Step 1: VM network adapter (most important)&lt;&#x2F;h3&gt;
&lt;p&gt;In whatever hypervisor you are using:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;VMware Workstation&#x2F;Player:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;VM Settings &amp;gt; Network Adapter &amp;gt; set to &lt;strong&gt;“Host-only”&lt;&#x2F;strong&gt; first, then ideally &lt;strong&gt;“No network connection”&lt;&#x2F;strong&gt; (disconnect entirely)&lt;&#x2F;li&gt;
&lt;li&gt;Tick &lt;strong&gt;“Connected”&lt;&#x2F;strong&gt; checkbox OFF&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;VirtualBox:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Settings &amp;gt; Network &amp;gt; Adapter 1 &amp;gt; untick &lt;strong&gt;“Enable Network Adapter”&lt;&#x2F;strong&gt; entirely&lt;&#x2F;li&gt;
&lt;li&gt;Do this for all adapters (check Adapter 2, 3, 4 tabs too)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Hyper-V:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;VM Settings &amp;gt; Network Adapter &amp;gt; set to &lt;strong&gt;“Not Connected”&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;-10&quot;&gt;Step 2: Disable network inside the guest OS&lt;&#x2F;h3&gt;
&lt;p&gt;Even with the adapter disconnected at hypervisor level, do this inside the VM as defence in depth:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;# In PowerShell as Administrator
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Disable-NetAdapter -Name &amp;quot;*&amp;quot; -Confirm:$false
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or via GUI: Device Manager &amp;gt; Network Adapters &amp;gt; right-click each &amp;gt; Disable.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-11&quot;&gt;Step 3: Shared folders and clipboard&lt;&#x2F;h3&gt;
&lt;p&gt;These are your biggest risk vectors for accidental data movement:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;VMware:&lt;&#x2F;strong&gt; VM Settings &amp;gt; Options &amp;gt; Shared Folders &amp;gt; &lt;strong&gt;Disabled&lt;&#x2F;strong&gt;; Guest Isolation &amp;gt; untick both clipboard and drag-and-drop&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;VirtualBox:&lt;&#x2F;strong&gt; Devices &amp;gt; Shared Folders &amp;gt; remove all; Devices &amp;gt; Shared Clipboard &amp;gt; &lt;strong&gt;Disabled&lt;&#x2F;strong&gt;; Devices &amp;gt; Drag and Drop &amp;gt; &lt;strong&gt;Disabled&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-12&quot;&gt;Step 4: Snapshot before touching the sample&lt;&#x2F;h3&gt;
&lt;p&gt;Take a clean snapshot &lt;strong&gt;now&lt;&#x2F;strong&gt;, before the sample is on the VM, labelled something like clean-pre-analysis. This is your recovery point if anything goes wrong.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;# VMware CLI alternative
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;vmrun snapshot &amp;quot;path&#x2F;to&#x2F;vm.vmx&amp;quot; &amp;quot;clean-pre-analysis&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;-13&quot;&gt;Step 5: Transfer the sample safely&lt;&#x2F;h3&gt;
&lt;p&gt;Since shared folders should be off, transfer via:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ISO mounting:&lt;&#x2F;strong&gt; package the sample into an ISO on the host, mount it read-only in the VM. The sample cannot write back through a mounted ISO.&lt;&#x2F;li&gt;
&lt;li&gt;Password-protected zip (password: infected is the malware research convention) mounted or copied in, then extracted inside the VM only.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Do &lt;strong&gt;not&lt;&#x2F;strong&gt; re-enable shared folders or clipboard to transfer the file.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;-14&quot;&gt;Step 6: Strip execute permissions immediately on arrival&lt;&#x2F;h3&gt;
&lt;p&gt;Once inside the VM, in PowerShell:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;# Confirm it is an ELF, not a PE - it should not execute on Windows anyway
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;# but strip any accidental execute association
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$file = &amp;quot;akira_sample&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;# Rename with a safe extension to prevent accidental double-click
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Rename-Item $file &amp;quot;$file.bin&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Renaming to .bin prevents Windows from accidentally associating it with anything executable.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-15&quot;&gt;Verify your air-gap&lt;&#x2F;h3&gt;
&lt;p&gt;Inside the VM after setup:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;# Should show no active connections
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Get-NetAdapter | Select Name, Status
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;ping 8.8.8.8  # Should time out with no response
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If ping times out and adapters show as Disabled or Disconnected, you are properly isolated.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;For the analysis involving debuggers&#x2F;execution like dynamic analysis, you can also install &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.google.com&#x2F;url?sa=t&amp;amp;source=web&amp;amp;rct=j&amp;amp;opi=89978449&amp;amp;url=https:&#x2F;&#x2F;blog.christophetd.fr&#x2F;malware-analysis-lab-with-virtualbox-inetsim-and-burp&#x2F;&quot;&gt;inetSim&lt;&#x2F;a&gt;. It’s recommended to check the book &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.oreilly.com&#x2F;library&#x2F;view&#x2F;practical-malware-analysis&#x2F;9781593272906&#x2F;ch04s08.html&quot;&gt;Practical Malware Analysis&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;-16&quot;&gt;Find the sample&lt;&#x2F;h3&gt;
&lt;p&gt;It’s encouraged to check out &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.talosintelligence.com&#x2F;akira-ransomware-continues-to-evolve&#x2F;&quot;&gt;the CISCO Talos threat report&lt;&#x2F;a&gt; and discover the details and the hashes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-17&quot;&gt;Binja plugins for Rust binary &amp;amp; Some useful links&lt;&#x2F;h2&gt;
&lt;p&gt;According to Cindy Xiao’s talk&#x2F;report in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;rust-reversing-workshop-recon-2024&quot;&gt;2024&lt;&#x2F;a&gt;, when you discover some clues of Rust types, considering the links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cheats.rs&#x2F;#owned-strings&quot;&gt;https:&#x2F;&#x2F;cheats.rs&#x2F;#owned-strings&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;primitive.str.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;primitive.str.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I would certainly recommend readers to enable those plugins before the binary analysis&#x2F;REing (I didn’t enable them this time, and you can still work through it manually).&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;rust_string_slicer&quot;&gt;&lt;strong&gt;Rust String Slicer (by Cindy Xiao)&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;inspier&#x2F;BinjaRustDemangler&quot;&gt;&lt;strong&gt;Rust Demangler (by inspier)&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cxiao&#x2F;rust_type_layout_helper_bn&quot;&gt;&lt;strong&gt;Rust Type Layout Helper (by Cindy Xiao)&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;08.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;rust_string_slicer: the string problem&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The core issue it solves is that Rust strings are not null-terminated. A &amp;amp;str is a fat pointer: a (pointer, length) pair. Binary Ninja&amp;#39;s default string scanner looks for null terminators, so it systematically misses Rust string literals or truncates them at the first zero byte in the data. This means your strings view in a naive analysis of Akira v2 will be incomplete or misleading.&lt;&#x2F;p&gt;
&lt;p&gt;The plugin runs two heuristics:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Recover from readonly data&lt;&#x2F;em&gt;: it scans .rodata for cross-referenced addresses and checks whether the cross-reference site has the pattern QWORD ptr_to_string_data followed immediately by QWORD length. This recovers string slice structs laid out statically in read-only data, which is where Rust places string literals used in static contexts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Recover from code&lt;&#x2F;em&gt;: it follows cross-references into code sections and looks for the idiom:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea rax, &amp;lt;string_data_addr&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov [rsp+var.ptr], rax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;mov [rsp+var.len], &amp;lt;immediate_length&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The immediate value after the lea is the length the plugin extracts. This catches strings constructed on the stack at runtime, which the data-section heuristic cannot see.&lt;&#x2F;p&gt;
&lt;p&gt;Once recovered, the plugin defines a &amp;amp;str type in Binary Ninja&amp;#39;s type system with fields char* address and int64_t length, then annotates every recovered instance. The practical payoff is that you get cross-references to the &amp;amp;str type, which means you can enumerate all recovered string literals and their usage sites in one pass. For Akira v2, this would surface file extension lists, ransom note content, C2 strings, and any additional developer path leaks that the naive string scanner missed.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;09.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Rust symbol mangling:&lt;&#x2F;strong&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;rustc&#x2F;symbol-mangling&#x2F;v0.html&quot;&gt;&lt;strong&gt;v0 scheme&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Rust has had two mangling schemes. &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.rust-lang.org&#x2F;2025&#x2F;11&#x2F;20&#x2F;switching-to-v0-mangling-on-nightly&#x2F;&quot;&gt;The legacy scheme&lt;&#x2F;a&gt; was borrowed loosely from C++ Itanium ABI and was never formally specified. The &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;rustc&#x2F;symbol-mangling&#x2F;v0.html&quot;&gt;v0 scheme&lt;&#x2F;a&gt; was stabilised in Rust 1.37 (2019) and is now the default. It is designed to be unambiguous, fully encode Rust’s type system, and be demangleable without compiler knowledge.&lt;&#x2F;p&gt;
&lt;p&gt;Every v0 symbol begins with the prefix _R. After that, the symbol encodes a path followed optionally by a generic argument list and a disambiguator. The grammar is roughly:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_R &amp;lt;path&amp;gt; [&amp;lt;generic-args&amp;gt;] [&amp;lt;instantiating-crate&amp;gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A concrete example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_RNvCs6XXXXXXX_5akira8compress
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Breaking this down:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;_R — v0 prefix&lt;&#x2F;li&gt;
&lt;li&gt;N — indicates a nested path follows&lt;&#x2F;li&gt;
&lt;li&gt;v — item type: function (v = value namespace)&lt;&#x2F;li&gt;
&lt;li&gt;Cs6XXXXXXX_ — crate disambiguator (a hash derived from crate metadata, ensuring uniqueness across crates with the same name)&lt;&#x2F;li&gt;
&lt;li&gt;5akira — crate name, length-prefixed (5 characters: akira)&lt;&#x2F;li&gt;
&lt;li&gt;8compress — function name, length-prefixed (8 characters: compress)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For a method on a type, the path nests further:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_RNvNtCs..._5akira9Encryptor8run_pass
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Where Nt indicates a type namespace and the path encodes akira::Encryptor::run_pass.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Generic instantiations&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Monomorphised generics encode their type arguments inline. For example, a function fn process&lt;T&gt; instantiated with u64 encodes the concrete type in the symbol. This means every monomorphisation of a generic function has a distinct, demangleable symbol that tells you exactly what type it was instantiated with. In a non-stripped binary this is extremely informative for RE. In a stripped binary the symbols are gone, but if any survive (e.g. in a partially stripped binary or in exported symbols), the type information is recoverable directly from the name.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Crate disambiguator&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The hash component (Cs6XXXXXXX_) is derived from the crate&amp;#39;s metadata hash at compile time. This means two builds of the same crate with different dependencies or compiler flags will produce different disambiguators. It is not useful for version fingerprinting directly, but it does mean you can cluster functions by crate of origin even in a mixed binary, since all functions from the same crate share the same disambiguator hash.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Why enabling the Rust demangler in Binary Ninja matters&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Without demangling, every Rust symbol in the function list appears as raw _RNvCs... garbage. With demangling enabled, Binary Ninja resolves these to human-readable akira::Encryptor::run_pass style names, which gives you several concrete advantages in your workflow:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Crate boundary recovery&lt;&#x2F;strong&gt;: the demangled name includes the crate component, so you can immediately see which functions belong to akira, which belong to akira_utils, and which belong to std or third-party dependencies. This is directly relevant to your akira_utils finding: if any symbols survived stripping, demangling would confirm the crate attribution rather than requiring you to infer it from panic strings alone.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Type information from generics&lt;&#x2F;strong&gt;: monomorphised function names encode their concrete type arguments. alloc::vec::Vec&lt;u8&gt;::push tells you the vector element type. This is information you would otherwise have to recover through data flow analysis.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Call graph legibility&lt;&#x2F;strong&gt;: Binary Ninja’s call graph and cross-reference views become significantly more readable. Instead of a graph of hashes, you see a graph of akira::encrypt::* functions calling into akira_utils::*, which immediately surfaces the module structure the developer intended.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Caveat for stripped binaries&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For a fully stripped release build like Akira v2, most symbols are gone and the demangler has nothing to work with for internal functions. However it still applies to: any exported symbols (ELF binaries sometimes retain a small export table), any symbols recovered by Binary Ninja’s analysis heuristics, and any strings in .rodata that happen to contain mangled names (uncommon but not impossible). The demangler costs nothing to enable and pays off immediately if any symbols surface during analysis.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;10.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;rust_type_layout_helper_bn: the type layout problem&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This addresses a different but equally serious problem. Rust’s type system allows the compiler to reorder struct fields for optimal alignment, and enum discriminants use niche optimisation (for example, Option&amp;lt;Box&lt;T&gt;&amp;gt; has no discriminant byte because a null pointer represents None). You cannot safely assume a struct&amp;#39;s field order in the binary matches its source order. Without knowing the actual layout, every memory access in HLIL is ambiguous.&lt;&#x2F;p&gt;
&lt;p&gt;The plugin works by ingesting the output of rustc&amp;#39;s -Zprint-type-sizes flag, which emits the exact field offsets, sizes, alignment padding, and enum variant layouts for every type in a compilation. The plugin parses that output and imports it directly into Binary Ninja&amp;#39;s type library as proper struct and enum definitions, including padding bytes, packed variants, and discriminant fields.&lt;&#x2F;p&gt;
&lt;p&gt;The workflow requires you to compile a version of the target code (or a close proxy) with -Zprint-type-sizes on a nightly toolchain and redirect the output to a file, then load that file via the plugin. For malware analysis where you do not have source, you would compile the closest available open-source equivalent, or reconstruct a proxy crate that uses the same dependencies at the same versions, then import the resulting type layout.&lt;&#x2F;p&gt;
&lt;p&gt;The caveat the author flags is that layout is &lt;strong&gt;not stable&lt;&#x2F;strong&gt; across compilations: different compiler versions, different optimisation flags, or different target platforms can change field ordering and padding. So the imported types are a strong approximation, not a guarantee. For Akira v2, given that you have aho-corasick 1.1.2 and can narrow the rustc version window via the .rustc section, you could construct a fairly tight proxy.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-18&quot;&gt;Is this a Rust binary?&lt;&#x2F;h2&gt;
&lt;p&gt;Rust binaries have a distinctive fingerprint across multiple analysis layers. The confidence of identification increases as more signals converge.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Panic infrastructure&lt;&#x2F;strong&gt; is the strongest single indicator. Every Rust binary compiled with std embeds panic handling machinery. Look for strings matching src&#x2F; paths with .rs extensions, often referencing crate names or Rust standard library internals. The string called Option::unwrap() on a None value or similar is essentially a Rust signature. Akira v2&amp;#39;s TODO: panic message you found is directly in this family.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Crate name recovery from panic strings&lt;&#x2F;strong&gt; is your second tier. The panic formatting system preserves file paths at compile time. You can recover internal crate names, sometimes even dependency crate names (you found aho-corasick 1.1.2 this way), and frequently leaked workspace paths such as the WSL dev path in your Akira sample.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Symbol and section naming&lt;&#x2F;strong&gt; gives structural confirmation. Even in stripped release builds, section names like .rustc, &lt;strong&gt;rustc_debug_gdb_scripts_section&lt;&#x2F;strong&gt; (on ELF), or the presence of Rust-specific metadata embedded in the binary give it away. On Windows PE, look for .rdata strings referencing core::panicking or alloc::.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Monomorphisation artefacts&lt;&#x2F;strong&gt; are visible in the function count. Rust generics are monomorphised at compile time, so a Rust binary typically has an unusually large number of small, similar functions relative to its feature surface. This is measurable in Binary Ninja by comparing function count to code size.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Trait vtable layout&lt;&#x2F;strong&gt; is distinguishable from C++ vtables. Rust vtable entries for a trait object are: size, alignment, destructor pointer, then method pointers in trait-definition order. No RTTI string, no virtual inheritance complexity. C++ vtables typically have a RTTI pointer at offset -8.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Standard allocator fingerprint&lt;&#x2F;strong&gt;: Rust’s default global allocator calls through __rust_alloc, __rust_dealloc, __rust_realloc. These names survive in the import table or as internal symbols even after stripping.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Absence of C++ indicators&lt;&#x2F;strong&gt; is a useful negative signal: no _ZN Itanium mangled names, no type_info strings, no exception handling personality routine (__gxx_personality_v0).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-19&quot;&gt;(Updated 10 Jun 2026) It’s A Release Build&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The std-DWARF leak is &lt;strong&gt;conditional on linking the backtrace machinery&lt;&#x2F;strong&gt;, which substantial std use pulls in.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Akirav2 is an optimised Release build:&lt;&#x2F;strong&gt; &lt;strong&gt;debug = 0, overflow-checks OFF.&lt;&#x2F;strong&gt; The .debug_* sections present are **leaked precompiled-**&lt;strong&gt;libstd DWARF&lt;&#x2F;strong&gt;; the attacker&amp;#39;s own code and its application dependencies carry &lt;strong&gt;no DWARF&lt;&#x2F;strong&gt;. &amp;quot;Debug&amp;quot; is true only colloquially (DWARF sections exist); the consequential point stands, there is no user-code debug information.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;AK=$(ls ~&#x2F;samples_elf_v2&#x2F;Akira_v2_0ee1d284*4625664 | grep -v STRIPPED | head -1)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;readelf -S &amp;quot;$AK&amp;quot; | grep -iE &amp;#39;\.debug&amp;#39;                 # are .debug_* sections present?
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;objdump --dwarf=info &amp;quot;$AK&amp;quot; 2&amp;gt;&#x2F;dev&#x2F;null | grep -ic &amp;#39;akira&amp;#39;   # does DWARF name attacker code?
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;objdump --dwarf=info &amp;quot;$AK&amp;quot; 2&amp;gt;&#x2F;dev&#x2F;null | grep -i &amp;#39;DW_AT_name&amp;#39; | grep -iE &amp;#39;lock|encrypt|prng&amp;#39; | head  
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;11.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;12.webp&quot; alt=&quot;Triage summary in Binary Ninja&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Triage summary in Binary Ninja&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;No&lt;&#x2F;strong&gt;**.debug_* sections anywhere in the list of Binary Ninja though.**&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;13.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;.eh_frame and .eh_frame_hdr: these are unwind tables for exception handling, present in both debug and release Rust builds, but they are not DWARF debug info&lt;&#x2F;li&gt;
&lt;li&gt;.gcc_except_table: exception handling landing pads, again not debug info&lt;&#x2F;li&gt;
&lt;li&gt;.rodata (0x438540-0x49700b): this is large at ~370KB, which is where all the Rust panic strings, format strings, and string literals live. This is your goldmine for understanding program behaviour without symbols.&lt;&#x2F;li&gt;
&lt;li&gt;.text (0x4c8030-0x5e529c): code segment, approximately 1.2MB of actual executable code&lt;&#x2F;li&gt;
&lt;li&gt;.dynsym and .dynstr: dynamic symbol table, minimal since most code is statically linked&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Though to be honest, it’s not 100% confirming this is a release build, especially &amp;gt; Rust 1.77 release, the way Rust binary stored debug info changed.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;To be more confident, you might want to investigate&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-20&quot;&gt;.rodata strings are extraordinarily revealing&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;14.webp&quot; alt=&quot;Summary info graph of sections for this analysis&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Summary info graph of sections for this analysis&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;In addition to the infograph above, it’s not 100% confirmed when the .debugxxx sections are absent.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;15.webp&quot; alt=&quot;Search result of &lt;code&gt;panic&lt;&#x2F;code&gt; in the string panel&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Search result of `panic` in the string panel&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;16.webp&quot; alt=&quot;Some interesting strings found in the section of &lt;code&gt;.rodata&lt;&#x2F;code&gt;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Some interesting strings found in the section of `.rodata`&lt;&#x2F;p&gt;
&lt;p&gt;Looking at the strings section and it contains multiple critical findings:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;ESXi targeting confirmed:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;vmfs&#x2F;volumes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;vim-cmd vmsvc&#x2F;getallvms | tail -n +2 | awk &amp;#39;{system(&amp;quot;vim-cmd vmsvc&#x2F;power.off &amp;quot; $1)}&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the exact shell command to enumerate and power off all VMs on an ESXi host, confirming the Check Point analysis.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ESXi&lt;&#x2F;strong&gt; is VMware’s bare-metal hypervisor. It runs directly on server hardware (no host OS underneath) and hosts multiple virtual machines. In enterprise environments it is extremely common: one physical server running ESXi might host dozens of VMs for databases, domain controllers, file servers, etc.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;-21&quot;&gt;Why ransomware authors love targeting it?&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One infection, massive blast radius.&lt;&#x2F;strong&gt; Encrypt the ESXi host and every VM it manages goes down simultaneously, rather than having to compromise each machine individually.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Often poorly monitored.&lt;&#x2F;strong&gt; EDR agents are rarely deployed on hypervisor hosts the way they are on Windows endpoints.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Critical infrastructure.&lt;&#x2F;strong&gt; Organisations are under extreme pressure to pay because their entire virtualised environment is offline.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;NFS&#x2F;VMFS datastores.&lt;&#x2F;strong&gt; VM disk images (.vmdk files) live on shared storage volumes that ESXi mounts directly, so the ransomware can enumerate and encrypt them efficiently without touching individual guest OSes.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The --path &#x2F;vmfs&#x2F;volumes default you saw in the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;Check Point article&lt;&#x2F;a&gt; is the standard ESXi datastore mount point. That single directory contains the disk images for every hosted VM.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;CLI argument strings:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;akiranew&#x2F;src&#x2F;main.rs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Valid values for --logs: trace, debug, error, info, warn
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Root path: Starting path: &#x2F;vmfs&#x2F;volumes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Exclude expression: Wrong regexp provided: No exclude expression
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This reveals the seahorse CLI parser argument names directly from the source path embedded in panic strings.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Build ID string:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Build ID not provided.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Build ID detail useful for the analysis:&lt;&#x2F;strong&gt; The build ID for v2 (version 2024.1.30) is located at offset 0x41970 for 10 bytes. The ransomware requires either the &#x2F;vmfs&#x2F;volumes&#x2F; path to exist or a path to be explicitly specified, otherwise it will fail to execute entirely. This matches what Talos documented about the build ID check.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Crate paths leaking source structure:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;aho-corasick-1.1.2&#x2F;src&#x2F;automaton.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is extraordinarily useful. It reveals:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The developer compiled on &lt;strong&gt;WSL&lt;&#x2F;strong&gt; (&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;) on a Windows host&lt;&#x2F;li&gt;
&lt;li&gt;The exact crate version: aho-corasick-1.1.2&lt;&#x2F;li&gt;
&lt;li&gt;The Cargo registry path, confirming this is a standard Cargo build&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;TODO comment leaked into binary (&lt;strong&gt;0x4419ce)&lt;&#x2F;strong&gt;:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;char const data_4419ce[0xb2] = &amp;quot;Continuing execution in parent process, new child has pid: New child processshFailed to stop vms: TODO: panic messageFork stopvm failed&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The string TODO: panic message appears embedded within a stopvm error handling path in .rodata, suggesting the VM shutdown error branch uses a todo!() macro or placeholder panic rather than a proper error message, indicating incomplete error handling in that specific code path.&lt;&#x2F;p&gt;
&lt;p&gt;The full string at 0x4419ce is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Continuing execution in parent process, new child has pid: 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;New child processsh
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Failed to stop vms: TODO: panic message
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Fork stopvm failed
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These are &lt;strong&gt;not&lt;&#x2F;strong&gt; separate panic messages concatenated by the Rust runtime. They are &lt;strong&gt;multiple&lt;&#x2F;strong&gt; &lt;strong&gt;&amp;amp;str literals that LLVM merged into a single&lt;&#x2F;strong&gt;**.rodata entry** at compile time. Here is why.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-22&quot;&gt;How Rust panic strings reach .rodata&lt;&#x2F;h3&gt;
&lt;p&gt;Every panic!(), eprintln!(), or string literal in Rust source becomes a &amp;amp;&amp;#39;static str pointing into .rodata. The Rust compiler lowers panic!(&amp;quot;some message&amp;quot;) roughly to:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;core::panicking::panic_fmt(format_args!(&amp;quot;some message&amp;quot;))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The format_args! macro constructs a fmt::Arguments struct at compile time. For simple string literals with no format arguments, LLVM sees these as adjacent static string data and, during its &lt;strong&gt;string merging&lt;&#x2F;strong&gt; optimisation pass (-O2&#x2F;-O3), physically concatenates adjacent string literals that share no null terminator dependency into a single .rodata blob. The individual strings are then referenced by pointer and length pairs into offsets within that blob.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-23&quot;&gt;What you are seeing at 0x4419ce&lt;&#x2F;h3&gt;
&lt;p&gt;Binja is showing you a single 178-byte (0xb2) merged blob containing what were originally &lt;strong&gt;four separate string literals&lt;&#x2F;strong&gt; in the source:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;17.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-24&quot;&gt;Why LLVM merges them&lt;&#x2F;h3&gt;
&lt;p&gt;With --release and LTO enabled, LLVM&amp;#39;s MergeGlobals and ConstantMerge passes fold adjacent read-only string data into contiguous blobs. Binja then displays the entire blob as one char const array from the start address to the end of the last string in the merged region. The individual strings are still referenced separately in the code via (ptr, len) fat pointer pairs pointing into offsets within the blob.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-25&quot;&gt;Verifying this in Binja&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;18.webp&quot; alt=&quot;Cross Ref of 0x4419ce&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Cross Ref of 0x4419ce&lt;&#x2F;p&gt;
&lt;p&gt;To confirm the individual string boundaries, look at the cross-references to 0x4419ce. Each xref will use a different offset into the blob with a different length, corresponding to one of the original source strings. For example:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;0x4419ce + 0x00, len=0x3c = &amp;quot;Continuing execution...&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;0x4419ce + 0x3c, len=0x12 = &amp;quot;New child process...&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;0x4419ce + 0x4e, len=0x27 = &amp;quot;Failed to stop vms: TODO: panic message&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;0x4419ce + 0x75, len=0x12 = &amp;quot;Fork stopvm failed&amp;quot;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;In Binja: right-click on data_4419ce &amp;gt; Cross References, and you will see each reference using a different offset, confirming the individual string boundaries.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Windows version regex in&lt;&#x2F;strong&gt;**.rodata:**&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;win10-3(.*)(.*)|win10-4(.*)(.*)|win10-5(.*)(.*)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interesting:&lt;&#x2F;strong&gt; Windows version detection strings in a Linux ELF. This likely comes from a dependency that includes cross-platform compatibility checks.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;aho-corasick crate confirmed:&lt;&#x2F;strong&gt; This is a string searching library, used here likely for file extension matching when deciding which files to encrypt.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;19.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Additional crate paths confirmed:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;aho-corasick-1.1.2&#x2F;src&#x2F;util&#x2F;alphabet.rs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;aho-corasick-1.1.2&#x2F;src&#x2F;util&#x2F;debug.rs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;aho-corasick-1.1.2&#x2F;src&#x2F;util&#x2F;remapper.rs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;aho-corasick-1.1.2&#x2F;src&#x2F;util&#x2F;primitives.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Standard library paths:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;library&#x2F;alloc&#x2F;src&#x2F;raw_vec.rs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;library&#x2F;alloc&#x2F;src&#x2F;ffi&#x2F;c_str.rs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;library&#x2F;alloc&#x2F;src&#x2F;sync.rs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;library&#x2F;alloc&#x2F;src&#x2F;vec&#x2F;mod.rs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;library&#x2F;alloc&#x2F;src&#x2F;fmt.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;lazy_st crate (truncated):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;lazy_st...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is almost certainly lazy_static or once_cell, used for global state initialisation. Common in Rust malware for initialising encryption keys or configuration once at startup.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;20.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;21.webp&quot; alt=&quot;Xref&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Xref&lt;&#x2F;p&gt;
&lt;p&gt;This is the cross-reference panel for the lazy_st blob, not the next address. But the code reference is extremely revealing:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_$LT$alloc..string..FromUtf8Error$u20$as$u20$core..fmt..Debug$GT$::fmt::h510500198db9042f
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And the instruction:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea rsi, [rel 0x442525]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This tells you the code is loading a pointer into the middle of the blob at 0x442525, which is an offset within data_4424b7. The mangled symbol name FromUtf8Error confirms this blob is primarily about the alloc::string::FromUtf8Error Debug implementation, and lazy_st is simply the tail end of an adjacent panic path that got merged into the same blob.&lt;&#x2F;p&gt;
&lt;p&gt;Navigate to 0x442525 directly in Binja to see what the string looks like from that offset. But more importantly, to find the continuation of lazy_st, go to:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;g -&amp;gt; 0x442580
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That is 0x4424b7 + 0xc9, the first byte after this blob ends. What Binja shows there will either be a new char const entry starting with atic or a completely unrelated string. Share that screenshot and we will know definitively.&lt;&#x2F;p&gt;
&lt;p&gt;Now confirmed. Look at address 0x500ba0:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lea rsi, [rel data_4424b7[0x6e]]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And Binja’s inline comment shows the full string at that offset:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;quot;FromUtf8Error&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;lazy_static-1.4.0&#x2F;src&#x2F;inline_lazy.rs&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.c
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Confirmed:&lt;&#x2F;strong&gt; &lt;strong&gt;lazy_static-1.4.0&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The exact version is 1.4.0, not unknown. The source file is inline_lazy.rs, which is the internal implementation file for lazy_static&amp;#39;s hidden static guarding mechanism.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;22.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;...&#x2F;lazy_static-1.4.0&#x2F;src&#x2F;inline_lazy.rs&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;unicode-wi
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;New crate partially confirmed:&lt;&#x2F;strong&gt; &lt;strong&gt;unicode-wi...&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is almost certainly unicode-width, a standard Rust crate for determining the display width of Unicode characters. It is a very common dependency pulled in by terminal&#x2F;CLI output crates. Given Akira v2 uses &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;tree&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&quot;&gt;indicatif&lt;&#x2F;a&gt; for progress bars and simplelog for logging, unicode-width as a transitive dependency makes complete sense.&lt;&#x2F;p&gt;
&lt;p&gt;To confirm the full name and version, navigate to the next address after this blob ends in Binja. The continuation should show dth-x.x.x completing unicode-width.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Rust struct names leaking:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;FatAVX2, SlimSSSE3, SlimAVX2 are &lt;strong&gt;Rust struct names&lt;&#x2F;strong&gt; defined inside &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;aho-corasick&#x2F;1.1.2&#x2F;src&#x2F;aho_corasick&#x2F;packed&#x2F;teddy&#x2F;builder.rs.html&quot;&gt;aho-corasick&lt;&#x2F;a&gt;&amp;#39;s &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BurntSushi&#x2F;aho-corasick&#x2F;tree&#x2F;master&#x2F;src&#x2F;packed&#x2F;teddy&quot;&gt;Teddy packed search engine&lt;&#x2F;a&gt;. They are not algorithm names in the abstract sense. Specifically for SIMD backend implementations of the Teddy packed search algorithm.:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;SlimSSSE3 is a struct wrapping a 128-bit SSSE3 Teddy searcher&lt;&#x2F;li&gt;
&lt;li&gt;FatAVX2 is a 256-bit AVX2 fat Teddy searcher.&lt;&#x2F;li&gt;
&lt;li&gt;These are selected at runtime based on CPU feature detection: SlimSSSE3 is chosen when AVX2 is unavailable, FatAVX2 when AVX2 is available and the fat variant is preferred.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;FatAVX2fat256teddymasksFatInvalidInputAnchoredInvalidInputUnanchoredUnsupportedStreamgotUnsupportedOverlappingPackedRareByteOffsetRa...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;aho-corasick-1.1.2&#x2F;src&#x2F;util&#x2F;remapper.rsLeftmostLongestRabinKarpsearch_kindby_idordertotal_pattern_bytesbucketsimpmemory_usageSlimSSSE3slim128sl
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;akira_utils&#x2F;src&#x2F;lib.rs:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;...akira_utils&#x2F;src&#x2F;lib.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Look carefully at line 004423e0: a_utils&#x2F;src&#x2F;lib.rs. Combined with the prefix on the line above ending in akir, this reads as akira_utils&#x2F;src&#x2F;lib.rs. This is a &lt;strong&gt;custom internal crate&lt;&#x2F;strong&gt; named akira_utils, not a public crates.io dependency. This means the malware authors structured their code as a Cargo workspace with at least one internal library crate.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-26&quot;&gt;What this tells you about the binary structure&lt;&#x2F;h2&gt;
&lt;p&gt;You can now reconstruct a partial Cargo.toml dependency tree purely from .rodata:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;[dependencies]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;aho-corasick = &amp;quot;1.1.2&amp;quot;      # confirmed, multiple source files
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;lazy_static = &amp;quot;1.4.0&amp;quot;       # Confirmed
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;[workspace]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;members = [
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &amp;quot;akira_utils&amp;quot;,           # internal crate confirmed
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The crates.io registry hash 6f17d22bba15001f is consistent across all paths, confirming these all came from the same Cargo build environment and the same registry snapshot.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-27&quot;&gt;Panic String Analysis&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a strong methodological point: &lt;strong&gt;panic string analysis recovers the internal crate architecture of a stripped Rust binary without any symbol information.&lt;&#x2F;strong&gt; Specifically:&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Internal workspace crate names (akira_utils) recoverable&lt;&#x2F;li&gt;
&lt;li&gt;Exact third-party crate versions (aho-corasick-1.1.2) recoverable&lt;&#x2F;li&gt;
&lt;li&gt;Developer platform (WSL on Windows, same username user throughout) recoverable&lt;&#x2F;li&gt;
&lt;li&gt;SIMD feature flags (AVX2, SSSE3) inferable from algorithm variant names&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;None of this requires DWARF, symbol tables, or dynamic analysis. It is purely static .rodata recovery&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;23.webp&quot; alt=&quot;Additional crates and internal paths&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Additional crates and internal paths&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-28&quot;&gt;Additional crates and internal paths&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;New crate confirmed:&lt;&#x2F;strong&gt; &lt;strong&gt;block-buffer-0.10.4&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;block-buffer-0.10.4&#x2F;src&#x2F;lib.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is a cryptography support crate used by digest algorithm implementations. Confirms the crypto pipeline more precisely.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;New crate confirmed:&lt;&#x2F;strong&gt; &lt;strong&gt;sha3-0.10.8&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;sha3-0.10.8&#x2F;src
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;SHA-3 hashing, likely used for key derivation or integrity checking.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;New crate confirmed:&lt;&#x2F;strong&gt; &lt;strong&gt;simplelog-0.12.1&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;simplelog-0.12.1&#x2F;src&#x2F;loggers&#x2F;writelog.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the logging crate. Confirms the --logs trace&#x2F;debug&#x2F;error&#x2F;info&#x2F;warn CLI argument you saw earlier.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;New crate confirmed:&lt;&#x2F;strong&gt; &lt;strong&gt;seahorse-2.2.0&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mnt&#x2F;c&#x2F;Users&#x2F;user&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;seahorse-2.2.0&#x2F;src&#x2F;flag.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The CLI argument parser, version now precisely pinned.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Internal source paths:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;akiranew&#x2F;src&#x2F;path_finder.rs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;akira_v2::path_finder
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Confirms a dedicated path_finder module for filesystem enumeration.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;akiranew&#x2F;src&#x2F;lock.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The encryption locking module, source path directly visible.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;ESXi path exclusions visible:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;proc&#x2F;sbin&#x2F;lib64&#x2F;locker&#x2F;productLocker&#x2F;lost+found&#x2F;bootbank&#x2F;altbootbank&#x2F;tardisks&#x2F;vmfs&#x2F;volumes&#x2F;BOOTBANK1&#x2F;vmfs&#x2F;volumes&#x2F;BOOTBANK2&#x2F;vmfs&#x2F;devices&#x2F;vmimages&#x2F;vmupgrade&#x2F;bootpart.gz&#x2F;local.tgz&#x2F;local.tgzlog
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These are ESXi-specific paths that the ransomware skips during encryption. Directly recoverable from static analysis.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;ThreadPool::execute error string:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;ThreadPool::execute unable to send job into queue.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Confirms a custom thread pool implementation with explicit error handling.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;&lt;strong&gt;indicatif&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; &lt;strong&gt;spinner format strings:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;{spinner:.green} [{elapsed_precise}] {msg} [{wide_bar:.cyan&#x2F;blue}] {bytes}&#x2F;{total_bytes} ({eta})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The progress bar format string from the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;indicatif&lt;&#x2F;a&gt; crate, directly confirming its use for operator-facing encryption progress display.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;mpmc&#x2F;mod.rs path:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;mpmc&#x2F;mod.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Multi-producer multi-consumer channel, part of the threading architecture for parallel encryption workers.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;24.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;25.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-29&quot;&gt;The ransom note is embedded directly in .rodata&lt;&#x2F;h2&gt;
&lt;p&gt;This is a significant finding. The entire akiranew.txt ransom note text is stored as a static string literal in .rodata starting around 0x440da0. Visible fragments include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Decryptor offer and negotiation text&lt;&#x2F;li&gt;
&lt;li&gt;.3. The security report or the exclusive first-hand information&lt;&#x2F;li&gt;
&lt;li&gt;Reference to darkmarket sale threat&lt;&#x2F;li&gt;
&lt;li&gt;Onion address: akiral2iz6a7qgd3ayp3l6yub7xx2uep76idk3u2kollpj5z3z636bad[.]onion&lt;&#x2F;li&gt;
&lt;li&gt;TOR browser instructions&lt;&#x2F;li&gt;
&lt;li&gt;Chat link: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;akiralkzxzq2dsrzsrvbr2xgbbu2wgsmxryd4csgfameg52n7efvr2id%5B.%5Donion&quot;&gt;https:&#x2F;&#x2F;akiralkzxzq2dsrzsrvbr2xgbbu2wgsmxryd4csgfameg52n7efvr2id[.]onion&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Code: 7818-NX-VVGG-FSFV&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is the complete ransom note hardcoded as a single static string, not generated dynamically at runtime, it is a compile-time constant embedded verbatim.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;26.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-30&quot;&gt;Encryption operational strings&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;prng.rs internal source path:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;akiranew&#x2F;src&#x2F;prng.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A first-party PRNG module (akiranew&#x2F;src&#x2F;prng.rs) in the malware’s own source tree. Correction (24 June 2026): an earlier version suggested the authors hand-rolled a generator instead of using a crate. The binary shows otherwise. It links rand 0.8.5, rand_chacha 0.3.1 and getrandom 0.2.10, and the demangled symbols expose ThreadRng as ReseedingRng&amp;lt;ChaCha12Core, OsRng&amp;gt;, that is, the standard ChaCha12 CSPRNG reseeded from OsRng. No hand-rolled LCG or xorshift constants are present. So prng.rs is best read as a thin first-party wrapper over the rand crate (used for key material and random IDs), not a bespoke generator.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Threading and file locking strings:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;num_threads: 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Free threads available: 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;akiranew.txt
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;note write fail
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;File crypting
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;File crypted
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Thread crashed.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;File doesn&amp;#39;t locked.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Error: Count of locking attempts for file is exceeded
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Done
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These are all operator-facing runtime status messages. Crucially akiranew&#x2F;src&#x2F;prng.rs is the source file for the PRNG, and the locking attempt counter confirms a retry mechanism for file locking during encryption.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Encryption progress format strings:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;; stepsize  MB was read from the file
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Unable to seek to position
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Seek position of file : [] MB, buffer size is , block counter = Current buffer for
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;] MB is filled of zeros, skipping...{prefix:.bold.dim} {spinner} {wide_msg} {bytes_per_sec}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These reveal the chunked encryption algorithm: the binary reads files in blocks (stepsize MB), seeks to positions, skips zero-filled blocks, and reports bytes per second to the operator.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;akira_v2::lock namespace confirmed:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;akira_v2::lock6edd23fc19cbb91fa5574afbcfc81fb0e368be2b42f825e3e9f48dcbc38a0525
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This mangled symbol name contains what appears to be a hash suffix, consistent with Rust’s symbol mangling scheme for disambiguating generic instantiations.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Wrong secret key format error:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Wrong secret key format. A hex string of 64 chars is expected
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Reveals the encryption key is passed as a 64-character hex string (32 bytes), consistent with a 256-bit key.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-31&quot;&gt;Updated Cargo dependency table&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;27.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;28.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Internal modules recovered:&lt;&#x2F;strong&gt; akira_utils, path_finder, lock, prng&lt;&#x2F;p&gt;
&lt;p&gt;All of this is recoverable purely from .rodata static analysis without executing the binary, without DWARF, and without symbols. That is your core thesis demonstrated in practice.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;29.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-32&quot;&gt;The full CLI help string recovered at 0x441603&lt;&#x2F;h2&gt;
&lt;p&gt;This is the most valuable string in the screenshot. The complete help text is visible:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Start path. Default value: &#x2F;vmfs&#x2F;volumes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;idstopvm
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;vmonlyCrypt only .vmdk, .vmem, .vmx, .log, .vswp, .vmsd, .vmsn files
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;threadsNumber of threads (1-1000). Default: number of logical CPU cores
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;epPercent of crypt. Default - 15%
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Work in background
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Print logs. Valid values for: trace, debug, error, info, warn. Default: off
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;excludeSkip files by &amp;quot;regular&amp;quot; extension. Example: --exclude=&amp;quot;startf&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This reconstructs the &lt;strong&gt;complete CLI interface&lt;&#x2F;strong&gt; of Akira v2:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;30.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The --ep 15% default is particularly significant: Akira v2 only encrypts &lt;strong&gt;15% of each file by default&lt;&#x2F;strong&gt;, which is a deliberate speed optimisation. Enough to corrupt the file but fast enough to maximise the number of files encrypted before detection.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-33&quot;&gt;Regex exclusion pattern at 0x441780&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;ilename(.*)(.*)&amp;quot; using this regular expression will skip all files 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;starting with startfilename and having any extensions. 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Multiple regular expressions using &amp;quot;|&amp;quot; can also be processed: 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;--exclude=&amp;quot;(win10-3(.*)(.*))|(win10-4(.*)(.*))|(win10-5(.*)(.*))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the help text for --exclude, and the example regex win10-3|win10-4|win10-5 is revealing: it shows the operators were using Windows version strings as exclusion patterns, suggesting they selectively avoided encrypting certain Windows VM guests while targeting others.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-34&quot;&gt;akiranew&#x2F;src&#x2F;main.rs source path confirmed at 0x4418a5&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;akiranew&#x2F;src&#x2F;main.rstraced
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The source file name akiranew&#x2F;src&#x2F;main.rs is directly embedded, confirming the project root crate name is akiranew, not akira_v2. The v2 designation is a researcher label, not the developer&amp;#39;s own naming.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-35&quot;&gt;The 0x4419ce string context now fully clear&lt;&#x2F;h2&gt;
&lt;p&gt;With this screenshot you can see the string immediately above 0x4419ce:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;d vmsvc&#x2F;getallvms | tail -n +2 |
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;awk &amp;#39;{system(&amp;quot;vim-cmd vmsvc&#x2F;power.off &amp;quot; $1)}&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then at 0x4419ce:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Continuing execution in parent process, new child has pid: 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;New child processsh
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Failed to stop vms: TODO: panic message
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Fork stopvm failed
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Logical CPUs:  Threads are set: EP values
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This gives you the full &lt;strong&gt;operational sequence&lt;&#x2F;strong&gt; of the --stopvm and --fork flow:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Execute shell command to power off all VMs&lt;&#x2F;li&gt;
&lt;li&gt;Fork child process for encryption&lt;&#x2F;li&gt;
&lt;li&gt;Report child PID to operator&lt;&#x2F;li&gt;
&lt;li&gt;If stopvm fails: emit the TODO: panic message placeholder error&lt;&#x2F;li&gt;
&lt;li&gt;Report thread and EP configuration&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The TODO: panic message is definitively the stopvm failure error handler, confirming it is a developer placeholder left in the stopvm error branch specifically.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-36&quot;&gt;Interactive prompt strings at 0x441a80&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;is 0 to 100. EP are set: %[1&#x2F;2]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Do you want to continue? (y&#x2F;n).
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And at 0x441abe:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Failed to read line
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Invalid input. Please enter &amp;#39;y&amp;#39; or &amp;#39;n&amp;#39;.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Exiting...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Continuing...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This reveals Akira v2 has an &lt;strong&gt;interactive confirmation prompt&lt;&#x2F;strong&gt; before beginning encryption, where the operator confirms the EP percentage and thread settings. This is an operator safety check, not something visible in prior published analyses.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-37&quot;&gt;Updated internal module map&lt;&#x2F;h2&gt;
&lt;p&gt;You can now reconstruct the project structure almost completely from .rodata alone:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;akiranew&#x2F;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;├── src&#x2F;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;│   ├── main.rs          (CLI parsing, interactive prompt, fork&#x2F;stopvm)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;│   ├── lock.rs          (encryption logic)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;│   ├── path_finder.rs   (filesystem enumeration)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;│   └── prng.rs          (custom PRNG)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;└── Cargo.toml
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    (workspace member: akira_utils)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All recovered without symbols, without DWARF, purely from panic strings and format literals in .rodata. This is now a very strong empirical demonstration of your thesis.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-38&quot;&gt;Updated dependency table&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;31.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-39&quot;&gt;The Control Flow Map&lt;&#x2F;h2&gt;
&lt;p&gt;The top-level call chain is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;main  -&amp;gt;  default_action  -&amp;gt;  lock  -&amp;gt;  lock_closure
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;main: collects CLI arguments, initialises the seahorse App struct, calls app.run(args).&lt;&#x2F;li&gt;
&lt;li&gt;default_action: the seahorse callback; reads flag values, optionally shuts down VMs, then calls lock.&lt;&#x2F;li&gt;
&lt;li&gt;lock: launches worker threads.&lt;&#x2F;li&gt;
&lt;li&gt;lock_closure: the actual encryption logic, running per thread.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;-40&quot;&gt;Section 1: How args().collect() Works&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;32.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The malware’s first action is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let program_args: Vec&amp;lt;String&amp;gt; = args().collect();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Tracing what this produces in memory requires following a four-layer type unwrapping chain.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-41&quot;&gt;The unwrapping chain&lt;&#x2F;h2&gt;
&lt;p&gt;The article traces the type system from the public API down to bytes. Each step is a wrapper that hides the real representation.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;33.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;std::env::args()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  -&amp;gt; returns Args
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      -&amp;gt; wraps ArgsOs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;          -&amp;gt; wraps sys::args::Args  (PAL type, OS-specific)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              -&amp;gt; on Unix: Vec&amp;lt;OsString&amp;gt;.into_iter()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                  -&amp;gt; IntoIter&amp;lt;OsString&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The key insight:&lt;&#x2F;strong&gt; the &lt;em&gt;public&lt;&#x2F;em&gt; type Args and its iterator wrapper are thin newtypes. By the time you reach the PAL implementation, the real data is a Vec&lt;OsString&gt; that was built by reading argc&#x2F;argv from C FFI, then converted to OsString entries.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;env.rs#L756&quot;&gt;std::env::args()&lt;&#x2F;a&gt; function returns Args, which wraps ArgsOs, which wraps sys::args::Args. Both outer wrappers are zero-overhead newtypes. The real work happens in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L18&quot;&gt;sys&#x2F;pal&#x2F;unix&#x2F;args.rs&lt;&#x2F;a&gt;, the PAL (Platform Abstraction Layer) implementation for Unix targets.&lt;&#x2F;p&gt;
&lt;p&gt;This file does not live in the main std source tree. It is swapped in at compile time based on the target OS, which is why you cannot find sys::args::args by grepping the standard library directly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-42&quot;&gt;Step 1: The Public API&lt;&#x2F;h2&gt;
&lt;p&gt;From &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;env.rs#L756&quot;&gt;env.rs&lt;&#x2F;a&gt; (line ~756):&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub struct Args {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    inner: ArgsOs,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub struct ArgsOs {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    inner: sys::args::Args,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn args() -&amp;gt; Args {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Args { inner: args_os() }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn args_os() -&amp;gt; ArgsOs {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ArgsOs { inner: sys::args::args() }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Args and ArgsOs are pure newtypes. They add zero bytes of overhead. The only real data lives in sys::args::Args, which is a PAL type.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L61&quot;&gt;sys::args::Args&lt;&#x2F;a&gt; is a PAL type because its concrete definition lives inside one of those OS-specific directories, not in a shared location. On Unix (which includes ESXi&#x2F;Linux), it is defined in &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L61&quot;&gt;sys&#x2F;pal&#x2F;unix&#x2F;args.rs&lt;&#x2F;a&gt;(line ~61) as:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub struct Args {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    iter: vec::IntoIter&amp;lt;OsString&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On Windows it would be defined differently, potentially with a different internal representation. I will list them in the section of Further Reading below.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; &lt;em&gt;The practical consequence for RE is that if you look for&lt;&#x2F;em&gt; &lt;em&gt;sys::args::Args in the main standard library source you will not find it, because there is no single canonical definition. You have to know which PAL branch was compiled in, which is determined by the binary&amp;#39;s target OS. For Akira v2 targeting ESXi, that means the Unix branch, and therefore the&lt;&#x2F;em&gt; &lt;em&gt;vec::IntoIter&lt;OsString&gt; layout we traced through.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;-43&quot;&gt;Step 2: The PAL Implementation (Unix)&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;PAL stands for &lt;strong&gt;Platform Abstraction Layer&lt;&#x2F;strong&gt;. It is a pattern in the Rust standard library where OS-specific implementations are swapped in at compile time based on the target platform.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The way it works structurally is that std has a module sys::pal with subdirectories for each supported OS:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    unix&#x2F;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    windows&#x2F;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    wasi&#x2F;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each subdirectory implements the same interface, but with platform-native code inside. The compiler selects the correct one via #[cfg(target_os = &amp;quot;...&amp;quot;)] attributes. The rest of std calls into sys::pal without knowing or caring which OS is underneath.&lt;&#x2F;p&gt;
&lt;p&gt;Because Akira v2 targeted ESXi (Linux-based), the relevant PAL is &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L18&quot;&gt;sys&#x2F;pal&#x2F;unix&#x2F;args.rs&lt;&#x2F;a&gt;. The article quotes the full function:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F;&#x2F; Returns the command line arguments
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn args() -&amp;gt; Args {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let (argc, argv) = imp::argc_argv();          &#x2F;&#x2F; (1)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let mut vec = Vec::with_capacity(argc as usize);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    for i in 0..argc {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let ptr = unsafe { argv.offset(i).read() }; &#x2F;&#x2F; (2)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        if ptr.is_null() { break; }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let cstr = unsafe { CStr::from_ptr(ptr) };  &#x2F;&#x2F; (3)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        vec.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); &#x2F;&#x2F; (4)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Args { iter: vec.into_iter() }                 &#x2F;&#x2F; (5)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What each line does:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;(1)&lt;&#x2F;strong&gt; &lt;strong&gt;argc_argv()&lt;&#x2F;strong&gt; calls into the C runtime via FFI. On Linux, argc and argv are passed into the process by the kernel via the ELF aux vector and made available through glibc&amp;#39;s .init_array mechanism. This gives you the classic C int argc, char** argv pair.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;(2)&lt;&#x2F;strong&gt; &lt;strong&gt;argv.offset(i).read()&lt;&#x2F;strong&gt; is raw pointer arithmetic. argv is a *const *const c_char. .offset(i) moves the pointer by i * 8 bytes (on x64), then .read() dereferences it, giving you a *const c_char pointing to the i-th argument string.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;(3)&lt;&#x2F;strong&gt; &lt;strong&gt;CStr::from_ptr(ptr)&lt;&#x2F;strong&gt; wraps that pointer as a null-terminated C string, no copy yet.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;(4)&lt;&#x2F;strong&gt; &lt;strong&gt;cstr.to_bytes().to_vec()&lt;&#x2F;strong&gt; copies the raw bytes (excluding the null terminator) into a Vec&lt;u8&gt;, then OsStringExt::from_vec wraps that as an OsString. On Linux, OsString is literally just a Vec&lt;u8&gt; (three words: ptr, len, cap).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;(5)&lt;&#x2F;strong&gt; &lt;strong&gt;vec.into_iter()&lt;&#x2F;strong&gt; is the key transformation. It consumes the Vec&lt;OsString&gt; and produces a vec::IntoIter&lt;OsString&gt;. This is what sys::args::Args actually stores.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-44&quot;&gt;Step 3: The Concrete Memory Layout&lt;&#x2F;h2&gt;
&lt;p&gt;The definitionof &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;bd0826a4521a845f36cce1b00e1dd2918ba09e90&#x2F;library&#x2F;alloc&#x2F;src&#x2F;vec&#x2F;into_iter.rs#L45&quot;&gt;IntoIter&lt;&#x2F;a&gt; in the current compiler source (lines 45–61 of &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;bd0826a4521a845f36cce1b00e1dd2918ba09e90&#x2F;library&#x2F;alloc&#x2F;src&#x2F;vec&#x2F;into_iter.rs#L45&quot;&gt;into_iter.rs&lt;&#x2F;a&gt;) is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub struct IntoIter&amp;lt;T, A: Allocator = Global&amp;gt; {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub(super) buf:     NonNull&amp;lt;T&amp;gt;,       &#x2F;&#x2F; pointer to start of heap allocation
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub(super) phantom: PhantomData&amp;lt;T&amp;gt;,   &#x2F;&#x2F; zero-size, no bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub(super) cap:     usize,            &#x2F;&#x2F; total capacity
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub(super) alloc:   ManuallyDrop&amp;lt;A&amp;gt;,  &#x2F;&#x2F; allocator (Global = zero-size)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub(super) ptr:     NonNull&amp;lt;T&amp;gt;,       &#x2F;&#x2F; pointer to next element to yield
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    pub(super) end:     *const T,         &#x2F;&#x2F; pointer one-past last remaining element
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With the default Global allocator (which is zero-sized), the in-memory layout on x64 is:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;34.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Total: &lt;strong&gt;32 bytes&lt;&#x2F;strong&gt; on x64. phantom and alloc (Global) contribute zero bytes.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The actual &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;bd0826a4521a845f36cce1b00e1dd2918ba09e90&#x2F;library&#x2F;std&#x2F;src&#x2F;ffi&#x2F;os_str.rs#L91&quot;&gt;OsString&lt;&#x2F;a&gt; objects live on the heap at the address buf points to.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Each &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;bd0826a4521a845f36cce1b00e1dd2918ba09e90&#x2F;library&#x2F;std&#x2F;src&#x2F;ffi&#x2F;os_str.rs#L91&quot;&gt;OsString&lt;&#x2F;a&gt; on that heap is, on Linux, a &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;bd0826a4521a845f36cce1b00e1dd2918ba09e90&#x2F;library&#x2F;alloc&#x2F;src&#x2F;vec&#x2F;mod.rs#L397&quot;&gt;Vec&lt;u8&gt;&lt;&#x2F;a&gt; internally:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;[ ptr: 8 bytes ][ len: 8 bytes ][ cap: 8 bytes ]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;-45&quot;&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;bd0826a4521a845f36cce1b00e1dd2918ba09e90&#x2F;library&#x2F;alloc&#x2F;src&#x2F;raw_vec.rs#L76&quot;&gt;&lt;strong&gt;RawVec&lt;T&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; &lt;strong&gt;struct definition&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;PhantomData is zero-sized. Global allocator is zero-sized. Cap is #[repr(transparent)] over usize. Unique&lt;u8&gt; is a non-null pointer, 8 bytes on x64.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub(crate) struct RawVec&amp;lt;T, A: Allocator = Global&amp;gt; {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    inner: RawVecInner&amp;lt;A&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    _marker: PhantomData&amp;lt;T&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;struct RawVecInner&amp;lt;A: Allocator = Global&amp;gt; {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ptr: Unique&amp;lt;u8&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    cap: Cap,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    alloc: A,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;35.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So if you ran the binary with three arguments, the heap buffer at buf would contain three consecutive 24-byte structs, each pointing to its own heap allocation holding the raw argument bytes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-46&quot;&gt;Quick recap&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;36.webp&quot; alt=&quot;We still have a long road to go from here…&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;We still have a long road to go from here…&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-47&quot;&gt;1. Entry point&lt;&#x2F;h3&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn args() -&amp;gt; Args {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Args { inner: args_os() }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn args_os() -&amp;gt; ArgsOs {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ArgsOs { inner: sys::args::args() }   &#x2F;&#x2F; calls into PAL
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Both Args and ArgsOs are newtypes. Zero overhead.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-48&quot;&gt;2. PAL for Linux ( sys&#x2F;pal&#x2F;unix&#x2F;args.rs)&lt;&#x2F;h3&gt;
&lt;p&gt;The imp module for Linux stores argc&#x2F;argv as two atomics set during process init (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L129&quot;&gt;link&lt;&#x2F;a&gt; here, line 129–130):&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;static ARGC: AtomicIsize = AtomicIsize::new(0);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;static ARGV: AtomicPtr&amp;lt;*const u8&amp;gt; = AtomicPtr::new(ptr::null_mut());
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These are populated via .&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L150&quot;&gt;init_array.00099&lt;&#x2F;a&gt; (on glibc Linux) before main runs. The function &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L168&quot;&gt;argc_argv()&lt;&#x2F;a&gt; simply reads them with &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L176&quot;&gt;Ordering::Relaxed&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Then &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L18&quot;&gt;args()&lt;&#x2F;a&gt; builds the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L58&quot;&gt;Vec&lt;OsString&gt;&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn args() -&amp;gt; Args {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let (argc, argv) = imp::argc_argv();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let mut vec = Vec::with_capacity(argc as usize);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    for i in 0..argc {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let ptr = unsafe { argv.offset(i).read() };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        if ptr.is_null() { break; }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let cstr = unsafe { CStr::from_ptr(ptr) };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        vec.push(OsStringExt::from_vec(cstr.to_bytes().to_vec()));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Args { iter: vec.into_iter() }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L61&quot;&gt;sys::args::Args&lt;&#x2F;a&gt; is defined immediately below in the same file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub struct Args {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    iter: vec::IntoIter&amp;lt;OsString&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So sys::args::Args is just a newtype over vec::IntoIter&lt;OsString&gt;. Nothing more.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-49&quot;&gt;3. Concrete memory layout&lt;&#x2F;h3&gt;
&lt;p&gt;After vec.into_iter(), on x64 Linux with &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L129&quot;&gt;Global&lt;&#x2F;a&gt; allocator:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;IntoIter&amp;lt;OsString&amp;gt; (32 bytes on stack or in parent struct):
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  +0x00  buf  (NonNull&amp;lt;OsString&amp;gt;)  = pointer to heap buffer
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  +0x08  cap  (usize)              = total allocated slots
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  +0x10  ptr  (NonNull&amp;lt;OsString&amp;gt;)  = next element to yield
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  +0x18  end  (*const OsString)    = one past last element
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each OsString on the heap is a &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;bd0826a4521a845f36cce1b00e1dd2918ba09e90&#x2F;library&#x2F;alloc&#x2F;src&#x2F;vec&#x2F;mod.rs#L397&quot;&gt;Vec&lt;u8&gt;&lt;&#x2F;a&gt; (on Linux), so each slot in the buffer is 24 bytes:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;OsString (24 bytes):
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  +0x00  ptr  (*mut u8)  = pointer to the argument string bytes
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  +0x08  len  (usize)    = length of the string
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  +0x10  cap  (usize)    = capacity
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;-50&quot;&gt;4. What next() looks like in assembly&lt;&#x2F;h3&gt;
&lt;p&gt;Each call to next() does:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Compare ptr against end. If equal, return None.&lt;&#x2F;li&gt;
&lt;li&gt;Read the OsString at ptr (24 bytes).&lt;&#x2F;li&gt;
&lt;li&gt;Increment ptr by 24 (size_of::&lt;OsString&gt;() on Linux).&lt;&#x2F;li&gt;
&lt;li&gt;Return Some(osstring).&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;When the outer Args::next() in env.rs is called, it additionally calls .into_string().unwrap() on the OsString, which validates UTF-8 and either reuses the allocation or panics.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-51&quot;&gt;The Key RE Insight&lt;&#x2F;h2&gt;
&lt;p&gt;When you see the assembly for args().collect() in the Akira v2 binary, the confusing pointer arithmetic is exactly this: reading the ARGC&#x2F;ARGV globals, looping over argv with stride-8 pointer arithmetic, copying each C string into a heap Vec&lt;u8&gt;, and building the IntoIter header. The collect() on top then walks ptr..end with stride-24 arithmetic, converting each OsString to String. Once you know those two stride values (8 for *const c_char, 24 for OsString on Linux), the assembly becomes readable.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-52&quot;&gt;What argc_argv() does on Linux&lt;&#x2F;h3&gt;
&lt;p&gt;The PAL’s argc_argv() function reads two atomics, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L168&quot;&gt;ARGC and&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;db034cee00570a9b82ea8b9e9e95221dbd745698&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;args.rs#L168&quot;&gt;ARGV&lt;&#x2F;a&gt;, that were written during process initialisation. On glibc Linux, a function registered in .init_array.00099 receives (argc, argv, envp) from glibc before main runs and stores them in these atomics. Reading them back is a single Ordering::Relaxed load on each.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-53&quot;&gt;Building the Vec&lt;OsString&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The args() function then:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Calls Vec::with_capacity(argc as usize) to pre-allocate.&lt;&#x2F;li&gt;
&lt;li&gt;Iterates 0..argc, using raw pointer arithmetic (argv.offset(i).read()) to read each *const c_char.&lt;&#x2F;li&gt;
&lt;li&gt;Wraps each pointer as a CStr, copies the bytes into a Vec&lt;u8&gt; via to_bytes().to_vec(), and pushes it as an OsString via OsStringExt::from_vec.&lt;&#x2F;li&gt;
&lt;li&gt;Calls vec.into_iter(), producing a vec::IntoIter&lt;OsString&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;On Linux, OsString is a Vec&lt;u8&gt; internally: a 24-byte (ptr, len, cap) triple.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-54&quot;&gt;The concrete memory layout&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;src&#x2F;alloc&#x2F;vec&#x2F;into_iter.rs.html#45-48&quot;&gt;vec::IntoIter&lt;T&gt;&lt;&#x2F;a&gt; struct (actual source, not the simplified nomicon version) has these fields:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;buf:     NonNull&amp;lt;T&amp;gt;       &#x2F;&#x2F; 8 bytes: pointer   to heap allocation
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cap:     usize            &#x2F;&#x2F; 8 bytes: total capacity
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;alloc:   ManuallyDrop&amp;lt;A&amp;gt;  &#x2F;&#x2F; 0 bytes: Global allocator is ZST
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;ptr:     NonNull&amp;lt;T&amp;gt;       &#x2F;&#x2F; 8 bytes: next element to yield
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;end:     *const T         &#x2F;&#x2F; 8 bytes: one-past-last element
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Total header: &lt;strong&gt;32 bytes&lt;&#x2F;strong&gt; on x64. The heap buffer at buf holds argc consecutive 24-byte OsString values. Iteration advances ptr by 24 bytes per call to next() and stops when ptr == end.&lt;&#x2F;p&gt;
&lt;p&gt;When collect() runs on the outer Args iterator, each OsString is validated as UTF-8 and converted to String. The final result is a Vec&lt;String&gt;, itself a 24-byte (ptr, len, cap) on the stack.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;RE implication&lt;&#x2F;strong&gt;: the confusing pointer arithmetic around args().collect() in the binary is stride-8 (reading argv[]) followed by string copies followed by stride-24 iteration. Once you know those two stride values, the assembly becomes readable.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-55&quot;&gt;Further Reading&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;-56&quot;&gt;1. Some useful crates for CLI development&lt;&#x2F;h3&gt;
&lt;p&gt;There are several established approaches in Rust:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Full-featured crates:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;clap-rs&#x2F;clap&quot;&gt;clap&lt;&#x2F;a&gt; : the dominant choice, supports derive macros, subcommands, validation, shell completions&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;google&#x2F;argh&quot;&gt;argh&lt;&#x2F;a&gt; : Google’s crate, derive-based, optimised for binary size&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Minimal&#x2F;lightweight crates:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;RazrFalcon&#x2F;pico-args&quot;&gt;pico-args&lt;&#x2F;a&gt; : zero dependencies, manual parsing, very small&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;blyxxyz&#x2F;lexopt&quot;&gt;lexopt&lt;&#x2F;a&gt;: low-level, explicit, good for when you want full control&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ksk001100&#x2F;seahorse&quot;&gt;seahorse&lt;&#x2F;a&gt; : what Akira used, builder pattern, minimal&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Manual approaches:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;std::env::args() directly: no dependency, just iterate and match strings yourself&lt;&#x2F;li&gt;
&lt;li&gt;std::env::args_os(): same but keeps OsString, useful when arguments may not be valid UTF-8&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Other:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;TeXitoi&#x2F;structopt&quot;&gt;structopt&lt;&#x2F;a&gt;: older derive-based approach, now largely superseded by clap v3+&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For malware authors, the interesting trade-off is binary size and recognisability. clap produces very identifiable strings and patterns in the binary. seahorse or pico-args leave a much smaller and less distinctive footprint, which may be part of why it was chosen here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-57&quot;&gt;2. Windows vs Linux: Building the Vec&lt;OsString&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The two implementations are fundamentally different because Windows and Unix have different models for how arguments reach a process.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-58&quot;&gt;The core difference&lt;&#x2F;h3&gt;
&lt;p&gt;On Linux, the kernel passes arguments as a char** argv array: each argument is already a separate null-terminated byte string. The PAL just reads them one by one.&lt;&#x2F;p&gt;
&lt;p&gt;On Windows, there is no argv array. The Win32 API delivers the entire command line as a &lt;strong&gt;single UTF-16 string&lt;&#x2F;strong&gt; via GetCommandLineW(). The PAL has to parse that string itself to split it into individual arguments.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-59&quot;&gt;What the Windows args() does&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Source code is&lt;&#x2F;em&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;ae4c6b66402cdbda8daa626216efd94e2dbe94b2&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;windows&#x2F;args.rs#L21&quot;&gt;&lt;em&gt;here&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; &lt;em&gt;(line 21), path is&lt;&#x2F;em&gt; Rust&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;windows&#x2F;args.rs#L21&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn args() -&amp;gt; Args {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; SAFETY: `GetCommandLineW` returns a pointer to a null terminated UTF-16
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; string so it&amp;#39;s safe for `WStrUnits` to use.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    unsafe {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let lp_cmd_line = c::GetCommandLineW();           &#x2F;&#x2F; (1)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let parsed_args_list = parse_lp_cmd_line(         &#x2F;&#x2F; (2)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            WStrUnits::new(lp_cmd_line),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            || current_exe()...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        );
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        Args { parsed_args_list: parsed_args_list.into_iter() }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;(1)&lt;&#x2F;strong&gt; GetCommandLineW() is a Win32 API call returning a pointer to a null-terminated UTF-16 string. The entire command line is one blob, e.g. myapp.exe --threads 8 --stopvm.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;(2)&lt;&#x2F;strong&gt; parse_lp_cmd_line is a hand-written parser implementing the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.microsoft.com&#x2F;en-us&#x2F;cpp&#x2F;cpp&#x2F;main-function-command-line-args&quot;&gt;Microsoft C&#x2F;C++ argument parsing rules&lt;&#x2F;a&gt;. It handles:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Whitespace as argument separators&lt;&#x2F;li&gt;
&lt;li&gt;Quoted strings (space inside quotes is literal)&lt;&#x2F;li&gt;
&lt;li&gt;Backslash escaping before quotes (odd number of backslashes escapes the quote)&lt;&#x2F;li&gt;
&lt;li&gt;Double-quote escaping inside a quoted string&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each parsed token is pushed as OsString::from_wide(&amp;amp;cur[..]), converting UTF-16 code units into a &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;bd0826a4521a845f36cce1b00e1dd2918ba09e90&#x2F;library&#x2F;std&#x2F;src&#x2F;sys_common&#x2F;wtf8.rs#L133&quot;&gt;WTF-8&lt;&#x2F;a&gt; encoded OsString.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;bd0826a4521a845f36cce1b00e1dd2918ba09e90&#x2F;library&#x2F;std&#x2F;src&#x2F;sys_common&#x2F;wtf8.rs#L133&quot;&gt;Wtf8Buf&lt;&#x2F;a&gt; has exactly two fields: bytes (a Vec&lt;u8&gt;, which is 24 bytes on x64: ptr, len, cap) and is_known_utf8 (a bool, 1 byte, padded to 8 bytes by alignment). So Wtf8Buf is &lt;strong&gt;32 bytes&lt;&#x2F;strong&gt; on x64.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub struct Wtf8Buf {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    bytes: Vec&amp;lt;u8&amp;gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    is_known_utf8: bool,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;-60&quot;&gt;Step-by-step comparison table&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;akira-part-1&#x2F;37.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;-61&quot;&gt;RE implication&lt;&#x2F;h3&gt;
&lt;p&gt;The final Args struct on both platforms is identically vec::IntoIter&lt;OsString&gt;, so the iteration pattern in assembly looks the same. The difference is everything before that: on Windows you will see a call to GetCommandLineW followed by a parsing loop with quote and backslash handling, where on Linux you will see atomic loads and a simple copy loop. The 32-byte vs 24-byte OsString stride difference also means the heap buffer iteration uses stride-32 on Windows vs stride-24 on Linux.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-62&quot;&gt;&lt;strong&gt;References&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Check Point Research (2023) &lt;em&gt;Rust Binary Analysis Feature by Feature&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2023&#x2F;rust-binary-analysis-feature-by-feature&#x2F;&quot;&gt;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2023&#x2F;rust-binary-analysis-feature-by-feature&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Check Point Research (2024) &lt;em&gt;Inside Akira Ransomware’s Rust Experiment&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&quot;&gt;https:&#x2F;&#x2F;research.checkpoint.com&#x2F;2024&#x2F;inside-akira-ransomwares-rust-experiment&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Cisco Talos (2024) &lt;em&gt;Akira Ransomware Continues to Evolve&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.talosintelligence.com&#x2F;akira-ransomware-continues-to-evolve&#x2F;&quot;&gt;https:&#x2F;&#x2F;blog.talosintelligence.com&#x2F;akira-ransomware-continues-to-evolve&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;console-rs (no date) &lt;em&gt;indicatif&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;console-rs&#x2F;indicatif&#x2F;blob&#x2F;0c037edc86449d84aa457d7d5db80b4166c18d6b&#x2F;examples&#x2F;yarnish.rs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;ksk001100 (no date) &lt;em&gt;seahorse&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ksk001100&#x2F;seahorse&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;ksk001100&#x2F;seahorse&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Rust Project (no date) &lt;em&gt;std::vec::IntoIter source&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;src&#x2F;alloc&#x2F;vec&#x2F;into_iter.rs.html#45-48&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;src&#x2F;alloc&#x2F;vec&#x2F;into_iter.rs.html#45-48&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Rust Project (no date) &lt;em&gt;sys&#x2F;pal&#x2F;unix&#x2F;args.rs&lt;&#x2F;em&gt;. Available at:&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;79e9716c980570bfd1f666e3b16ac583f0168962&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;unix&#x2F;args.rs&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;79e9716c980570bfd1f666e3b16ac583f0168962&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;unix&#x2F;args.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Sapin, S. (no date) &lt;em&gt;WTF-8: A superset of UTF-8&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;simonsapin.github.io&#x2F;wtf-8&#x2F;&quot;&gt;https:&#x2F;&#x2F;simonsapin.github.io&#x2F;wtf-8&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Wikipedia (no date) &lt;em&gt;cgroups&lt;&#x2F;em&gt;. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Cgroups&quot;&gt;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Cgroups&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Vector 35 (no date) &lt;em&gt;Signature libraries&lt;&#x2F;em&gt;. Binary Ninja. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;binary.ninja&#x2F;2020&#x2F;03&#x2F;11&#x2F;signature-libraries.html&quot;&gt;https:&#x2F;&#x2F;binary.ninja&#x2F;2020&#x2F;03&#x2F;11&#x2F;signature-libraries.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Microsoft Security (no date) &lt;em&gt;Unveiling RIFT: Enhancing Rust malware analysis through pattern matching&lt;&#x2F;em&gt;. Microsoft Security Blog. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;security&#x2F;blog&#x2F;2025&#x2F;06&#x2F;27&#x2F;unveiling-rift-enhancing-rust-malware-analysis-through-pattern-matching&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;security&#x2F;blog&#x2F;2025&#x2F;06&#x2F;27&#x2F;unveiling-rift-enhancing-rust-malware-analysis-through-pattern-matching&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Vector 35 (no date) &lt;em&gt;binaryninja.demangle — Binary Ninja API documentation&lt;&#x2F;em&gt;. Binary Ninja. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;api.binary.ninja&#x2F;_modules&#x2F;binaryninja&#x2F;demangle.html&quot;&gt;https:&#x2F;&#x2F;api.binary.ninja&#x2F;_modules&#x2F;binaryninja&#x2F;demangle.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Vector 35 (no date) &lt;em&gt;binaryninja::demangle — Binary Ninja Rust API&lt;&#x2F;em&gt;. Binary Ninja. Available at: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;rust.binary.ninja&#x2F;binaryninja&#x2F;demangle&#x2F;index.html&quot;&gt;https:&#x2F;&#x2F;rust.binary.ninja&#x2F;binaryninja&#x2F;demangle&#x2F;index.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Voldemort Type in Rust, C&#x2F;C++, Zig, Go and Haskell</title>
        <published>2025-11-02T00:00:00+00:00</published>
        <updated>2025-11-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yenwang.dev/blog/medium-voldemort-type/"/>
        <id>https://yenwang.dev/blog/medium-voldemort-type/</id>
        
        <content type="html" xml:base="https://yenwang.dev/blog/medium-voldemort-type/">&lt;p&gt;Based on the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wiktionary.org&#x2F;wiki&#x2F;Voldemort_type&quot;&gt;Wiktionary&lt;&#x2F;a&gt; page, a Voldemort type is a programming concept named after the villain in Harry Potter, who “must not be named.” It refers to a data type that has no directly speakable name, meaning it cannot be explicitly named or referenced outside of its defining scope, but can still be used through type inference or other indirect means.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-key-characteristics-of-voldemort-types-are&quot;&gt;The key characteristics of Voldemort types are:&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;They are defined within a function or local scope&lt;&#x2F;li&gt;
&lt;li&gt;They cannot be directly named outside that scope&lt;&#x2F;li&gt;
&lt;li&gt;They can still be returned and used through type inference&lt;&#x2F;li&gt;
&lt;li&gt;They’re useful for encapsulation and creating types that exist only for specific operations&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;rust&quot;&gt;Rust&lt;&#x2F;h2&gt;
&lt;p&gt;Voldemort Types in Rust:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A Voldemort type is a type the caller is prohibited from naming, either because no syntax exists to express it (closures), or because the API deliberately seals it behind impl Trait in return position.&lt;&#x2F;li&gt;
&lt;li&gt;In both cases the effect on the caller is the same: you cannot annotate, store, or depend on the concrete type directly.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;closure-type&quot;&gt;Closure Type&lt;&#x2F;h3&gt;
&lt;p&gt;The closure &lt;code&gt;move |y| x + y&lt;&#x2F;code&gt; has a type the caller cannot write. &lt;code&gt;impl Fn(i32) -&amp;gt; i32&lt;&#x2F;code&gt; is the only way to return it without boxing.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Link: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;types&#x2F;closure.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;types&#x2F;closure.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn create_adder(x: i32) -&amp;gt; impl Fn(i32) -&amp;gt; i32 {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    move |y| x + y
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn main() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let adder = create_adder(5);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;{}&amp;quot;, adder(3)); &#x2F;&#x2F; 8
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;impl-trait-in-return-position&quot;&gt;impl Trait in Return Position&lt;&#x2F;h3&gt;
&lt;p&gt;When a function returns &lt;code&gt;impl Trait&lt;&#x2F;code&gt;, the concrete type is sealed from the caller by design.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn make_greeting() -&amp;gt; impl Display {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    String::from(&amp;quot;hello&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The caller knows they have something that implements &lt;code&gt;Display&lt;&#x2F;code&gt;. They cannot name the concrete type &lt;code&gt;String&lt;&#x2F;code&gt; from the return type alone.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;types&#x2F;impl-trait.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;types&#x2F;impl-trait.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;zig&quot;&gt;Zig&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;const std = @import(&amp;quot;std&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 1: Function returning anonymous struct
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn createPoint(x: f32, y: f32) @TypeOf(.{ .x = x, .y = y }) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return .{ .x = x, .y = y };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 2: Generic function with unnamed type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn createContainer(comptime T: type, value: T) struct { data: T } {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return struct { data: T }{ .data = value };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 3: Function returning anonymous enum
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn createStatus() @TypeOf(enum { ok, error }) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    const Status = enum { ok, error };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return Status.ok;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pub fn main() void {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    const point = createPoint(1.0, 2.0);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    std.debug.print(&amp;quot;Point: ({}, {})\n&amp;quot;, .{ point.x, point.y });
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    const container = createContainer(i32, 42);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    std.debug.print(&amp;quot;Container data: {}\n&amp;quot;, .{container.data});
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;c&quot;&gt;C&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; C doesn&amp;#39;t have true Voldemort types, but we can simulate with static functions
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; and function pointers
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#include &amp;lt;stdlib.h&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 1: Function returning function pointer to static function
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;typedef int (*operation_t)(int, int);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;operation_t create_adder() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; This static function is &amp;quot;unnamed&amp;quot; from outside perspective
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    static int add_impl(int a, int b) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        return a + b;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return add_impl;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 2: Macro creating unnamed struct type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#define CREATE_POINT(x, y) \
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    (struct { int x, y; }){ .x = (x), .y = (y) }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 3: Function with local struct definition
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Note: Caller must free the returned pointer
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;typedef struct container_t container_t;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;void* create_data_container(int value) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    struct container {  &#x2F;&#x2F; This struct type is local and unnamed outside
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        int data;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        void (*print)(void*);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    static void print_container(void* ptr) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        struct container* c = (struct container*)ptr;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        printf(&amp;quot;Data: %d\n&amp;quot;, c-&amp;gt;data);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    struct container* c = malloc(sizeof(struct container));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if (c == NULL) return NULL;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    c-&amp;gt;data = value;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    c-&amp;gt;print = print_container;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return c;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;int main() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    operation_t op = create_adder();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    printf(&amp;quot;Result: %d\n&amp;quot;, op(5, 3));
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    auto point = CREATE_POINT(10, 20);  &#x2F;&#x2F; Type cannot be named
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    printf(&amp;quot;Point: (%d, %d)\n&amp;quot;, point.x, point.y);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Example 3 with proper cleanup
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    void* container = create_data_container(42);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if (container != NULL) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &#x2F;&#x2F; Access through function pointer stored in the container
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &#x2F;&#x2F; Note: In real code, you&amp;#39;d need a way to safely call methods
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        free(container);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return 0;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;c-1&quot;&gt;C++&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#include &amp;lt;iostream&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;#include &amp;lt;functional&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 1: Lambda with unnamed type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;auto create_multiplier(int factor) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return [factor](int x) { return x * factor; };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Lambda type cannot be named directly
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 2: Function returning auto with complex type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;auto create_complex_operation() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return [](auto x, auto y) -&amp;gt; decltype(x + y) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        return x * 2 + y * 3;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 3: Template function with unnamed return type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;template&amp;lt;typename T&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;auto create_wrapper(T value) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    struct wrapper {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        T data;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        void print() const { std::cout &amp;lt;&amp;lt; &amp;quot;Value: &amp;quot; &amp;lt;&amp;lt; data &amp;lt;&amp;lt; std::endl; }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return wrapper{value};
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 4: Function returning unnamed iterator type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;auto create_filtered_range(const std::vector&amp;lt;int&amp;gt;&amp;amp; vec) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return vec | std::views::filter([](int x) { return x &amp;gt; 0; })
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              | std::views::transform([](int x) { return x * 2; });
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;int main() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    auto mult = create_multiplier(3);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    std::cout &amp;lt;&amp;lt; mult(4) &amp;lt;&amp;lt; std::endl; &#x2F;&#x2F; 12
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    auto op = create_complex_operation();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    std::cout &amp;lt;&amp;lt; op(2, 3) &amp;lt;&amp;lt; std::endl; &#x2F;&#x2F; 13
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    auto wrapper = create_wrapper(42);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    wrapper.print();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return 0;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;go&quot;&gt;Go&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;package main
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;import &amp;quot;fmt&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 1: Function returning anonymous struct
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;func createPoint(x, y float64) struct{ X, Y float64 } {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return struct{ X, Y float64 }{x, y}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 2: Function returning closure
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;func createPrinter(msg string) func() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return func() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        fmt.Println(msg)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Add Print method to the anonymous struct
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;func (p struct{ message string }) Print() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    fmt.Println(p.message)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 3: Function returning channel of anonymous type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;func createDataChannel() &amp;lt;-chan struct{ ID int; Name string } {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ch := make(chan struct{ ID int; Name string }, 1)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ch &amp;lt;- struct{ ID int; Name string }{1, &amp;quot;test&amp;quot;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    close(ch)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return ch
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Example 4: Function with local type definition
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;func createProcessor() func(int) int {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    type processor struct {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        multiplier int
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    p := processor{multiplier: 2}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return func(x int) int {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        return x * p.multiplier
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;func main() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    point := createPoint(1.5, 2.5)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    fmt.Printf(&amp;quot;Point: (%.1f, %.1f)\n&amp;quot;, point.X, point.Y)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    printer := createPrinter(&amp;quot;Hello, Voldemort!&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    printer() &#x2F;&#x2F; Call the closure
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ch := createDataChannel()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    data := &amp;lt;-ch
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    fmt.Printf(&amp;quot;Data: ID=%d, Name=%s\n&amp;quot;, data.ID, data.Name)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    proc := createProcessor()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    fmt.Printf(&amp;quot;Processed: %d\n&amp;quot;, proc(5))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;haskell&quot;&gt;Haskell&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;-- Example 1: Function with local data type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;createCounter :: Int -&amp;gt; (Int -&amp;gt; Int, Int -&amp;gt; Int)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;createCounter initial = (increment, decrement)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  where
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    -- Local data type that cannot be named outside
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    data Counter = Counter Int
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    increment x = case Counter (initial + x) of
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      Counter n -&amp;gt; n
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    decrement x = case Counter (initial - x) of
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      Counter n -&amp;gt; n
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;-- Example 2: Function returning existential type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;{-# LANGUAGE ExistentialQuantification #-}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;data SomeShow = forall a. Show a =&amp;gt; SomeShow a
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;createShowable :: Int -&amp;gt; SomeShow
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;createShowable x = SomeShow (LocalType x)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  where
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    -- This type exists only in this scope
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    data LocalType = LocalType Int
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    instance Show LocalType where
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      show (LocalType n) = &amp;quot;LocalType &amp;quot; ++ show n
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;-- Example 3: Function with local newtype
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;createProcessor :: String -&amp;gt; (String -&amp;gt; String)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;createProcessor prefix = process
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  where
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    -- Local newtype that cannot be accessed outside
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    newtype ProcessedString = PS String
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    process :: String -&amp;gt; String
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    process s = case PS (prefix ++ s) of
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      PS result -&amp;gt; result
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;-- Example 4: Rank-2 polymorphism creating unnamed types
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;{-# LANGUAGE RankNTypes #-}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;createApplier :: (forall a. Show a =&amp;gt; a -&amp;gt; String) -&amp;gt; Int -&amp;gt; String
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;createApplier f x = f (LocalInt x)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  where
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    -- Local data type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    data LocalInt = LocalInt Int
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    instance Show LocalInt where
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      show (LocalInt n) = &amp;quot;Local: &amp;quot; ++ show n
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;-- Example 5: Function returning record with unnamed type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;-- Example 5: Function returning tuple with operations on unnamed type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;createRecord :: Int -&amp;gt; (Int, Int -&amp;gt; Int)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;createRecord x = (getValue, transform)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  where
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    data LocalData = LocalData Int
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    extract (LocalData n) = n
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    localData = LocalData x
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    getValue = extract localData
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    transform y = extract (LocalData (x + y))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;main :: IO ()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;main = do
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  let (inc, dec) = createCounter 10
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  print $ inc 5  -- 15
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  print $ dec 3  -- 7
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  let showable = createShowable 42
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  case showable of
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    SomeShow x -&amp;gt; print x
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  let processor = createProcessor &amp;quot;Prefix: &amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  putStrLn $ processor &amp;quot;test&amp;quot;  -- &amp;quot;Prefix: test&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  let applier = createApplier show
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  putStrLn $ applier 100
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  let (value, transform) = createRecord 10
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  print value              -- 10
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  print $ transform 5      -- 15
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;key-benefits-of-voldemort-types&quot;&gt;Key Benefits of Voldemort Types:&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Encapsulation&lt;&#x2F;strong&gt;: The type cannot be constructed or manipulated outside its defining scope&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Information Hiding&lt;&#x2F;strong&gt;: Implementation details are completely hidden&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Type Safety&lt;&#x2F;strong&gt;: The compiler can still perform type checking even though the type is unnamed&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;&#x2F;strong&gt;: Often allows for better optimization since the type’s usage is constrained&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;API Design&lt;&#x2F;strong&gt;: Forces users to interact with your code through specific interfaces&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Voldemort types are particularly useful in functional programming and when designing APIs where you want to provide functionality without exposing implementation details.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Rust Syntax Sugars That Make Your Code Sweeter</title>
        <published>2025-08-26T00:00:00+00:00</published>
        <updated>2025-08-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://yenwang.dev/blog/medium-rust-syntax-sugars/"/>
        <id>https://yenwang.dev/blog/medium-rust-syntax-sugars/</id>
        
        <content type="html" xml:base="https://yenwang.dev/blog/medium-rust-syntax-sugars/">&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blog&#x2F;rust-syntax-sugars&#x2F;01.webp&quot; alt=&quot;Understanding the magic behind Rust’s concise and readable syntax&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;caption&quot;&gt;Understanding the magic behind Rust’s concise and readable syntax&lt;&#x2F;p&gt;
&lt;p&gt;If you’ve been writing Rust for a while, you’ve probably noticed how elegant and concise the language can be. Much of this elegance comes from &lt;strong&gt;syntax sugar&lt;&#x2F;strong&gt; — convenient shorthand that the compiler automatically transforms into more verbose code behind the scenes.&lt;&#x2F;p&gt;
&lt;p&gt;Today, we’ll explore 15 essential Rust syntax sugars that make your code more readable and maintainable. For each one, I’ll show you both the “sweet” version you write and the “de-sugared” version the compiler sees.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;&quot;&gt;1. Method Call Syntax: The Foundation of Ergonomic APIs&lt;&#x2F;h2&gt;
&lt;p&gt;This is probably the most fundamental syntax sugar in Rust:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let mut v = vec![1, 2, 3];
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;v.push(4);                    &#x2F;&#x2F; Sweet syntax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Vec::push(&amp;amp;mut v, 4);         &#x2F;&#x2F; What the compiler sees
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The method call syntax automatically handles borrowing and makes APIs much more intuitive. Instead of thinking about which type implements a method, you can just call it on your value.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-1&quot;&gt;2. Auto-dereferencing: Smart Pointer Magic&lt;&#x2F;h2&gt;
&lt;p&gt;Rust automatically dereferences types when calling methods or accessing fields:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let s = String::from(&amp;quot;hello&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let r = &amp;amp;s;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let len = r.len();            &#x2F;&#x2F; Automatic dereferencing
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let len = (*r).len();         &#x2F;&#x2F; Manual dereferencing
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This works with smart pointers too — &lt;code&gt;Box&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;, &lt;code&gt;Rc&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;, and &lt;code&gt;Arc&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; all benefit from this sugar.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-2&quot;&gt;3. Range Syntax: Elegant Iteration&lt;&#x2F;h2&gt;
&lt;p&gt;Ranges are everywhere in Rust, and the syntax is beautifully concise:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;for i in 0..10 {}             &#x2F;&#x2F; Exclusive range
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;for i in 0..=10 {}            &#x2F;&#x2F; Inclusive range
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let slice = &amp;amp;arr[1..4];       &#x2F;&#x2F; Slicing
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Desugared:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;for i in std::ops::Range { start: 0, end: 10 } {}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-3&quot;&gt;4. Pattern Matching Shortcuts: if let and while let&lt;&#x2F;h2&gt;
&lt;p&gt;These constructs let you pattern match without the verbosity of full &lt;code&gt;match&lt;&#x2F;code&gt; statements:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Concise option handling
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if let Some(x) = maybe_value {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;Got: {}&amp;quot;, x);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Desugared equivalent:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;match maybe_value {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Some(x) =&amp;gt; println!(&amp;quot;Got: {}&amp;quot;, x),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    _ =&amp;gt; {}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Great for loops with iterators
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;while let Some(item) = iterator.next() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    process(item);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-4&quot;&gt;5. The ? Operator: Error Handling Made Easy&lt;&#x2F;h2&gt;
&lt;p&gt;This might be my favorite syntax sugar. It transforms error handling from verbose to elegant:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn read_config() -&amp;gt; Result&amp;lt;Config, Error&amp;gt; {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let content = fs::read_to_string(&amp;quot;config.toml&amp;quot;)?;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let config = toml::from_str(&amp;amp;content)?;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Ok(config)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Without the ? operator:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn read_config() -&amp;gt; Result&amp;lt;Config, Error&amp;gt; {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let content = match fs::read_to_string(&amp;quot;config.toml&amp;quot;) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        Ok(content) =&amp;gt; content,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        Err(e) =&amp;gt; return Err(e),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let config = match toml::from_str(&amp;amp;content) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        Ok(config) =&amp;gt; config,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        Err(e) =&amp;gt; return Err(e),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Ok(config)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-5&quot;&gt;6. Closure Syntax: Functional Programming Bliss&lt;&#x2F;h2&gt;
&lt;p&gt;Closures in Rust have incredibly flexible syntax:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let numbers = vec![1, 2, 3, 4, 5];
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Minimal syntax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let doubled: Vec&amp;lt;i32&amp;gt; = numbers.iter().map(|x| x * 2).collect();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; More explicit (but still sugar!)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let doubled: Vec&amp;lt;i32&amp;gt; = numbers.iter().map(|x: &amp;amp;i32| -&amp;gt; i32 { x * 2 }).collect()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-6&quot;&gt;7. for Loops: Iterator Protocol Under the Hood&lt;&#x2F;h2&gt;
&lt;p&gt;The humble &lt;code&gt;for&lt;&#x2F;code&gt; loop is actually quite sophisticated:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;for item in collection {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;{}&amp;quot;, item);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Desugared:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let mut iter = IntoIterator::into_iter(collection);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;loop {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    match Iterator::next(&amp;amp;mut iter) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        Some(item) =&amp;gt; println!(&amp;quot;{}&amp;quot;, item),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        None =&amp;gt; break,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-7&quot;&gt;8. Struct Update Syntax: DRY for Data Structures&lt;&#x2F;h2&gt;
&lt;p&gt;When you need to create a struct that’s mostly like another one:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;struct Config {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    host: String,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    port: u16,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ssl: bool,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    timeout: u64,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let prod_config = Config {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    host: &amp;quot;prod.example.com&amp;quot;.to_string(),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    port: 443,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ssl: true,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    timeout: 30,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;};
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let dev_config = Config {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    host: &amp;quot;localhost&amp;quot;.to_string(),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    port: 8080,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ssl: false,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ..prod_config  &#x2F;&#x2F; Copy the rest!
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-8&quot;&gt;9. Advanced Pattern Matching&lt;&#x2F;h2&gt;
&lt;p&gt;Pattern matching has several sweet shortcuts:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;match temperature {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    t if t &amp;gt; 30 =&amp;gt; println!(&amp;quot;Hot!&amp;quot;),        &#x2F;&#x2F; Guards
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    20..=25 =&amp;gt; println!(&amp;quot;Perfect&amp;quot;),         &#x2F;&#x2F; Range patterns
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    x @ 10..=19 =&amp;gt; println!(&amp;quot;Cool: {}&amp;quot;, x), &#x2F;&#x2F; Binding
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    _ =&amp;gt; println!(&amp;quot;Extreme&amp;quot;),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-9&quot;&gt;10. Deref Coercion: Seamless Type Conversion&lt;&#x2F;h2&gt;
&lt;p&gt;This happens automatically and makes working with different string types seamless:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn print_str(s: &amp;amp;str) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;{}&amp;quot;, s);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let owned = String::from(&amp;quot;Hello&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;print_str(&amp;amp;owned);        &#x2F;&#x2F; &amp;amp;String automatically becomes &amp;amp;str
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-10&quot;&gt;11. Implicit Returns: Functional Style&lt;&#x2F;h2&gt;
&lt;p&gt;Rust functions can return values without explicit &lt;code&gt;return&lt;&#x2F;code&gt; statements:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn add(x: i32, y: i32) -&amp;gt; i32 {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    x + y  &#x2F;&#x2F; No semicolon = implicit return
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn complex_logic(input: i32) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if input &amp;gt; 0 {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &amp;quot;positive&amp;quot;.to_string()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    } else if input &amp;lt; 0 {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &amp;quot;negative&amp;quot;.to_string()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    } else {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &amp;quot;zero&amp;quot;.to_string()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }  &#x2F;&#x2F; Each branch returns a value
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-11&quot;&gt;12. Destructuring: Unpack All the Things&lt;&#x2F;h2&gt;
&lt;p&gt;Destructuring works with tuples, structs, and enums:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Tuples
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let (x, y, z) = (1, 2, 3);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Structs
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;struct Point { x: i32, y: i32 }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let Point { x, y } = point;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; In function parameters
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn distance((x1, y1): (f64, f64), (x2, y2): (f64, f64)) -&amp;gt; f64 {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    ((x2 - x1).powi(2) + (y2 - y1).powi(2)).sqrt()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-12&quot;&gt;13. Collection Macros: Convenient Initialisation&lt;&#x2F;h2&gt;
&lt;p&gt;Option 1:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;[dependencies]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;maplit = &amp;quot;1.0&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;use std::collections::HashMap;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;use maplit::hashmap;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn main() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let v = vec![1, 2, 3, 4, 5];
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let map = hashmap!{
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &amp;quot;key1&amp;quot; =&amp;gt; &amp;quot;value1&amp;quot;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &amp;quot;key2&amp;quot; =&amp;gt; &amp;quot;value2&amp;quot;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    };
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;Vector: {:?}&amp;quot;, v);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;HashMap: {:?}&amp;quot;, map);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Option 2:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;use std::collections::HashMap;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn main() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Using vec! macro
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let v = vec![1, 2, 3, 4, 5];
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Using hashmap! macro (requires maplit crate)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; Or manual HashMap creation:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let mut map = HashMap::new();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    map.insert(&amp;quot;key1&amp;quot;, &amp;quot;value1&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    map.insert(&amp;quot;key2&amp;quot;, &amp;quot;value2&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;Vector: {:?}&amp;quot;, v);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;HashMap: {:?}&amp;quot;, map);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; vec! macro desugars to:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let mut v_desugared = Vec::new();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    v_desugared.push(1);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    v_desugared.push(2);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    v_desugared.push(3);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    v_desugared.push(4);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    v_desugared.push(5);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;Desugared vector: {:?}&amp;quot;, v_desugared);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; HashMap creation desugars to:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let mut map_desugared: HashMap&amp;lt;&amp;amp;str, &amp;amp;str&amp;gt; = HashMap::new();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    map_desugared.insert(&amp;quot;key1&amp;quot;, &amp;quot;value1&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    map_desugared.insert(&amp;quot;key2&amp;quot;, &amp;quot;value2&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    println!(&amp;quot;Desugared HashMap: {:?}&amp;quot;, map_desugared);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-13&quot;&gt;14. String Formatting: Powerful and Flexible&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let name = &amp;quot;Alice&amp;quot;;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let age = 30;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Multiple ways to format strings
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let msg1 = format!(&amp;quot;Hello, {}!&amp;quot;, name);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let msg2 = format!(&amp;quot;Hello, {name}!&amp;quot;);  &#x2F;&#x2F; Named parameters
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let msg3 = format!(&amp;quot;Hello, {name}! You are {age} years old.&amp;quot;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-14&quot;&gt;15. Operator Overloading: Mathematical Elegance&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;use std::ops::Add;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let result = a + b + c;       &#x2F;&#x2F; Sweet and mathematical
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F; Desugared:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;let result = Add::add(Add::add(a, b), c);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;-15&quot;&gt;Why Syntax Sugar Matters&lt;&#x2F;h2&gt;
&lt;p&gt;These syntax sugars aren’t just about making code shorter — they’re about making code more &lt;strong&gt;expressive&lt;&#x2F;strong&gt; and &lt;strong&gt;maintainable&lt;&#x2F;strong&gt;. They allow you to write code that clearly communicates intent while the compiler handles the mechanical details.&lt;&#x2F;p&gt;
&lt;p&gt;Understanding what’s happening under the hood helps you:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Debug more effectively&lt;&#x2F;strong&gt; when things go wrong&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Write better APIs&lt;&#x2F;strong&gt; that feel natural to use&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Understand compiler errors&lt;&#x2F;strong&gt; that reference the desugared forms&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Appreciate Rust’s design&lt;&#x2F;strong&gt; philosophy of zero-cost abstractions&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;-16&quot;&gt;Wrapping Up&lt;&#x2F;h2&gt;
&lt;p&gt;Rust’s syntax sugar strikes an excellent balance between conciseness and explicitness. Each piece of sugar we’ve explored makes common patterns more ergonomic while maintaining Rust’s core principles of safety and performance.&lt;&#x2F;p&gt;
&lt;p&gt;The next time you write &lt;code&gt;vec.push(item)&lt;&#x2F;code&gt; instead of &lt;code&gt;Vec::push(&amp;amp;mut vec, item)&lt;&#x2F;code&gt;, take a moment to appreciate the thoughtful design that makes Rust both powerful and pleasant to use.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
