<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[ping's blog]]></title><description><![CDATA[Programming and stuff]]></description><link>https://blog.tst.sh/</link><image><url>https://blog.tst.sh/favicon.png</url><title>ping&apos;s blog</title><link>https://blog.tst.sh/</link></image><generator>Ghost 5.42</generator><lastBuildDate>Sun, 07 Dec 2025 04:19:07 GMT</lastBuildDate><atom:link href="https://blog.tst.sh/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[New stuff at notes.tst.sh]]></title><description><![CDATA[<p>I&apos;ve been wanting to share my thoughts more but in a format that is a little less bulky, over the weekend I set up <a href="https://notes.tst.sh/?ref=blog.tst.sh">notes.tst.sh</a> which uses <a href="https://obsidian-publisher.netlify.app/?ref=blog.tst.sh">Obsidian Mkdocs Publisher</a> to give you a better idea of what I&apos;ve been up to.</p><p>Writing in</p>]]></description><link>https://blog.tst.sh/notes/</link><guid isPermaLink="false">64482f158b863a50d7bdeba0</guid><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Tue, 25 Apr 2023 19:59:38 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2023/04/Screenshot_20230425_160057.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.tst.sh/content/images/2023/04/Screenshot_20230425_160057.png" alt="New stuff at notes.tst.sh"><p>I&apos;ve been wanting to share my thoughts more but in a format that is a little less bulky, over the weekend I set up <a href="https://notes.tst.sh/?ref=blog.tst.sh">notes.tst.sh</a> which uses <a href="https://obsidian-publisher.netlify.app/?ref=blog.tst.sh">Obsidian Mkdocs Publisher</a> to give you a better idea of what I&apos;ve been up to.</p><p>Writing in a cross-platform markdown editor is way nicer than keeping a bunch of drafts in Ghost, and much easier to organize. If it goes well I might even move all of my old blog posts over and set up redirects, stay tuned!</p>]]></content:encoded></item><item><title><![CDATA[Reverse engineering Flutter apps (Part 2)]]></title><description><![CDATA[As you have probably guessed so far, reverse engineering its not an easy task.]]></description><link>https://blog.tst.sh/reverse-engineering-flutter-apps-part-2/</link><guid isPermaLink="false">5fa9b817c9aac25a0711725a</guid><category><![CDATA[ARM]]></category><category><![CDATA[Assembly]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Low Level]]></category><category><![CDATA[Mobile Dev]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Wed, 24 Feb 2021 16:14:12 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2021/01/re.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.tst.sh/content/images/2021/01/re.png" alt="Reverse engineering Flutter apps (Part 2)"><p>This is a continuation of <a href="https://blog.tst.sh/reverse-engineering-flutter-apps-part-1/">Part 1 </a>which covered how Flutter compiles apps and what snapshots look like internally.</p><p>As you have probably guessed so far, reverse engineering its not an easy task.</p><hr><h3 id="calling-conventions">Calling conventions</h3><p></p><p>Let&apos;s first cover some basics about Dart&apos;s type system:</p><pre><code class="language-dart">void main() {
  void foo() {}
  int bar([int aaa]) {}
  Null biz({int aaa}) {}
  int baz(int aa, {int aaa}) {}
  
  print(foo is void Function());
  print(bar is void Function());
  print(biz is void Function());
  print(baz is void Function());
}</code></pre><p>Which functions do you think print true?</p><p>It turns out the Dart type system is much more flexible than you might expect, as long as a function takes the same positional arguments and has compatible return type it is a valid function subtype. Because of this, all but the <code>baz</code> print true.</p><p>Here&apos;s another experiment:</p><pre><code class="language-dart">void main() {
  int foo({int a}) {}
  int bar({int a, int b}) {}
  
  print(foo is int Function());
  print(foo is int Function({int a}));
  print(bar is int Function({int a}));
  print(bar is int Function({int b}));
  print(bar is int Function({int b, int c}));
}
</code></pre><p>Here we check if functions have a valid subtype when they have a subset of named arguments, all but the last prints true.</p><p>For a formal description of function types, see <em>&quot;9.3 Type of a Function&quot;</em> in the <a href="https://dart.dev/guides/language/specifications/DartLangSpec-v2.2.pdf?ref=blog.tst.sh">Dart language specification</a>.</p><p>Mixing and matching parameter signatures are a nice feature but pose some problems when implementing them at a low level, for example:</p><pre><code class="language-dart">void main() {
  void Function({int a, int c}) foo;
  
  foo = ({int a, int b, int c}) {
    print(&quot;Hi $a $b $c&quot;);
  };
  
  foo(a: 1, c: 2);
}
</code></pre><p>In order for this to work, <code>foo</code> needs some way of knowing the caller provided <code>a</code> and <code>c</code> but not <code>b</code>, this piece of information is called an argument descriptor.</p><p>Internally argument descriptors are defined by <code>vm/dart_entry.h</code>. The implementation is just an interface over a regular Array object which the callee provides via the argument descriptor register.</p><p>For example:</p><pre><code class="language-dart">void bar({int a}) {
  print(&quot;Hi $a&quot;);
}

void foo() {
  bar(a: 42);
}</code></pre><p>Rather than using Dart&apos;s built-in disassembler I&apos;ll be using a custom one that provides proper annotations for calls, object pool entries, and other constants.</p><p>Disassembly of <code>foo</code>, the caller:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
034 | ...
038 | mov ip, #0x54        // 
03c | str ip, [sp, #-4]!   // push smi 42
040 | add r4, pp, #0x2000  //
044 | ldr r4, [r4, #0x4a3] // load the argument descriptor into r4
048 | bl 0x1b8             // call bar
04C | ...</code></pre><figcaption>foo</figcaption></figure><p>The argument descriptor for the call to bar is the following RawArray:</p><figure class="kg-card kg-code-card"><pre><code class="language-dart">[
  0, // type arguments length
  1, // argument count
  0, // positional arg count
  
  // named arguments (name, position)
  &quot;x&quot;, 0,
  
  null, // null terminator
]</code></pre><figcaption>r4</figcaption></figure><p>The descriptor is used in the prologue of the callee to map stack indices to their respective argument slots and verify the proper arguments were received. Here is the disassembly of the callee:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
// prologue, polymorphic entry

000 | stmdb sp!, {fp, lr}
004 | add fp, sp, #0
008 | sub sp, sp, #4

// optional parameter handling

00c | ldr r0, [r4, #0x13] // arr[2] (positional arg count)
010 | ldr r1, [r4, #0xf]  // arr[1] (argument count)
014 | cmp r0, #0          // check if we have positional args
018 | bgt 0x74            // jump to 08c

// check named args

01c | ldr r0, [r4, #0x17]  // arr[3] (first arg name)
020 | add ip, pp, #0x2000  // 
024 | ldr ip, [ip, #0x4a7] // string &quot;x&quot;
028 | cmp r0, ip           // check if arg present
02c | bne 0x20             // jump to 04c

030 | ldr r0, [r4, #0x1b]    // arr[4] (first arg position)
034 | sub r2, r1, r0         // r2 = arg_count - position
038 | add r0, fp, r2, lsl #1 // r0 = fp + r2 * 2
    |                        // this is really r2 * 4 because it&apos;s an smi
03c | ldr r0, [r0, #4]       // read arg
040 | mov r2, r0             // 
044 | mov r0, #2             // 
048 | b 12                   // jump to 054

04c | ldr r2, [thr, #0x68] // thr-&gt;objectNull
050 | mov r0, #0           // 

054 | str r2, [fp, #-4] // store arg in local

// done loading args

058 | cmp r1, r0 // check if we have read all args
05c | bne 0x30   // jump to 08c

// continue prologe

060 | ldr ip, [thr, #0x24] // thr-&gt;stackLimit
064 | cmp sp, ip           //
068 | blls -0x5af00        // stackOverflowStubWithoutFpuRegsStub

// rest of function

06c | ...

// incompatible args path

08c | ldr r6, [pp, #0x33] // Code* callClosureNoSuchMethod
090 | sub sp, fp, #0      // 
094 | ldmia sp!, {fp, lr} // exit frame
098 | ldr pc, [r6, #3]    // invoke stub</code></pre><figcaption>bar</figcaption></figure><p>To summarize, it loops the array assigning slots to any matching arguments, throwing a NoSuchMethodError if any are not part of the function type. Also keep in mind argument checking is only required for polymorphic calls, most (including the hello world example) are monomorphic.</p><p>This code is generated at a high level in <code>vm/compiler/frontend/prologue_builder.cc</code> <code>PrologueBuilder::BuildOptionalParameterHandling</code> meaning registers and subroutines may be layed out differently depending on the types of arguments and what optimizations it feels like doing.</p><hr><h3 id="integer-arithmetic">Integer arithmetic</h3><p></p><p>The <code>num</code>, <code>int</code>, and <code>double</code> classes are special in the Dart type system, for performance reasons they cannot be extended or implemented.</p><p>Because of this restriction we never have to check the type of an int before doing arithmetic, if that wasn&apos;t the case the compiler would have to generate relatively expensive method calls instead.</p><p>All objects in dart are pointers to <code>RawObject</code> however only pointers tagged with <code>kHeapObjectTag</code> are actual heap objects, objects without the tag are signed ints shifted to the left by one.</p><p>Because of pointer tagging you will see a lot of <code>tst r0, #1</code> and similar instructions in generated code, these are for discriminating between smis and heap objects. You will also see a lot of odd-numbered offset loads and stores to subtract the heap flag.</p><p>Fun fact: The core <code>int</code> type used to be a bigint before Dart 2.0, you can find the writeup by the Dart team here: <a href="https://github.com/dart-lang/sdk/blob/2.15.1/docs/language/informal/int64.md?ref=blog.tst.sh">https://github.com/dart-lang/sdk/blob/2.15.1/docs/language/informal/int64.md</a></p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/02/smi-1.png" class="kg-image" alt="Reverse engineering Flutter apps (Part 2)" loading="lazy"></figure><p>Any integer that can fit within the word size minus one bit (31 bits on A32) can be stored as an smi, otherwise larger integers are stored as 64 bit mint (medium int) instances on the heap.</p><p>Smis can contain negative numbers too of course, it uses an arithmetic right shift to sign extend the number back into place.</p><p>For example, here is a simple function that adds two ints:</p><pre><code class="language-dart">int hello(int x, int y) =&gt; x + y;</code></pre><p>To start, <code>x</code> and <code>y</code> are each unboxed into pairs of registers, Dart ints are 64 bit so two registers are needed for each arg on A32:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
024 | ...
028 | ldr r1, [fp, #12]    // load argument x
02c | ldr ip, [thr, #0x68] // thr-&gt;objectNull
030 | cmp r1, ip           // check if x is null
034 | bleq -0x50954        // nullErrorStubWithoutFpuRegsStub
038 | ...
048 | mov r3, r1, asr #0x1f // sign-extend top half
04c | movs r4, r1, asr #1   // shift heap flag into carry
050 | bcc 12                // jump to 05c if heap flag is clear
054 | ldr r4, [r1, #7]      // load lower half from mint
058 | ldr r3, [r1, #11]     // load upper half from mint
05c | ...</code></pre><figcaption>x + y</figcaption></figure><p>After <code>x</code> and <code>y</code> are in pairs of registers it can perform the actual 64 bit add:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
070 | adds r7, r4, r6 // bottom half
074 | adcs r2, r3, r1 // carry into top half</code></pre><figcaption>x + y</figcaption></figure><p>Before returning the result gets re-boxed:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
074 | ...
078 | mov r0, r7, lsl #1      // create smi from lower half
07c | cmp r7, r0, asr #1      // check if MSB of smi isn&apos;t clobbered
080 | cmpeq r2, r0, asr #0x1f // check if upper half is empty
084 | beq 0x34                // jump to 0b8 if smi is valid
  
// construct mint
  
088 | ldr r0, [thr, #0x3c] // thr-&gt;top
08c | adds r0, r0, #0x10   // add size of mint
090 | ldr ip, [thr, #0x40] // thr-&gt;end
094 | cmp ip, r0           // check if mint fits in pool
098 | bls 0x28             // jump to 0c0 (slow path)

// construct mint in pool

09c | str r0, [thr, #0x3c] // shift down pool start
0a0 | sub r0, r0, #15      // go back to original top
0a4 | mov ip, #0x2204      // misc tags 
0a8 | movt ip, #0x31       // mint object id
0ac | str ip, [r0, #-1]    // write tags

// store values in new mint

0b0 | str r7, [r0, #7]  // write lower half
0b4 | str r2, [r0, #11] // write upper half

// function epilogue

0b8 | sub sp, fp, #0
0bc | ldmia sp!, {fp, pc}
  
// slow path, invoke mint constructor

0c0 | stmdb sp!, {r2, r7} //
0c4 | bl 0x651f4          // new dart:core::Mint_at_0150898
0c8 | ldmia sp!, {r2, r7} //
0cc | b -0x1c             // jump to 0b0</code></pre><figcaption>x + y</figcaption></figure><p>Boxing looks more expensive than it actually since the value will be returned immediately as an smi and only hits the slow code paths when the result is larger than 31 bits.</p><hr><h3 id="instances">Instances</h3><p></p><p>The code below creates an instance by calling an allocation stub followed by a call to the constructor:</p><pre><code class="language-dart">makeFoo() =&gt; Foo&lt;int&gt;();</code></pre><p>Disassembled:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
014 | ...
018 | ldr ip, [pp, #0x93] //
01c | str ip, [sp, #-4]!  // push type args &lt;int&gt;
020 | bl -0x628           // Foo allocation stub
024 | add sp, sp, #4      // pop arg
028 | str r0, [fp, #-4]   // store object in frame
02c | str r0, [sp, #-4]!  // push object as arg
030 | bl -0x9f0           // Foo::Foo()
034 | add sp, sp, #4      // pop arg
038 | ldr r0, [fp, #-4]   // load object from frame into return reg
03c | ...</code></pre><figcaption>makeFoo</figcaption></figure><p>Each class has a corresponding allocation stub that allocates and initializes an instance (very similar to how boxing creates an object), these stubs are generated for any classes that can be constructed.</p><p>Unfortunately for us, field information is removed from the snapshot so we can&apos;t directly get their names. You can however see the names of implicit getter and setter methods (assuming they haven&apos;t been inlined).</p><p>Offsets for fields are calculated at <code>Class::CalculateFieldOffsets</code>, the rules go as follows:</p><ol><li>Start at end of super class, otherwise start at <code>sizeof(RawInstance)</code></li><li>Use the type arguments field of parent, else put it at the start</li><li>Lay out remaining (non static) fields sequentially</li></ol><p>Because type arguments are shared with the super, instantiating the following class gives us a type arguments field containing <code>&lt;String, int&gt;</code>:</p><pre><code class="language-dart">class Foo&lt;T&gt; extends Bar&lt;String&gt; {}
var x = Foo&lt;int&gt;(); // instance type arguments are &lt;String, int&gt;</code></pre><p>Whereas if the type arguments are the same for parent and child, the list will only contain <code>&lt;int&gt;</code>:</p><pre><code class="language-dart">class Foo&lt;T&gt; extends Bar&lt;T&gt; {}
var x = Foo&lt;int&gt;(); // instance type arguments are &lt;int&gt;</code></pre><p>Another fun feature of Dart is that all field access is done via setters and getters, this may sound very slow but in practice dart eliminates a ton of overhead with the following optimizations:</p><ol><li>Whole-program static analysis</li><li>Inlining calls on known types</li><li>Code de-duplication</li><li>Inline cache (via ICData)</li></ol><p>These optimizations apply to all methods including getters and setters, in the following example the setter is inlined:</p><pre><code class="language-dart">class Foo {
  int x;
}

Foo bar() =&gt; Foo()..x = 42;</code></pre><p>Disassembled:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
028 | ...
02c | ldr r0, [fp, #-4] // load foo
030 | mov ip, #0x54     // smi 42
034 | str ip, [r0, #3]  // store first field
038 | ...</code></pre><figcaption>bar</figcaption></figure><p>But when we call this setter through an interface:</p><pre><code class="language-dart">abstract class Foo {
  set x(int x);
}

class FooImpl extends Foo {
  int x;
}

void bar(Foo foo) {
  foo.x = 42;
}</code></pre><p>Disassembled:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
010 | ...
014 | ldr ip, [fp, #8]     // 
018 | str ip, [sp, #-4]!   // push foo
01c | mov ip, #0x54        // 
020 | str ip, [sp, #-4]!   // push smi 42
024 | ldr r0, [sp, #4]     // load foo into receiver
028 | add lr, pp, #0x2000  // 
02c | ldr lr, [lr, #0x4a3] // unlinkedCall stub
030 | add r9, pp, #0x2000  // 
034 | ldr r9, [r9, #0x4a7] // RawUnlinkedCall set:a
038 | blx lr               // invoke stub
03c | ...</code></pre><figcaption>bar</figcaption></figure><p>Here it invokes an unlinkedCall stub which is a magic bit of code that handles polymorphic method invocation, it will patch its own object pool entry so that further calls are quicker.</p><p>I&apos;d love to get into more detail about how this works at runtime but all we need to know is that it invokes the method specified in the RawUnlinkedCall. If you are interested, there is a great article on the internals of DartVM that explains more: <a href="https://mrale.ph/dartvm/?ref=blog.tst.sh">https://mrale.ph/dartvm/</a></p><hr><h3 id="type-checking">Type Checking</h3><p></p><p>Type checking is a fundamental component of polymorphism, dart provides this through the <code>is</code> and <code>as</code> operators.</p><p>Both operators do a subtype check with the exception of <code>as</code> allowing null values, here is the <code>is</code> operator in action:</p><pre><code class="language-dart">class FooBase {}
class Foo extends FooBase {}
class Bar extends FooBase {}

bool isFoo(FooBase x) =&gt; x is Foo;</code></pre><p>Disassembled:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
024 | ...
028 | ldr r1, [fp, #8]       // load x
02c | ldrh r2, r3, [r1, #1]  // read classid
030 | mov r2, r2, lsl #1     // make smi, suboptimal
034 | cmp r2, #0x12c         // Foo classid (as smi)
038 | ldreq r0, [thr, #0x6c] // thr-&gt;boolTrue
03c | ldrne r0, [thr, #0x70] // thr-&gt;boolFalse
040 | ...</code></pre><figcaption>x is Foo</figcaption></figure><p>Since whole-program analysis determined <code>Foo</code> only has one implementer, it can simply check equality of the class ID, but what if it has a child class?</p><pre><code class="language-dart">class Baz extends Foo {}</code></pre><p>We now get:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
028 | ...
02c | ldr r1, [fp, #8]      // load x
030 | ldrh r2, r3, [r1, #1] // read classid
034 | mov r2, r2, lsl #1    // make smi
038 | mov r1, #0x12c        // Foo smi classid
03c | mov r4, r1, asr #1    // unbox smi (redundant)
040 | mov r3, r4, asr #0x1f // 64 bit sign extend (redundant)
044 | mov r6, r2, asr #1    // unbox smi (redundant)
048 | mov r1, r6, asr #0x1f // 64 bit sign extend (redundant)
04c | cmp r1, r3            // always equal since top half is clear
050 | bgt 0x10              // jump to 0x60 (never)
054 | blt 0x40              // jump to 0x94 (never)
058 | cmp r6, r4            // compare x and Foo
05c | blo 0x38              // jump to 0x94 if x &lt; Foo
060 | mov r2, #0x12e        // smi 0x97
064 | mov r4, r2, asr #1    // unbox smi (redundant)
068 | mov r3, r4, asr #0x1f // 64 bit sign extend (redundant)
06c | cmp r1, r3            // always equal since top half is clear
070 | blt 0x18              // jump to 0x88 (never)
074 | bgt 12                // jump to 0x80 (never)
078 | cmp r6, r4            // compare x and 0x97
07c | bls 12                // jump to 0x88
080 | ldr r2, [thr, #0x70]  // thr-&gt;boolFalse
084 | b 8                   // jump to 0x8c
088 | ldr r2, [thr, #0x6c]  // thr-&gt;boolTrue
08c | mov r0, r2            // 
090 | b 8                   // jump to 0x98
094 | ldr r0, [thr, #0x70]  // thr-&gt;boolFalse
098 | ...</code></pre><figcaption>x is Foo</figcaption></figure><p>Gah! This code is awful so here is a basic translation:</p><pre><code class="language-cpp">bool isFoo(FooBase* x) {
  if (x.classId &lt; FooClassId) return false;
  return x.classId &lt;= BazClassId;
}</code></pre><p>All it is doing here is checking if the class id falls within a set of ranges, in this case there is only one range to check.</p><p>This is definitely a place where DartVM could improve on ARM, it&apos;s doing 64 bit smi range checks for 16 bit class ids instead of just comparing it directly.</p><p>The range checks also do not take into consideration the super type its comparing from which can cause a range to be split by a type that does not implement the super, perhaps as a result of unsoundness.</p><hr><h3 id="control-flow">Control flow</h3><p></p><p>Dart uses a relatively advanced flow graph, represented as an SSA (Single Static Assignment) intermediate similar to modern compilers like gcc and clang. It can perform many optimizations that change the control flow structure of the program, making reasoning about its generated code a bit harder.</p><p>Here is a simple if statement:</p><pre><code class="language-dart">void hello(bool condition) {
  if (condition) {
    print(&quot;foo&quot;);
  } else {
    print(&quot;bar&quot;);
  }
}</code></pre><p>Disassembled:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
010 | ...
014 | ldr r0, [fp, #8]      // load condition
018 | ldr ip, [thr, #0x68]  // thr-&gt;objectNull
01c | cmp r0, ip            // 
020 | bne 0x18              // jump to 038 if condition != null
024 | str r0, [sp, #-4]!    // push condition
028 | ldr r9, [thr, #0x178] // thr-&gt;nonBoolTypeErrorEntryPoint
02c | mov r4, #1            // entry argument count
030 | ldr ip, [thr, #0xd0]  // thr-&gt;callToRuntimeEntryPoint
034 | blx ip                // invoke stub
038 | ldr r0, [fp, #8]      // load condition
03c | ldr ip, [thr, #0x6c]  // thr-&gt;boolTrue
040 | cmp r0, ip            // 
044 | bne 0x1c              // jump to 060 if condition != true
048 | add ip, pp, #0x2000   // 
04c | ldr ip, [ip, #0x4a3]  // 
050 | str ip, [sp, #-4]!    // push string &quot;foo&quot;
054 | bl -0x33b1c           // call print
058 | add sp, sp, #4        // pop arg
05c | b 0x18                // jump to 074
060 | add ip, pp, #0x2000   // 
064 | ldr ip, [ip, #0x4a7]  // 
068 | str ip, [sp, #-4]!    // push string &quot;bar&quot;
06c | bl -0x33b34           // call print
070 | add sp, sp, #4        // pop arg
074 | ...</code></pre><figcaption>hello</figcaption></figure><p>That null check is an example of a &quot;runtime entry&quot; dynamic call, this is the bridge from dart code to subroutines defined in <code>vm/runtime_entry.cc</code>.</p><p>In this case it is a specialized entry that throws a <code>Failed assertion: boolean expression must not be null</code>, as you would expect if the condition of an if statement is null.</p><p>Whole program optimization (and sound non-nullability in the future) allows this null check to be elided, for example if <code>hello</code> never gets called with a possible null value then it won&apos;t do the check at all:</p><pre><code class="language-dart">void main() {
  hello(true);
  hello(false);
}

void hello(bool condition) {
  if (condition) {
    print(&quot;foo&quot;);
  } else {
    print(&quot;bar&quot;);
  }
}</code></pre><p>Disassembled:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
010 | ...
014 | ldr r0, [fp, #8]     // load condition
018 | ldr ip, [thr, #0x6c] // thr-&gt;boolTrue
01c | cmp r0, ip           //
020 | bne 0x1c             // jump to 03c if condition != true
024 | add ip, pp, #0x2000  // 
028 | ldr ip, [ip, #0x4a3] // 
02c | str ip, [sp, #-4]!   // push string &quot;foo&quot;
030 | bl -0x33a90          // call print
034 | add sp, sp, #4       // pop arg
038 | b 0x18               // jump to 050
03c | add ip, pp, #0x2000  // 
040 | ldr ip, [ip, #0x4a7] // 
044 | str ip, [sp, #-4]!   // push string &quot;bar&quot;
048 | bl -0x33aa8          // call print
04c | add sp, sp, #4       // pop arg
050 | ldr r0, [thr, #0x68] // thr-&gt;objectNull
054 | ...</code></pre><figcaption>hello</figcaption></figure><hr><h3 id="closures">Closures</h3><p></p><p>Closures are the implementation of first-class functions under the <code>Function</code> type, you can acquire one by creating an anonymous function or extracting a method.</p><p>A simple function <code>hi</code> that returns an anonymous function:</p><pre><code class="language-dart">void Function() hi() {
  return (x) { print(&quot;Hi $x&quot;); };
}</code></pre><p>Disassembled:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
024 | ...
028 | bl 0x3a6e0           // new dart:core::Closure_at_0150898
02c | add ip, pp, #0x2000  //
030 | ldr ip, [ip, #0x2cf] // RawFunction instance
034 | str ip, [r0, #15]    // RawClosure-&gt;function
038 | ...</code></pre><figcaption>hi</figcaption></figure><p>And to call the closure:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
// Call hi

010 | ...
014 | bl -0x6c           // call hi
018 | str r0, [sp, #-4]! // push arg

// Null check

01c | ldr ip, [thr, #0x68] // thr-&gt;objectNull
020 | cmp r0, ip
024 | bleq -0x3fdc4 // nullErrorStubWithoutFpuRegsStub

// Call closure

028 | ldr r1, [r0, #15]   // RawClosure-&gt;function
02c | mov r0, r1          //
030 | ldr r4, [pp, #0xfb] // arg desc [0, 1, 1, null]
034 | ldr r6, [r0, #0x2b] // RawFunction-&gt;code
038 | ldr r2, [r0, #7]    // RawFunction-&gt;uncheckedEntryPoint
03c | mov r9, #0          // null ICData
040 | blx r2              // invoke entry point
044 | add sp, sp, #4      // pop arg
048 | ...</code></pre><figcaption>hi()()</figcaption></figure><p>Pretty simple, but what if the lambda depends on a local variable from the parent function?</p><pre><code class="language-dart">int Function() hi() {
  int i = 123;
  return () =&gt; ++i;
}</code></pre><p>Disassembled:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
014 | ...
018 | mov r1, #1          // number of variables
01c | ldr r6, [pp, #0x1f] // RawCode allocateContext
020 | ldr lr, [r6, #3]    // RawCode-&gt;entryPoint
024 | blx lr              // invoke stub
028 | str r0, [fp, #-4]   //
02c | ...

040 | ldr r0, [fp, #-4]    // load context
044 | mov ip, #0xf6        // smi 123
048 | str ip, [r0, #11]    // store first variable

04c | bl 0x3a84c           // new dart:core::Closure_at_0150898
050 | add ip, pp, #0x2000  //
054 | ldr ip, [ip, #0x2cf] // RawFuntion instance
058 | str ip, [r0, #15]    // RawClosure-&gt;function
05c | ldr r1, [fp, #-4]    // load context
060 | str r1, [r0, #0x13]  // RawClosure-&gt;context
064 | ...</code></pre><figcaption>hi</figcaption></figure><p>Instead of storing the variable <code>i</code> in the stack frame like a regular local variable, the function will store it in a <code>RawContext</code> and pass that context to the closure.</p><p>When called, the closure can access that variable from the closure argument:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
028 | ...
02c | ldr r1, [fp, #8]    // load first arg
030 | ldr r2, [r1, #0x13] // RawClosure-&gt;context
034 | ldr r0, [r2, #11]   // load first variable
038 | ...</code></pre><figcaption>() =&gt; ++i</figcaption></figure><p>Another way to get a closure is method extraction:</p><pre><code class="language-dart">class Potato {
  int _foo = 0;
  int foo() =&gt; _foo++;
}

int Function() extractFoo() =&gt; Potato().foo;</code></pre><p>When you call <code>get:foo</code> on Potato, Dart will generate that getter method as follows:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
010 | ...
014 | mov r4, #0           // entry args
018 | add ip, pp, #0x2000  // 
01c | ldr r1, [ip, #0x303] // RawFunction Potato.foo
020 | add ip, pp, #0x2000  //
024 | ldr r6, [ip, #0x2ff] // buildMethodExtractorCode
028 | ldr pc, [r6, #11]    // direct jump</code></pre><figcaption>get:foo</figcaption></figure><p><code>get:foo</code> invokes buildMethodExtractor, which eventually returns a RawClosure and stores the receiver (<code>this</code>) in its context and loads that back into <code>r0</code> when called, just like a regular instance call.</p><hr><h3 id="where-the-fun-starts">Where the fun starts</h3><p></p><p>With a good starting point to reverse engineer real world applications, the first big Flutter app that comes to mind is <a href="https://twitter.com/jtmcdole/status/1192853350179491840?ref=blog.tst.sh">Stadia</a>.</p><p>So let&apos;s take a crack at it, first step is to grab an APK off of apkmirror, in this case version 2.2.289534823:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/03/image.png" class="kg-image" alt="Reverse engineering Flutter apps (Part 2)" loading="lazy"></figure><p>(I don&apos;t recommend downloading apps from third party websites, it&apos;s just the easiest way to grab an apk file without a compatible android device)</p><p>The important part here is that the version information contains <code>arm64-v8a + armeabi-v7a</code> which are A64 and A32 respectively.</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/03/image-1.png" class="kg-image" alt="Reverse engineering Flutter apps (Part 2)" loading="lazy"></figure><p>The interesting bits are in the lib folder like <code>libflutter.so</code> which is the flutter engine, and <code>libproduction_android_library.so</code> which is just a renamed <code>libapp.so</code>.</p><p>Before being able to do anything with the snapshot we must know the <em>exact</em> version of Dart that was used to build the app, a quick search of <code>libflutter.so</code> in a hex editor gives us a version string:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/03/image-6.png" class="kg-image" alt="Reverse engineering Flutter apps (Part 2)" loading="lazy"></figure><p>That <code>c547f5d933</code> is a commit hash in the Dart SDK which you can view on GitHub: <a href="https://github.com/dart-lang/sdk/tree/c547f5d933?ref=blog.tst.sh">https://github.com/dart-lang/sdk/tree/c547f5d933</a>, after some digging this corresponds to Flutter version <code>v1.13.6</code> or commit <code>659dc8129d</code>.</p><p>Knowing the exact version of dart is important because it gives you a reference to know how objects are layed out and provides a testbed.</p><p>Once decoded, the next step is to search for the root library, in this version of dart it&apos;s located at index 66 of the root objects list:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/10/image.png" class="kg-image" alt="Reverse engineering Flutter apps (Part 2)" loading="lazy"></figure><p>Neat, we can see the package name of this app is <code>chrome.cloudcast.client.mobile.app</code>, which you might notice is not actually valid for a pub package, what&apos;s going on here?</p><p>The reason for the weird package name is that Google doesn&apos;t actually use pub for internal projects and instead uses it&apos;s internal Google3 repository. You can occasionally see issues on the Flutter GitHub labelled <code>customer: ... (g3)</code>, this is what it refers to.</p><p>By extracting uris from every library defined in the app, we can view the complete file structure for every packages it contains.</p><p>As you might expect from a large project, it depends on quite a few packages: <a href="https://gist.github.com/pingbird/7503be142607df38582b454f3b1e8153?ref=blog.tst.sh">https://gist.github.com/pingbird/7503be142607df38582b454f3b1e8153</a></p><p>We can gather it uses some of the following technologies:</p><ul><li>protobuf</li><li>markdown boilerplate (from AdWords?)</li><li>firebase</li><li>rx</li><li>bloc</li><li>provider</li></ul><p>Most of which appear to be internal implementations.</p><p>Going deeper, here is the root of the lib folder: <a href="https://gist.github.com/pingbird/f2029cd88d5343c0991f706403012f62?ref=blog.tst.sh">https://gist.github.com/pingbird/f2029cd88d5343c0991f706403012f62</a></p><p>I picked a random widget to look at, <code>SocialNotificationCard</code> from <code>profile/view/social_notification_card.dart</code>.</p><p>The library containing this widget is structured as follows:</p><figure class="kg-card kg-code-card"><pre><code class="language-dart">enum SocialNotificationIconType {
  avatarUrl,
  apiImage,
  defaultIcon,
  partyIcon,
}

class SocialNotificationCard extends StatelessWidget {
  SocialNotificationCard({
    dynamic socialNotificationIconType,
    dynamic title,
    dynamic body,
    dynamic timestamp,
    dynamic avatarUrl,
    dynamic apiImage,
  }) { }
  
  NessieString get title { }

  Widget _buildNotificationMessage(dynamic arg1) { }
  Widget _buildNotificationTimestamp(dynamic arg1) { }
  Widget _buildGeneralNotificationIcon() { }
  Widget _buildPartyIcon(dynamic arg1) { }
  Widget _buildAvatarUrlIcon(dynamic arg1) { }
  Widget _buildApiImage(dynamic arg1) { }
  Widget _buildNotificationIconImage(dynamic arg1) { }
  Widget _buildNotificationIcon(dynamic arg1) { }
  Widget build(dynamic arg1) { }
}</code></pre><figcaption>profile/view/social_notification_card.dart</figcaption></figure><p>The type information on these parameters is missing, but since they are build methods we can assume they all take a <code>BuildContext</code>.</p><p>The full disassembly of the <code>_buildPartyIcon</code> method goes as follows:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
// prologue

000 | tst r0, #1
004 | ldrhne ip, [r0, #1]
008 | moveq ip, #0x30
00c | cmp r9, ip, lsl #1
010 | ldrne pc, [thr, #0x108]
014 | stmdb sp!, {fp, lr}
018 | add fp, sp, #0

// function body

01c | bl 0x27ef84          // Image allocation stub
020 | add ip, pp, #0x54000 //
024 | ldr ip, [ip, #0xdc3] // const AssetImage&lt;AssetBundleImageKey&gt;{&quot;assets/social/party_invite.png&quot;, null, null}
028 | str ip, [r0, #7]     // assign field 1 (image)
02c | ldr ip, [thr, #0x70] // thr-&gt;false
030 | str ip, [r0, #0x43]  // assign field 16 (excludeFromSemantics)
034 | add ip, pp, #0x44000 // 
038 | ldr ip, [ip, #0x6b]  // int 48
03c | str ip, [r0, #0x13]  // assign field 4 (width)
040 | add ip, pp, #0x44000 //
044 | ldr ip, [ip, #0x6b]  // int 48
048 | str ip, [r0, #0x17]  // assign field 5 (height)
04c | add ip, pp, #0x46000 //
050 | ldr ip, [ip, #0xe2b] // const BoxFit.cover
054 | str ip, [r0, #0x27]  // assign field 9 (fit)
058 | add ip, pp, #0xd000  //
05c | ldr ip, [ip, #0xacf] // const Alignment{0, 0}
060 | str ip, [r0, #0x2b]  // assign field 10 (alignment)
064 | add ip, pp, #0x38000 //
068 | ldr ip, [ip, #0x653] // const ImageRepeat.noRepeat
06c | str ip, [r0, #0x2f]  // assign field 11 (repeat)
070 | ldr ip, [thr, #0x70] // thr-&gt;false
074 | str ip, [r0, #0x37]  // assign field 13 (matchTextDirection)
078 | ldr ip, [thr, #0x70] // thr-&gt;false
07c | str ip, [r0, #0x3b]  // assign field 14 (gaplessPlayback)
080 | add ip, pp, #0x38000 //
084 | ldr ip, [ip, #0x657] // const FilterQuality.low
088 | str ip, [r0, #0x1f]  // assign field 7 (filterQuality)

// epilogue

08c | sub sp, fp, #0
090 | ldmia sp!, {fp, pc}</code></pre><figcaption>SocialNotificationCard._buildPartyIcon</figcaption></figure><p>This one is quite easy to turn back into code by hand since it constructs a single <code>Image</code> widget:</p><pre><code class="language-dart">Widget _buildPartyIcon(BuildContext context) {
  return Image.asset(
    // The `name` parameter is converted into the const AssetImage we
    // saw above at compile-time, by the Image.asset constructor.
    &quot;assets/social/party_invite.png&quot;,
    fit: BoxFit.cover,
    width: 48,
    height: 48,
    // All of the other fields were assigned to their default value
  );
}</code></pre><p>Note that object construction generally happens in 3 parts:</p><ol><li>Invoke allocation stub, passing type arguments if needed</li><li>Evaluate parameter expressions and assigning them to fields in-order</li><li>Calling the constructor body, if any</li></ol><p>The initializer list and default parameters seem to be unconditionally inlined to the caller, leading to a bit more noise.</p><p>Finally let&apos;s disassemble the actual <code>build</code> method of <code>SocialNotificationCard</code>:</p><figure class="kg-card kg-code-card"><pre><code>#lint dartdec-dasm
// prologue

000 | tst r0, #1              //
004 | ldrhne ip, [r0, #1]     //
008 | moveq ip, #0x30         //
00c | cmp r9, ip, lsl #1      //
010 | ldrne pc, [thr, #0x108] // thr-&gt;monomorphicMissEntry
014 | stmdb sp!, {fp, lr}     //
018 | add fp, sp, #0          //
01c | sub sp, sp, #0x14       // allocate space for local variables
020 | ldr ip, [thr, #0x24]    // thr-&gt;stackLimit
024 | cmp sp, ip              //
028 | blls -0x52ee40          // stack overflow

// construct Padding

02c | bl 0x3e3ce4          // Padding allocation stub
030 | str r0, [fp, #-4]    // assign Padding to local 0

// construct EdgeInsets

034 | bl 0x3e11f4          // EdgeInsets allocation stub
038 | str r0, [fp, #-8]    // assign EdgeInsets to local 1
03c | add ip, pp, #0x5000  //
040 | ldr ip, [ip, #0x9db] // int 0
044 | str ip, [r0, #3]     // assign field 0 (left)
048 | add ip, pp, #0xf000  //
04c | ldr ip, [ip, #0xd7]  // int 16
050 | str ip, [r0, #7]     // assign field 1 (top)
054 | add ip, pp, #0x5000  //
058 | ldr ip, [ip, #0x9db] // int 0
05c | str ip, [r0, #11]    // assign field 2 (right)
060 | add ip, pp, #0xf000  //
064 | ldr ip, [ip, #0xd7]  // int 16
068 | str ip, [r0, #15]    // assign field 3 (bottom)

// construct Row

06c | bl 0x217984          // Row allocation stub
070 | str r0, [fp, #-12]   // assign Row to local 2

// construct List&lt;Widget&gt;

074 | add ip, pp, #0x31000 //
078 | ldr ip, [ip, #0x677] // type args &lt;Widget&gt;
07c | str ip, [sp, #-4]!   // push to stack, this is used at 1cc
080 | add r1, pp, #0x31000 //
084 | ldr r1, [r1, #0x677] // type args &lt;Widget&gt;
088 | mov r2, #6           // smi 3 (new List length)
08c | ldr r6, [pp, #7]     // code allocateArray
090 | ldr lr, [r6, #3]     //
094 | blx lr               // call stub, putting new List into r0
098 | str r0, [fp, #-0x10] // assign List&lt;Widget&gt; to local 3

// call _buildNotificationIcon

09c | ldr ip, [fp, #12]    // argument 0 (self)
0a0 | str ip, [sp, #-4]!   // push
0a4 | ldr ip, [fp, #8]     // argument 1 (context)
0a8 | str ip, [sp, #-4]!   // push
0ac | bl -0x180            // call _buildNotificationIcon + 0x14
0b0 | add sp, sp, #8       // pop arguments

// add notification icon to list

0b4 | ldr r1, [fp, #-0x10]   // load local 3 (List&lt;Widget&gt;)
0b8 | add r9, r1, #11        //
0bc | str r0, [r9]           // list[0] = r0

// garbage collection stuff

0c0 | tst r0, #1             //
0c4 | beq 0x1c               // skip if smi
0c8 | ldrb ip, [r1, #-1]     //
0cc | ldrb lr, [r0, #-1]     //
0d0 | and ip, lr, ip, lsr #2 //
0d4 | ldr lr, [thr, #0x30]   // thr-&gt;writeBarrierMask
0d8 | tst ip, lr             //
0dc | blne -0x52f1ac         // call arrayWriteBarrier stub

// construct Expanded

0e0 | add ip, pp, #0x31000 //
0e4 | ldr ip, [ip, #0xaab] // type args &lt;Flex&gt; (it extends ParentDataWidget&lt;Flex&gt;)
0e8 | str ip, [sp, #-4]!   // push type arguments
0ec | bl 0x39ae5c          // Expanded allocation stub
0f0 | add sp, sp, #4       // pop type arguments
0f4 | str r0, [fp, #-0x14] // assign Expanded to local 4

// call _buildNotificationMessage

0f8 | ldr ip, [fp, #12]  // argument 0 (self)
0fc | str ip, [sp, #-4]! // push
100 | ldr ip, [fp, #8]   // argument 1 (context)
104 | str ip, [sp, #-4]! // push
108 | bl -0x1ae634       // _buildNotificationMessage + 0x14
10c | add sp, sp, #8     // pop arguments

// fill in Expanded

110 | ldr r1, [fp, #-0x14] // load local 4 (Expanded)
114 | mov ip, #2           // smi 1
118 | str ip, [r1, #15]    // assign field 3 (flex)
11c | add ip, pp, #0x31000 //
120 | ldr ip, [ip, #0xab7] // const FlexFit.tight
124 | str ip, [r1, #0x13]  // assign field 4 (fit)
128 | str r0, [r1, #7]     // assign field 1 (child)

// garbage collection stuff

12c | ldrb ip, [r1, #-1]     //
130 | ldrb lr, [r0, #-1]     //
134 | and ip, lr, ip, lsr #2 //
138 | ldr lr, [thr, #0x30]   // thr-&gt;writeBarrierMask
13c | tst ip, lr             //
140 | blne -0x52f020         // call WriteBarrierWrappers stub (r1 object)

// finalize construction of Expanded, this is simply a call to the
// Diagnosticable constructor since none of its other ancestors
// have constructor bodies

144 | str r1, [sp, #-4]!   // push (Expanded)
148 | bl -0x529018         // call Diagnosticable ctor
14c | add sp, sp, #4       // pop

// add Expanded to list

150 | ldr r1, [fp, #-0x10] // load local 3 (List&lt;Widget&gt;)
154 | ldr r0, [fp, #-0x14] // load local 4 (Expanded)
158 | add r9, r1, #15      //
15c | str r0, [r9]         // list[1] = r0

// garbage collection stuff

160 | tst r0, #1             //
164 | beq 0x1c               // skip if smi
168 | ldrb ip, [r1, #-1]     //
16c | ldrb lr, [r0, #-1]     //
170 | and ip, lr, ip, lsr #2 //
174 | ldr lr, [thr, #0x30]   // thr-&gt;writeBarrierMask
178 | tst ip, lr             //
17c | blne -0x52f24c         // call arrayWriteBarrier stub

// call _buildNotificationTimestamp

180 | ldr ip, [fp, #12]  // argument 0 (self)
184 | str ip, [sp, #-4]! // push
188 | ldr ip, [fp, #8]   // argument 1 (context)
18c | str ip, [sp, #-4]! // push
190 | bl -0x894          // call _buildNotificationTimestamp + 0x14
194 | add sp, sp, #8     // pop arguments

// add result to list

198 | ldr r1, [fp, #-0x10] // load local 3 (List&lt;Widget&gt;)
19c | add r9, r1, #0x13    //
1a0 | str r0, [r9]         // list[2] = r0

// garbage collection stuff

1a4 | tst r0, #1             //
1a8 | beq 0x1c               // skip if smi
1ac | ldrb ip, [r1, #-1]     //
1b0 | ldrb lr, [r0, #-1]     //
1b4 | and ip, lr, ip, lsr #2 //
1b8 | ldr lr, [thr, #0x30]   // thr-&gt;writeBarrierMask
1bc | tst ip, lr             //
1c0 | blne -0x52f290         // call arrayWriteBarrier stub

// finalize construction of list (note push at 07c)

1c4 | ldr ip, [fp, #-0x10] // load local 3 (List&lt;Widget&gt;)
1c8 | str ip, [sp, #-4]!   // push
1cc | bl -0x52948c         // call List._fromLiteral
1d0 | add sp, sp, #8       // pop arguments

// fill in Row

1d4 | ldr r1, [fp, #-12]   // load local 2 (Row)
1d8 | add ip, pp, #0x3a000 //
1dc | ldr ip, [ip, #0x1a3] // const Axis.horizontal
1e0 | str ip, [r1, #11]    // assign field 2 (direction)
1e4 | add ip, pp, #0x31000 //
1e8 | ldr ip, [ip, #0xa8b] // const MainAxisAlignment.start
1ec | str ip, [r1, #15]    // assign field 3 (mainAxisAlignment)
1f0 | add ip, pp, #0x31000 //
1f4 | ldr ip, [ip, #0xa93] // const MainAxisSize.max
1f8 | str ip, [r1, #0x13]  // assign field 4 (mainAxisSize)
1fc | add ip, pp, #0x31000 //
200 | ldr ip, [ip, #0xa77] // const CrossAxisAlignment.start
204 | str ip, [r1, #0x17]  // assign field 5 (crossAxisAlignment)
208 | add ip, pp, #0x31000 //
20c | ldr ip, [ip, #0xa9b] // VerticalDirection.down
210 | str ip, [r1, #0x1f]  // assign field 7 (verticalDirection)
214 | str r0, [r1, #7]     // assign List&lt;Widget&gt; to field 1 (children)

// garbage collection stuff

218 | tst r0, #1             //
21c | beq 0x1c               // skip if smi
220 | ldrb ip, [r1, #-1]     //
224 | ldrb lr, [r0, #-1]     //
228 | and ip, lr, ip, lsr #2 //
22c | ldr lr, [thr, #0x30]   // thr-&gt;writeBarrierMask
230 | tst ip, lr             //
234 | blne -0x52f114         // call WriteBarrierWrappers stub (r1 object)

// finalize construction of Row

238 | str r1, [sp, #-4]! // push Row
23c | bl -0x52910c       // call Diagnosticable ctor
240 | add sp, sp, #4     // pop

// fill in Padding

244 | ldr r0, [fp, #-8]  // load local 1 (EdgeInsets)
248 | ldr r1, [fp, #-4]  // load local 0 (Padding)
24c | str r0, [r1, #11]  // assign field 2 (padding)

// garbage collection stuff

250 | ldrb ip, [r1, #-1]     //
254 | ldrb lr, [r0, #-1]     //
258 | and ip, lr, ip, lsr #2 //
25c | ldr lr, [thr, #0x30]   // thr-&gt;writeBarrierMask
260 | tst ip, lr             //
264 | blne -0x52f144         // call WriteBarrierWrappers stub (r1 object)

// fill in Padding

268 | ldr r0, [fp, #-12] // load local 2 (Row)
26c | str r0, [r1, #7]   // assign field 1 (child)

// garbage collection stuff

270 | ldrb ip, [r1, #-1]     //
274 | ldrb lr, [r0, #-1]     //
278 | and ip, lr, ip, lsr #2 //
27c | ldr lr, [thr, #0x30]   // thr-&gt;writeBarrierMask
280 | tst ip, lr             //
284 | blne -0x52f164         // call WriteBarrierWrappers stub (r1 object)

// epilogue

288 | mov r0, r1          // return Padding
28c | sub sp, fp, #0      //
290 | ldmia sp!, {fp, pc} //</code></pre><figcaption>SocialNotificationCard.build</figcaption></figure><p>There was a bit more GC related code this time, if you are interested these write barriers are required due to the <a href="https://v8.dev/blog/concurrent-marking?ref=blog.tst.sh">tri-color invariant</a>. Hitting a write barrier is actually pretty rare, so it has minimal impact on performance with the benefit of allowing parallel garbage collection.</p><p>The equivalent dart code:</p><pre><code class="language-dart">Widget build(BuildContext context) {
  return Padding(
    padding: EdgeInsets.symmetric(vertical: 16.0),
    child: Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: &lt;Widget&gt;[
        _buildNotificationIcon(context),
        Expanded(
          child: _buildNotificationMessage(context),
        ),
        _buildNotificationTimestamp(context),
      ],
    ),
  );
}</code></pre><p>A little more tedious to reverse due to the amount of code, but still relatively easy given tools to identify object pool entries and call targets.</p><hr><h1 id="conclusion">Conclusion</h1><p></p><p>This was a super fun project and I thoroughly enjoyed picking apart assembly code. I hope this series inspires others to also learn more about compilers and the internals of Dart.</p><h3 id="can-someone-steal-my-app">Can someone steal my app?</h3><p>Technically was always possible, given enough time and resources.</p><p>In practice this is not something you should worry about (yet), we are far off from having a full decompilation suite that allows someone to steal an entire app.</p><h3 id="are-my-tokens-and-api-keys-safe">Are my tokens and API keys safe?</h3><p>Nope!</p><p>There will never be a way to fully hide secrets in any client-side application. Note that things like the <a href="https://pub.dev/packages/google_maps_flutter?ref=blog.tst.sh">google_maps_flutter</a> API key is not actually private.</p><p>If you are currently using hard coded credentials or tokens for third party apis in your app you should switch to a real backend or <a href="https://firebase.google.com/docs/functions?ref=blog.tst.sh">cloud function</a> ASAP.</p><h3 id="will-obfuscation-help">Will obfuscation help?</h3><p>Yes and no.</p><p>Obfuscation will randomize identifier names for things like classes and methods, but it won&apos;t prevent us from viewing class structure, library structure, strings, assembly code, etc.</p><p>A competent reverse engineer can still look for common patterns like http API layers, state management, and widgets. It is also possible to partially symbolize code that uses publicly available packages, e.g. you can build signatures for functions in <code>package:flutter</code> and correlate them to ones in an obfuscated snapshot.</p><p>I generally don&apos;t recommend obfuscating Flutter apps because it makes reading error messages harder without doing much for security, you can read more about it <a href="https://flutter.dev/docs/deployment/obfuscate?ref=blog.tst.sh">here</a>.</p>]]></content:encoded></item><item><title><![CDATA[Building a kernel: CS60 pset3]]></title><description><![CDATA[The goal is to implement functioning kernel virtual memory management, and use it to implement syscalls such as fork.
What we start off with is a basic kernel with example programs loaded in, along with a display that visualizes page tables.]]></description><link>https://blog.tst.sh/building-a-kernel/</link><guid isPermaLink="false">5fb44782ed39e923f34cc4ab</guid><category><![CDATA[Low Level]]></category><category><![CDATA[C/C++]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Sat, 02 Jan 2021 18:13:07 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2021/01/pset3-banner.gif" medium="image"/><content:encoded><![CDATA[<hr><h3 id="background">Background</h3><img src="https://blog.tst.sh/content/images/2021/01/pset3-banner.gif" alt="Building a kernel: CS60 pset3"><p>This assignment can be found <a href="https://cs61.seas.harvard.edu/site/2020/WeensyOS/?ref=blog.tst.sh">here</a>, and the starting code can be found in the <code>pset3</code> directory <a href="https://github.com/cs61/cs61-f20-psets?ref=blog.tst.sh">here</a>.</p><p>The goal is to implement functioning kernel virtual memory management, and use it to implement syscalls such as <code>fork</code>.</p><p>What we start off with is a basic kernel with example programs loaded in, along with a display that visualizes page tables:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/11/09jiq.gif" class="kg-image" alt="Building a kernel: CS60 pset3" loading="lazy" width="770" height="529" srcset="https://blog.tst.sh/content/images/size/w600/2020/11/09jiq.gif 600w, https://blog.tst.sh/content/images/2020/11/09jiq.gif 770w" sizes="(min-width: 720px) 720px"></figure><p>By default, WeensyOS spawns 4 programs which map pages sequentially until its memory is exhausted.</p><p>You may notice physical memory looks nearly identical to the virtual memory of each process. This is because there is also no isolation between processes or the kernel, aka an &apos;identity mapping&apos;.</p><hr><h4 id="kernel-and-process-isolation">Kernel and process isolation</h4><p>The first step of this assignment is to implement basic isolation between processes, preventing them from touching memory they shouldn&apos;t.</p><p>I chose the strategy of first applying default protections to the kernel&apos;s page table, then copying it it to each process:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">void kernel_start(const char* command) {
  ...

  // Initialize the kernel page table
  for (vmiter it(kernel_pagetable); it.va() &lt; MEMSIZE_VIRTUAL; it += PAGESIZE)  {
    if (it.va() != 0) {
      int flags;
      // Console and process memory should be user accessible
      if (it.va() == CONSOLE_ADDR || it.va() &gt;= PROC_START_ADDR) {
        flags = PTE_P | PTE_W | PTE_U;
      } else {
        flags = PTE_P | PTE_W;
      }
      it.map(it.va(), flags);
    } else {
      it.map(it.va(), 0);
    }
  }
  
  ...
}

void process_setup(pid_t pid, const char* program_name) {
  ...

  // Allocate a new page table
  x86_64_pagetable* pagetable = kalloc_pagetable();

  // Iterate both our new page table and the kernel page table
  vmiter it2(pagetable);
  for (vmiter it(kernel_pagetable); it.va() &lt; MEMSIZE_VIRTUAL; it += PAGESIZE) {
  	// Copy 1:1 from kernel to process
    it2.map(it.va(), it.perm());
    it2 += PAGESIZE;
  }
  
  // Assign to the process, where it will be loaded by exception_return
  ptable[pid].pagetable = pagetable;
  
  ...
}</code></pre><figcaption>kernel.cc</figcaption></figure><p>This gives us the desired isolation where processes can only read and write to their own memory:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/11/UARwo.gif" class="kg-image" alt="Building a kernel: CS60 pset3" loading="lazy" width="770" height="529" srcset="https://blog.tst.sh/content/images/size/w600/2020/11/UARwo.gif 600w, https://blog.tst.sh/content/images/2020/11/UARwo.gif 770w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="kernel-page-allocator">Kernel page allocator</h2><p>Processes are given dedicated space in physical memory (defined by <code>PROC_START_ADDR</code>), a limitation which prevents them from using more than <code>PROC_SIZE</code> bytes of memory.</p><p>First we need to implement a simple page allocator, I&apos;ve chosen to implement an extremely simple free list.</p><p>How a free list works is that chunks of unallocated pages form a single-linked list:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">struct free_page {
  free_page* next;
};

extern free_page* next_free_page;</code></pre><figcaption>kernel.hh</figcaption></figure><p>To fill this list, we use <code>allocatable_physical_address</code> to check if a physical page is allocatable, pushing them all to the list in <code>kernel_start</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">free_page* next_free_page = nullptr;

void kernel_start(const char* command) {
  ...
  
  // Set up page allocator
  for (int i = NPAGES; i != 0; i--) {
    uintptr_t kmem = (i - 1) * PAGESIZE;
    if (allocatable_physical_address(kmem)) {
      auto newPage = (free_page*)kmem;
      newPage-&gt;next = next_free_page;
      next_free_page = newPage;
    }
    pages[i - 1].refcount = 0;
  }
  
  ...
}</code></pre><figcaption>kernel.cc</figcaption></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/12/build-freelist-1.gif" class="kg-image" alt="Building a kernel: CS60 pset3" loading="lazy" width="400" height="456"></figure><p><code>kalloc</code> and <code>kfree</code> now have a trivial implementation, pushing and popping from the list:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">void* kalloc() {
  if (next_free_page == nullptr) {
    return nullptr;
  } else {
    free_page* page = next_free_page;
    auto&amp; info = pages[(uintptr_t)page / PAGESIZE];
    assert(info.refcount == 0);
    info.refcount = 1;
    next_free_page = next_free_page-&gt;next;
    return page;
  }
}

void kfree(void* kptr) {
  if (kptr == nullptr) return;
  check_valid_kptr(kptr);
  auto page = (free_page*)kptr;
  auto&amp; info = pages[(uintptr_t)page / PAGESIZE];
  assert(info.refcount == 1);
  info.refcount = 0;
  page-&gt;next = next_free_page;
  next_free_page = page;
}</code></pre><figcaption>kernel.cc</figcaption></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/12/alloc-freelist.gif" class="kg-image" alt="Building a kernel: CS60 pset3" loading="lazy" width="400" height="456"></figure><p>The <code>pages</code> array provided by WeensyOS is how the memory viewer keeps track of physical pages, including a reference count:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">struct pageinfo {
  uint8_t refcount;
  bool visited;

  bool used() const { return this-&gt;refcount != 0; }
};</code></pre><figcaption>kernel.hh</figcaption></figure><p>This reference count is useful because multiple processes can share the same physical memory, whether it be from COW or read-only executable sections.</p><p>To facilitate reference counting I&apos;ve made functions to acquire and release references to shared physical pages:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">void kacquire(void* kptr) {
  if (kptr == nullptr) return;
  pageinfo&amp; info = pages[(uintptr_t)kptr / PAGESIZE];
  assert_gt(info.refcount, 0);
  info.refcount++;
}

void krelease(void* kptr) {
  if (kptr == nullptr) return;
  pageinfo&amp; info = pages[(uintptr_t)kptr / PAGESIZE];
  assert_gt(info.refcount, 0);
  if (info.refcount == 1) {
    kfree(kptr);
  } else {
    info.refcount--;
  }
}</code></pre><figcaption>kernel.cc</figcaption></figure><p>The <code>kacquire</code> function simply increments the reference count of the page, and <code>krelease</code> decrements it, freeing the page when it hits zero.</p><hr><h2 id="virtual-memory">Virtual memory</h2><p>In order to implement virtual memory we need to replace the primitive 1:1 page mapping with ones that takes pages from <code>kalloc</code>, but first we will add support for execution protection.</p><h4 id="no-execute-nx-protection">No-execute (NX) protection</h4><p>This wasn&apos;t part of the assignment but I thought it was a cool feature to add.</p><p>Without NX protection all memory is executable, this is problematic as it makes buffer overflow exploits extremely powerful. To solve this security issue we will mark pages that do not contain code as not executable with the NX bit.</p><p>In several places WeensyOS mistakenly uses a 32 bit integer to store page protection flags, this truncates the top 32 bits that contain NX and some other useful OS flags.</p><p>The fix is fairly simple, just replace all of these instances of <code>int</code> with <code>uint64_t</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-diff">-int vmiter::try_map(uintptr_t pa, int perm) {
+int vmiter::try_map(uintptr_t pa, uint64_t perm) {</code></pre><figcaption>k-vmiter.cc</figcaption></figure><pre><code class="language-diff">-  inline void map(uintptr_t pa, int perm);
+  inline void map(uintptr_t pa, uint64_t perm);

-  inline void map(void* kptr, int perm);
+  inline void map(void* kptr, uint64_t perm);

-  [[gnu::warn_unused_result]] int try_map(uintptr_t pa, int perm);
-  [[gnu::warn_unused_result]] inline int try_map(void* kptr, int perm);
+  [[gnu::warn_unused_result]] int try_map(uintptr_t pa, uint64_t perm);
+  [[gnu::warn_unused_result]] inline int try_map(void* kptr, uint64_t perm);

-  int perm_;
+  uint64_t perm_;

-  static constexpr int initial_perm = 0xFFF;
+  static constexpr uint64_t initial_perm = 0xFFE0000000000FFF;

-inline void vmiter::map(uintptr_t pa, int perm) {
+inline void vmiter::map(uintptr_t pa, uint64_t perm) {

-inline void vmiter::map(void* kp, int perm) {
+inline void vmiter::map(void* kp, uint64_t perm) {

-inline int vmiter::try_map(void* kp, int perm) {
+inline int vmiter::try_map(void* kp, uint64_t perm) {</code></pre><p>Some extra defines can be added to <code>x86-64.h</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">// More useful flags

#define PTE_PU 0x7UL // PTE_P | PTE_U

#define PTE_OS4  0x0020000000000000UL
#define PTE_OS5  0x0040000000000000UL
#define PTE_OS6  0x0080000000000000UL
#define PTE_OS7  0x0100000000000000UL
#define PTE_OS8  0x0200000000000000UL
#define PTE_OS9  0x0400000000000000UL
#define PTE_OS10 0x0800000000000000UL
#define PTE_OS12 0x1000000000000000UL
#define PTE_OS13 0x2000000000000000UL
#define PTE_OS14 0x4000000000000000UL

#define PTE_PXD   0x8000000000000001UL // PTE_P | PTE_XD
#define PTE_PUXD  0x8000000000000005UL // PTE_P | PTE_U | PTE_XD
#define PTE_PWXD  0x8000000000000003UL // PTE_P | PTE_W | PTE_XD
#define PTE_PWUXD 0x8000000000000007UL // PTE_P | PTE_W | PTE_U | PTE_XD</code></pre><figcaption>x86-64.h</figcaption></figure><p>In order to track which segments of memory are executable, a few more modifications to the kernel and its linker script are necessary:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">// Returns the start of the kernel rodata segment.
uintptr_t rodata_start_addr();

// Returns the start of the kernel (r/w) data segment.
uintptr_t data_start_addr();

// Returns the end of the kernel address space.
uintptr_t kernel_end_addr();

struct program_image_segment {
  ...

  // Return true iff the segment is executable.
  bool executable() const;

  ...
}</code></pre><figcaption>kernel.hh</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-cpp">// Make these offsets accessible to the rest of the kernel
uintptr_t rodata_start_addr() {
  extern char _rodata_start[];
  return (uintptr_t)_rodata_start;
}

uintptr_t data_start_addr() {
  extern char _data_start[];
  return (uintptr_t)_data_start;
}

uintptr_t kernel_end_addr() {
  extern char _kernel_end[];
  return (uintptr_t)_kernel_end;
}

bool allocatable_physical_address(uintptr_t pa) {
  return !reserved_physical_address(pa)
    &amp;&amp; (pa &lt; KERNEL_START_ADDR || pa &gt;= round_up(kernel_end_addr(), PAGESIZE)) 
    &amp;&amp; (pa &lt; KERNEL_STACK_TOP - PAGESIZE || pa &gt;= KERNEL_STACK_TOP)
    &amp;&amp; pa &lt; MEMSIZE_PHYSICAL;
}

bool program_image_segment::executable() const {
  return ph_-&gt;p_flags &amp; ELF_PFLAG_EXEC;
}</code></pre><figcaption>k-hardware.cc</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-ld">    ...
    
    . = ALIGN(4096);
    _rodata_start = .; /* A tag to indicate the start of rodata */
    .rodata : {
    
    ...</code></pre><figcaption>kernel.ld</figcaption></figure><p>Finally, execution protection can be enforced for the kernel in <code>kernel_start</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">void kernel_start(const char* command) {
  ...

  // (re-)initialize kernel page table
  for (vmiter it(kernel_pagetable); it.va() &lt; MEMSIZE_PHYSICAL; it += PAGESIZE) {
    if (it.va() != 0) {
      uint64_t flags;
      if (it.va() == CONSOLE_ADDR) {
        // The console should be r/w to users
        flags = PTE_PWUXD;
      } else if (it.va() &gt;= KERNEL_START_ADDR &amp;&amp; it.va() &lt; rodata_start_addr()) {
        // The text segment should be read-only execute
        flags = PTE_P;
      } else if (it.va() &gt;= rodata_start_addr() &amp;&amp; it.va() &lt; data_start_addr()) {
        // The rodata segment should be read-only
        flags = PTE_PXD;
      } else {
        // The rest (data segment, heap) should be r/w
        flags = PTE_PWXD;
      }
      it.map(it.va(), flags);
    } else {
      // nullptr is inaccessible even to the kernel
      it.map(it.va(), 0);
    }
  }
  
  ...
}</code></pre><figcaption>kernel.cc</figcaption></figure><p>To check if this actually works you can mark the text segment with the XD bit, WeensyOS will crash on startup because it&apos;s trying to execute memory that is now protected:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/11/tWOXN.gif" class="kg-image" alt="Building a kernel: CS60 pset3" loading="lazy" width="770" height="528" srcset="https://blog.tst.sh/content/images/size/w600/2020/11/tWOXN.gif 600w, https://blog.tst.sh/content/images/2020/11/tWOXN.gif 770w" sizes="(min-width: 720px) 720px"></figure><p>If the XD bit is cleared from the text segment, the OS continues running as normal, yay!</p><h4 id="process-allocation">Process allocation</h4><p>So far pages are manually mapped to a process from a few places, to make it simpler I&apos;ve created a single function that takes care of allocation, mapping, copying, and dealing with edge cases:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">static bool proc_alloc(pid_t pid, uintptr_t vaddr, size_t sz, uint64_t flags = PTE_PWUXD, void* src = nullptr) {
  assert_eq(vaddr &amp; PAGEOFFMASK, 0);
  vmiter it(ptable[pid].pagetable);
  uintptr_t num_pages = (sz + PAGESIZE - 1) / PAGESIZE;
  for (uintptr_t page = 0; page &lt; num_pages; page++) {
    void* kptr = kalloc();
    if (kptr != nullptr) {
      it.find(vaddr + page * PAGESIZE);
      if (it.try_map(kptr, flags) &gt;= 0) {
        // Success, copy from src and continue
        if (src != nullptr) {
          memcpy(kptr, (void*)src, min(sz, PAGESIZE));
          src = (char*)src + PAGESIZE;
          sz -= PAGESIZE;
        }
        continue;
      } else {
        kfree(kptr);
      }
    }

    // Map or allocation failed, free and unmap everything
    while (page &gt; 1) {
      page--;
      it.find(vaddr + page * PAGESIZE);
      kfree(it.kptr());
      assert(it.perm() == flags);
      it.map(it.pa(), PTE_XD);
    }
    return false;
  }
  return true;
}</code></pre><figcaption>kernel.cc</figcaption></figure><p>We can then use this function along with the <code>program_image_segment::executable</code> function defined earlier in <code>process_setup</code> to load ELF segments:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">void process_setup(pid_t pid, const char* program_name) {
  ...

  for (auto seg = pgm.begin(); seg != pgm.end(); seg++) {
    if (seg.size() == 0) continue;
    uint64_t flags = PTE_PUXD;
    if (seg.writable()) {
      flags |= PTE_W;
    }
    if (seg.executable()) {
      flags &amp;= ~PTE_XD;
    }

    // Allocate and copy segment to process
    if (!proc_alloc(pid, seg.va(), seg.size(), flags, (void*)seg.data())) {
      panic(&quot;Failed to allocate process memory during setup&quot;);
    }
  }
  
  ...
}</code></pre><figcaption>kernel.cc</figcaption></figure><p>Additionally the <code>syscall_page_alloc</code> function can use <code>proc_alloc</code> rather than mapping directly:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">int syscall_page_alloc(uintptr_t addr) {
  if ((addr &amp; PAGEOFFMASK) != 0 || addr &lt; PROC_START_ADDR) {
    return -1;
  } else if (proc_alloc(current-&gt;pid, addr, PAGESIZE)) {
    return 0;
  } else {
    return -1;
  }
}</code></pre><figcaption>kernel.cc</figcaption></figure><h4 id="the-result">The result</h4><p>Now that virtual memory is implemented, WeensyOS now looks like this:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/11/ITHFs.gif" class="kg-image" alt="Building a kernel: CS60 pset3" loading="lazy" width="770" height="548" srcset="https://blog.tst.sh/content/images/size/w600/2020/11/ITHFs.gif 600w, https://blog.tst.sh/content/images/2020/11/ITHFs.gif 770w" sizes="(min-width: 720px) 720px"></figure><p>Instead of a 1:1 mapping, pages are allocated on-demand, this allows greedy processes like #4 to consume much more than <code>PROC_SIZE</code> bytes of memory.</p><hr><h2 id="fork">Fork</h2><p>The final steps of the assignment are to implement the fork syscall.</p><p>If you are not familiar with <code>fork</code> it essentially duplicates your process incl. its memory and execution state, for more information see the Linux <a href="https://www.man7.org/linux/man-pages/man2/fork.2.html?ref=blog.tst.sh">fork(2)</a>.</p><p>Thankfully, all of the low level stuff is taken care of already, adding a new syscall is as simple as defining it in <code>lib.hh</code> and adding a switch case to <code>exception</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">uintptr_t syscall(regstate* regs) {
  ...
  
  // To handle a new syscall number we just add a case here
  switch (regs-&gt;reg_rax) {
    ...
    case SYSCALL_FORK:
      return syscall_fork(current);
    ...
  }

  ...
}
</code></pre><figcaption>kernel.cc</figcaption></figure><p>Instead of simply copying all of the processes memory in fork, we only copy pages when they are written to. This optimization is called copy on write (COW) and can be implemented by marking a page read-only then copying it when a page fault occurs.</p><p>To tell our exception handler a page is COW, we will use one of the user-defined flags in its page table entry:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">#define PTE_COW PTE_OS1 // Copy on write</code></pre><figcaption>kernel.cc</figcaption></figure><p>The implementation of <code>syscall_fork</code> goes as follows:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">int syscall_fork(proc* process) {
  pid_t pid = 0;

  // Find the first available process id
  for (pid_t i = 1; i &lt; NPROC; i++) {
    if (ptable[i].state == P_FREE) {
      pid = i;
      break;
    }
  }

  if (!pid) return -1;

  proc&amp; proc_info = ptable[pid];

  x86_64_pagetable* pt = kalloc_pagetable();

  if (pt == nullptr) return -1;

  // Copy page table of parent to child
  vmiter proc_it(pt);
  void* lastaquire = nullptr;
  for (vmiter it(process-&gt;pagetable); it.va() &lt; MEMSIZE_VIRTUAL; it.next()) {
    int perms = it.perm();
    void* kptr = it.kptr();
    lastaquire = 0;

    if (it.va() &gt;= PROC_START_ADDR &amp;&amp; it.present()) {
      if (it.writable()) {
        // Mark the page COW if it was writeable
        perms = (it.perm() | PTE_COW) &amp; ~PTE_W;
        // Mark it COW in the parent process too, this map doesn&apos;t need to be
        // reverted on abort because it does not change the semantics of writes
        if (it.try_map(kptr, perms) &lt; 0) {
          goto abort_fork;
        }
      }
	  // Read-only pages will be shared implicitly, acquire a reference
      kacquire(kptr);
      lastaquire = kptr;
    }

    proc_it.find(it.va());
    if (proc_it.try_map(kptr, perms) &lt; 0) {
      goto abort_fork;
    }
  }

  // Page table creation success, initialize child process

  // Copy registers from parent
  proc_info.regs = process-&gt;regs;

  // Set return value of child syscall to 0
  proc_info.regs.reg_rax = 0;

  // Enqueue the process
  proc_info.wake = 0; // Field used by the sleep syscall later on
  proc_info.pagetable = pt;
  proc_info.state = P_RUNNABLE;

  // Return child process id
  return pid;

abort_fork:

  // Release pa if try_map fails after an acquire or kalloc
  if (lastaquire != nullptr) {
    krelease(lastaquire);
  }

  // Release pages we&apos;ve aquired so far
  for (vmiter it(pt); it.va() &lt; MEMSIZE_VIRTUAL; it += PAGESIZE) {
    if (it.va() &gt;= PROC_START_ADDR &amp;&amp; it.present()) {
      krelease(it.kptr());
    }
  }

  free_page_table(pt);

  return -1;
}</code></pre><figcaption>kernel.cc</figcaption></figure><p>In short this function copies the parent&apos;s state to a new process, remapping any writeable pages as read-only plus <code>PTE_COW</code>. </p><p>Now in the <code>exception</code> function, we can check for page faults occurring on a COW page:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">void exception(regstate* regs) {
  ...

  bool usermode = (regs-&gt;reg_cs &amp; 0x3) != 0;
  
  switch (regs-&gt;reg_intno) {
    ...
    case INT_PF: {
      uintptr_t addr = rdcr2();

      bool is_write = regs-&gt;reg_errcode &amp; PFERR_WRITE;

      // Check if write exception was in process memory
      if (usermode &amp;&amp; is_write &amp;&amp; addr &gt;= PROC_START_ADDR) {
        vmiter it(current-&gt;pagetable, round_down(addr, PAGESIZE));
        // If page is present and PTE_OS1 is set, this is a COW page
        if (it.perm() &amp; (PTE_P | PTE_COW)) {
          pageinfo&amp; info = pages[it.pa() / PAGESIZE];
          assert(info.used());
          if (info.refcount == 1) {
            // We have the only reference to this page, avoid copying by
            // just marking it writeable
            if (it.try_map(it.pa(), (it.perm() | PTE_W) &amp; ~PTE_COW) == 0) {
              // Success, resume process
              break;
            }
          } else {
            // Allocate and copy data to new page
            void* kptr = kalloc();
            if (kptr) {
              void* source = it.kptr();
              memcpy(kptr, source, PAGESIZE);
              // Set writeable, clear COW
              if (it.try_map(kptr, (it.perm() | PTE_W) &amp; ~PTE_COW) == 0) {
                // Success, resume process
                krelease(source);
                break;
              }
            }
          }

          // Out of memory!
          proc_kill(current);
          schedule();
        }
      }

      // Bits that log page faults to the console
      ...
    }
    ...
  }

  // We can quietly resume the process if not defunct
  if (current-&gt;state == P_RUNNABLE) {
    run(current);
  } else {
    schedule();
  }
}</code></pre><figcaption>kernel.cc</figcaption></figure><h4 id="the-result-1">The result</h4><p>Along with the default program <code>p-allocator.cc</code>, WeensyOS also comes with <code>p-fork.cc</code> and <code>p-forkexit.cc</code> which can be booted using special key codes.</p><p>In this case we want to test the fork implementation using <code>p-fork.cc</code> which can be run by pressing <code>f</code> at startup:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/11/gYnmt.gif" class="kg-image" alt="Building a kernel: CS60 pset3" loading="lazy" width="779" height="529" srcset="https://blog.tst.sh/content/images/size/w600/2020/11/gYnmt.gif 600w, https://blog.tst.sh/content/images/2020/11/gYnmt.gif 779w" sizes="(min-width: 720px) 720px"></figure><p>This program is a variation of the first but starts with a single process which calls fork 3 times rather than the kernel initializing them.</p><p>You may notice that the first few pages are now labelled <code>S</code>, this indicates the same physical address has been mapped to multiple processes.</p><hr><h2 id="exit-and-kill">Exit and kill</h2><p>The final task of the assignment is to implement the exit syscall and test it with <code>p-forkexit.cc</code>. Later on we will also implement the sleep, kill, mmap, and munmap syscalls as part of extra credit.</p><p>Like with fork, you handle syscalls in the kernel by adding cases to the <code>syscall</code> function:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">    case SYSCALL_EXIT:
      proc_kill(current);
      schedule();

    case SYSCALL_KILL:
      if (arg0 &gt; 0 &amp;&amp; arg0 &lt; 16 &amp;&amp; ptable[arg0].state == P_RUNNABLE) {
        proc_kill(&amp;ptable[arg0]);
        return 0;
      }
      return -1;</code></pre><figcaption>kernel.cc</figcaption></figure><p>Exit and kill basically do the same thing, so their functionality can just be combined into a single function.</p><p>The implementation of <code>proc_kill</code> turns out to be extremely simple:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">// Also used in syscall_fork previously 
static void free_page_table(x86_64_pagetable* pt) {
  if (pt == nullptr) return;
  // The ptiter utility can be used to iterate page tables recursively, as
  // opposed to vmiter which just iterates virtual memory entries
  for (ptiter it(pt); !it.done(); it.next()) {
    kfree(it.kptr());
  }
  kfree(pt);
}

static void proc_kill(proc* process) {
  // Clean up pages
  for (vmiter it(process-&gt;pagetable); it.va() &lt; MEMSIZE_VIRTUAL; it += PAGESIZE) {
    if (it.va() &gt;= PROC_START_ADDR &amp;&amp; it.present()) {
      krelease((void*)it.pa());
    }
  }

  free_page_table(process-&gt;pagetable);

  process-&gt;pagetable = nullptr;
  process-&gt;state = P_FREE;
}</code></pre><figcaption>kernel.cc</figcaption></figure><p><code>p-forkexit.cc</code> is a fairly intense test, along with consuming memory and forking it also exits at random intervals. It is quite hard to get past this point without leaking memory, there was a lot of debugging involved.</p><p>Like <code>p-fork.cc</code>, this program is also started through a special key combination <code>e</code>:</p><!--kg-card-begin: html--><video width="778" height="528" allowfullscreen controls>
<source src="https://i.tst.sh/LfdXP.mp4" type="video/mp4">
</video><!--kg-card-end: html--><p>After running it for awhile we don&apos;t experience a crash and no pages are leaked!</p><p>Those large strips of <code>S</code>es are COW pages that haven&apos;t been written to yet, they are shared between children just like the shared read-only segments from before.</p><hr><h2 id="sleep">Sleep</h2><p>To implement this syscall you must first understand how the scheduler works in WeensyOS.</p><p>Here are a few notable functions we start with:</p><ul><li><code><em>void </em>schedule()</code> - Picks the next runnable process and runs it.</li><li><code><em>void </em>run(proc* p)</code> - Sets the <code>current</code> variable and uses <code>exception_return</code> to resume the process.</li><li><code><em>void </em>exception(regstate* regs)</code> - The exception handler called by <code>k-exception.S</code>, including APIC timer interrupts.</li></ul><p>On startup <code>kernel_start</code> initializes the <a href="https://wiki.osdev.org/APIC_timer?ref=blog.tst.sh">APIC timer</a>, a neat bit of hardware that sends periodic interrupts to the processor. Along with keeping track of time with the <code>ticks</code> variable, this timer allows the kernel to prevent processes from hogging CPU by interrupting them.</p><p>When an interrupt or exception happens the processor will save register state, disable interrupts, enter kernel mode, and call the appropriate exception handler. The <code>exception</code> function copies the register state to <code>current-&gt;regstate</code>, which is used in the <code>run</code> function when resuming the process.</p><p>To resume a process the kernel uses the <code>exception_return</code> function, a small bit of assembly that restores register state and invokes <code>iretq</code> to get back into user mode.</p><p>Note that the kernel&apos;s state is not saved, you are reset back to the bottom of the stack every time there is an exception (<code>schedule</code>, <code>run</code>, and <code>exception</code> never return).</p><p>With this knowledge implementing <code>sleep</code> is much more straightforward, to start I added a field to keep track of when a specific process should resume from sleep:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">struct proc {
  ...
  
  unsigned long wake;         // when to wake up from a sleep syscall
};</code></pre><figcaption>kernel.cc</figcaption></figure><p>Like the global <code>ticks</code> variable, the <code>wake</code> field is based on the frequency of the APIC timer (<code>HZ</code>), but the syscall will take its duration in milliseconds:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">    case SYSCALL_SLEEP:
      current-&gt;regs.reg_rax = 0;
      current-&gt;wake = ticks + (arg0 / (1000 / HZ));
      schedule();</code></pre><figcaption>kernel.cc</figcaption></figure><p>Now rewrite the <code>schedule</code> function to skip any processes that are not ready to wake:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">void schedule() {
  for (;;) {
    bool sleeping = false;
    for (int i = 1; i &lt;= NPROC; i++) {
      pid_t pid = (current-&gt;pid + i) % 16;
      proc&amp; p = ptable[pid];
      if (p.state != P_RUNNABLE) continue;
      if (ticks &gt;= p.wake) {
        run(&amp;p);
      } else if (p.wake) {
        sleeping = true;
      }
    }
    check_keyboard();
    if (sleeping) {
      // Interrupts are disabled, enable them with the sti instruction
      sti();
      // Put the processor in a more efficient halt state while we wait
      // for timer interrupts
      halt();
      // The halt instruction can occasionally resume, whatever, just
      // disable interrupts and try again
      cli();
    } else {
      memshow();
    }
  }
}</code></pre><figcaption>kernel.cc</figcaption></figure><p>Hold up, this introduces some unexpected behavior!</p><p>It turns out the <code>exception</code> function didn&apos;t anticipate any non-fatal exceptions to happen while in kernel mode.</p><p>The problem is that it unconditionally stashes register states to the previous process regardless of if the exception actually happened in that process. This leads to clobbering of the the state of the last running process with the state of the kernel while its halted above.</p><p>A simple solution is to only set <code>current-&gt;regs</code> if the lower 2 bits of the <code>cs</code> register (the privilege level) is non-zero:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">void exception(regstate* regs) {
  bool is_user_mode = (regs-&gt;reg_cs &amp; 0x3) != 0;

  if (is_user_mode) {
    current-&gt;regs = *regs;
    regs = &amp;current-&gt;regs;
  }

  ...
}</code></pre><figcaption>kernel.cc</figcaption></figure><hr><h2 id="mmap-munmap">mmap, munmap</h2><p>Currently all memory management in a process is done through the very primitive <code>sys_page_alloc</code> syscall, this has several limitations such as:</p><ul><li>Can only map pages at specific address, can&apos;t find free ones automatically</li><li>No way to free pages (munmap).</li><li>No way to map executable or read-only pages (such as in a dynamic linker).</li><li>No way to share memory between a parent an child process.</li></ul><p>To solve these issues I&apos;ve implemented the <code>mmap</code> and <code>munmap</code> syscalls, starting with definitions in <code>lib.hh</code> / <code>u-lib.hh</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">// Available flags for the sys_mmap syscall
#define MAP_PROT_READ  0x10 // Page can be read.
#define MAP_PROT_WRITE 0x20 // Page can be read and written.
#define MAP_PROT_EXEC  0x40 // Page can be executed.
#define MAP_PROT_NONE  0x00 // Page can not be accessed.

#define MAP_PRIVATE 0x00 // Changes are private.
#define MAP_SHARED  0x01 // Share changes.
#define MAP_FIXED   0x02 // Interpret addr exactly.</code></pre><figcaption>lib.hh</figcaption></figure><p>Note that <code>MAP_ANONYMOUS</code> is not defined because there is no filesystem, all mappings are anonymous by default!</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">inline void* sys_mmap(void* addr, size_t length, int flags) {
  return (void*)make_syscall(SYSCALL_MMAP, (uintptr_t)addr, length, flags);
}

inline int sys_munmap(void* addr, size_t length) {
  return make_syscall(SYSCALL_MUNMAP, (uintptr_t)addr, length);
}

// Replace previous implementation with one that uses mmap
inline int sys_page_alloc(void* addr) {
  void* result = sys_mmap(addr, PAGESIZE, MAP_PRIVATE | MAP_FIXED | MAP_PROT_WRITE);
  return result == nullptr ? -1 : 0;
}

inline int sys_page_free(void* addr) {
  return sys_munmap(addr, PAGESIZE);
}</code></pre><figcaption>u-lib.hh</figcaption></figure><p>Like other syscalls, mmap and munmap get their own functions: </p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">    case SYSCALL_MMAP:
      return syscall_mmap(current, arg0, arg1, arg2);
    
    case SYSCALL_MUNMAP:
      return syscall_munmap(current, arg0, arg1);</code></pre><figcaption>kernel.cc</figcaption></figure><p>The syscall_mmap function is probably the most complex so far as it has to handle several edge cases and failure conditions, its implementation goes as follows:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">static uintptr_t next_free_vaddr(proc* process, uintptr_t num_pages) {
  // This could be made a lot faster if we used a tree to manage virtual
  // address space.
  size_t viable = 0;
  vmiter it(process-&gt;pagetable);
  for (uintptr_t addr = PROC_START_ADDR; addr &lt; MEMSIZE_VIRTUAL; addr += PAGESIZE) {
    it.find(addr);
    if (it.present()) {
      viable = 0;
    } else if (++viable == num_pages) {
      // Found free pages
      addr -= (viable - 1) * PAGESIZE;
      return addr;
    }
  }
  return 0;
}

static uintptr_t syscall_mmap(proc* process, uint64_t addr, uint64_t length, uint64_t flags) {
  if ((addr &amp; PAGEOFFMASK) != 0 || length == 0) {
    // The base address must be a multiple of PAGESIZE
    return 0;
  }

  uintptr_t num_pages = (length + PAGESIZE - 1) / PAGESIZE;
  vmiter it(process-&gt;pagetable);

  if (addr == 0) {
    // nullptr always allocates new pages
    addr = next_free_vaddr(process, num_pages);
  } else if ((flags &amp; MAP_FIXED) == 0) {
    // If the address is not fixed, reallocate if any overlap with existing maps
    for (uintptr_t i = 0; i &lt; num_pages; i++) {
      it.find(addr + i * PAGESIZE);
      if (it.present()) {
        // Page overlaps, reallocate
        addr = next_free_vaddr(process, num_pages);
        break;
      }
    }
  }

  uintptr_t end = addr + num_pages * PAGESIZE;
  if (
    addr &lt; PROC_START_ADDR
    || end &gt; MEMSIZE_VIRTUAL
    || num_pages &gt; PAGESIZE / sizeof(uint64_t)
  ) {
    // Either the range is invalid, we failed to allocate one, or there are too
    // many page entries to fit in a journal.
    return 0;
  }

  uint64_t pte = PTE_PXD;
  if (flags &amp; MAP_SHARED)     pte |= PTE_SHARED;
  if (flags &amp; MAP_PROT_READ)  pte |= PTE_PU;
  if (flags &amp; MAP_PROT_WRITE) pte |= PTE_PWU;
  if (flags &amp; MAP_PROT_EXEC)  pte &amp;= ~PTE_XD;

  // Keep a journal of the entries that we clobber, so they can be restored on failure
  auto journal = (uint64_t*)kalloc();
  if (journal == nullptr) {
    return 0;
  }

  for (uintptr_t i = 0; i &lt; num_pages; i++) {
    it.find(addr + i * PAGESIZE);
    uint64_t pa = it.pa();
    uint64_t perm = it.perm();
    journal[i] = perm | pa;
    uint64_t new_perm = pte;

    if (!(perm &amp; PTE_P)) {
      // Allocate new pages if they do not exist
      pa = (uint64_t)kalloc();
      if (pa == 0) {
        goto abort_map;
      }
    } else if (pages[pa / PAGESIZE].refcount &gt; 1 &amp;&amp; (new_perm &amp; PTE_PWU)) {
      // If page has multiple references and should be writable, mark it COW
      new_perm = (new_perm &amp; ~PTE_W) | PTE_COW;
    }

    // Finally apply permissions
    if (it.try_map(pa, new_perm) &lt; 0) {
      if (!it.present()) {
        kfree((void*)pa);
      }
      goto abort_map;
    }

    continue;

  abort_map:

    // Map incomplete, revert changes using journal
    for (uintptr_t p = 0; p &lt; i; p++) {
      it.find(addr + p * PAGESIZE);
      uint64_t prev = journal[i];
      if (!(prev &amp; PTE_P)) {
        kfree(it.kptr());
      }
      it.map(prev &amp; PTE_PAMASK, prev &amp; ~PTE_PAMASK);
    }
    kfree(journal);

    return 0;
  }

  // Success, all pages have been mapped
  kfree(journal);
  return addr;
}</code></pre><figcaption>kernel.cc</figcaption></figure><p>Because <code>try_map</code> / <code>kalloc</code> can fail at any point, the implementation of mmap writes exiting page table entries to a journal that can be rolled back.</p><p>A similar is approach is used to release pages with munmap:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">static int syscall_munmap(proc* process, uint64_t addr, uint64_t length) {
  if ((addr &amp; PAGEOFFMASK) != 0 || length == 0) {
    // The base address must be a multiple of PAGESIZE
    return 0;
  }

  uintptr_t num_pages = (length + PAGESIZE - 1) / PAGESIZE;
  vmiter it(process-&gt;pagetable);

  uintptr_t end = addr + length;
  if ((length == 0 || addr &lt; PROC_START_ADDR || end &gt; MEMSIZE_VIRTUAL || addr &gt; end)) {
    return 0;
  }

  // Keep a journal of the entries that we clobber
  auto journal = (uint64_t*)kalloc();
  assert(num_pages &lt;= PAGESIZE / sizeof(uint64_t));
  if (journal == nullptr) {
    return -1;
  }

  for (uintptr_t i = 0; i &lt; num_pages; i++) {
    it.find(addr + i * PAGESIZE);
    uint64_t pa = it.pa();
    journal[i] = it.perm() | pa;
    // Finally apply permissions
    if (it.try_map(pa, 0) &lt; 0) {
      // Mapping incomplete, revert changes using journal
      for (uintptr_t p = 0; p &lt; i; p++) {
        it.find(addr + p * PAGESIZE);
        uint64_t prev = journal[i];
        it.map(prev &amp; PTE_PAMASK, prev &amp; ~PTE_PAMASK);
      }
      kfree(journal);

      return -1;
    }
  }

  // Success, all pages have been mapped, release their references.
  for (uintptr_t i = 0; i &lt; num_pages; i++) {
    if (journal[i] &amp; PTE_P) {
      kfree((void*)(journal[i] &amp; PTE_PAMASK));
    }
  }

  kfree(journal);

  return 0;
}</code></pre><figcaption>kernel.cc</figcaption></figure><hr><h2 id="testing-all-the-things">Testing all the things</h2><p>In order to test all the new syscalls we need to create custom user-mode programs, thankfully the process is pretty straightforward.</p><p>First, create the program:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">#include &quot;u-lib.hh&quot;

[[noreturn]] void process_main() {
  // Let the system allocate new memory, make it shared between procs
  int* shared_memory = (int*)sys_mmap(nullptr, PAGESIZE, MAP_SHARED | MAP_PROT_WRITE);
  assert(shared_memory != nullptr);

  pid_t cp = sys_fork();
  if (cp == 0) {
    // Child process
    // Read 1, write 2, wait
    assert_eq(*shared_memory, 1);
    *shared_memory = 2;
    sys_sleep(1000);

    // Read 3
    assert_eq(*shared_memory, 3);

    // Write 4 until killed
    for (;;) {
      *shared_memory = 4;
      sys_yield();
    }
  } else {
    // Parent process
    // Write 1, wait
    *shared_memory = 1;
    sys_sleep(500);

    // Read 2, write 3, wait
    assert_eq(*shared_memory, 2);
    *shared_memory = 3;
    sys_sleep(1000);

    // Read 4
    assert_eq(*shared_memory, 4);

    // Kill, write 5, wait
    sys_kill(cp);
    *shared_memory = 5;
    sys_sleep(1000);

    // Read 5
    assert_eq(*shared_memory, 5);

    // Success
    sys_munmap(shared_memory, PAGESIZE);
    panic(&quot;Success!&quot;);
  }
}
</code></pre><figcaption>p-custom.cc&#xA0;</figcaption></figure><p>This test covers most of the functionality of <code>mmap</code>, <code>sleep</code>, <code>kill</code>, <code>munmap</code>, then panics with a success message after everything checks out.</p><p>Next, add the program to the Makefile:</p><figure class="kg-card kg-code-card"><pre><code class="language-make">PROCESS_BINARIES += $(OBJDIR)/p-custom
PROCESS_OBJS += $(OBJDIR)/p-custom.uo</code></pre><figcaption>GNUMakefile</figcaption></figure><p>Finally, add it to <code>k-hardware.cc</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-cpp">// These symbols are defined by the linker
extern uint8_t _binary_obj_p_custom_start[];
extern uint8_t _binary_obj_p_custom_end[];

...

} ramimages[] = {
  ...
  {&quot;custom&quot;, _binary_obj_p_custom_start, _binary_obj_p_custom_end}};

// The ramimages table defines ranges of static memory for
// each process binary

...

int check_keyboard() {
  ...
  // Add custom key codes here and below
  if (c == &apos;a&apos; || c == &apos;f&apos; || c == &apos;e&apos; || c == &apos;c&apos;) {
    ...
    // The kernel uses this argument to load a specific program
    // from the table above
    const char* argument = &quot;fork&quot;;
    if (c == &apos;a&apos;) {
      ...
    } else if (c == &apos;c&apos;) {
      argument = &quot;custom&quot;;
    }
    ...
  }
  ...
}</code></pre><figcaption>k-hardware.cc</figcaption></figure><p>Now the program should run when you press <code>c</code>:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2021/01/5lURW.gif" class="kg-image" alt="Building a kernel: CS60 pset3" loading="lazy" width="788" height="529" srcset="https://blog.tst.sh/content/images/size/w600/2021/01/5lURW.gif 600w, https://blog.tst.sh/content/images/2021/01/5lURW.gif 788w" sizes="(min-width: 720px) 720px"></figure><p>Success!</p>]]></content:encoded></item><item><title><![CDATA[Reverse engineering Flutter apps (Part 1)]]></title><description><![CDATA[<h3 id="chapter-1-down-the-rabbit-hole">Chapter 1: Down the rabbit hole</h3><p><br>To start this journey I&apos;ll cover some backstory on the Flutter stack and how it works.</p><p>What you probably already know: Flutter was built from the ground up with its own render pipeline and widget library, allowing it to be truly cross</p>]]></description><link>https://blog.tst.sh/reverse-engineering-flutter-apps-part-1/</link><guid isPermaLink="false">5fa9b817c9aac25a07117259</guid><category><![CDATA[ARM]]></category><category><![CDATA[Assembly]]></category><category><![CDATA[C/C++]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Low Level]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Sat, 28 Mar 2020 09:29:14 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2020/03/HighresScreenshot00003_2.png" medium="image"/><content:encoded><![CDATA[<h3 id="chapter-1-down-the-rabbit-hole">Chapter 1: Down the rabbit hole</h3><img src="https://blog.tst.sh/content/images/2020/03/HighresScreenshot00003_2.png" alt="Reverse engineering Flutter apps (Part 1)"><p><br>To start this journey I&apos;ll cover some backstory on the Flutter stack and how it works.</p><p>What you probably already know: Flutter was built from the ground up with its own render pipeline and widget library, allowing it to be truly cross platform and have a consistent design and feel no matter what device its running on.</p><p>Unlike most platforms, all of the essential rendering components of the flutter framework (including animation, layout, and painting) are fully exposed to you in <code><a href="https://github.com/flutter/flutter/tree/master/packages/flutter?ref=blog.tst.sh">package:flutter</a></code>.</p><p>You can see these components in the official architecture diagram from <a href="https://github.com/flutter/flutter/wiki/The-Engine-architecture?ref=blog.tst.sh">wiki/The-Engine-architecture</a>:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/02/framework.png" class="kg-image" alt="Reverse engineering Flutter apps (Part 1)" loading="lazy"></figure><p>From a reverse engineering perspective the most interesting part is is the Dart layer since that is where all of the app logic sits.</p><p>But what does the Dart layer look like?</p><p>Flutter compiles your Dart to native assembly code and uses formats that have not been publicly documented in-depth let alone fully decompiled and recompiled.</p><p>For comparison other platforms like React Native just bundle minified javascript which is trivial to inspect and modify, additionally the bytecode for Java on Android is well documented and there are many free decompilers for it.</p><p>Despite the lack of obfuscation (by default) or encryption, Flutter apps are still extremely difficult to reverse engineer at the moment since it requires in-depth knowledge of Dart internals to even scratch the surface.</p><p>This makes Flutter very good from an intellectual property perspective, your code is <em>almost</em> safe from prying eyes.</p><p>Next I&apos;ll show you the build process of Flutter applications and explain in detail how to reverse engineer the code that it produces.</p><hr><h3 id="snapshots">Snapshots</h3><p></p><p>The Dart SDK is highly versatile, you can embed Dart code in many different configurations on many different platforms.</p><p>The simplest way to run Dart is to use the <code>dart</code> executable which just reads dart source files directly like a scripting language. It includes the primary components we call the front-end (parses Dart code), runtime (provides the environment for code to run in), and the JIT compiler.</p><p>You can also use <code>dart</code> to create and execute <a href="https://github.com/dart-lang/sdk/wiki/Snapshots?ref=blog.tst.sh">snapshots</a>, a pre-compiled form of Dart which is commonly used to speed up frequently used command line tools (like <code>pub</code>).</p><pre><code>#lint shell
ping@debian:~/Desktop$ time dart hello.dart
Hello, World!

real    0m0.656s
user    0m0.920s
sys     0m0.084s

ping@debian:~/Desktop$ dart --snapshot=hello.snapshot hello.dart
ping@debian:~/Desktop$ time dart hello.snapshot
Hello, World!

real    0m0.105s
user    0m0.208s
sys     0m0.016s</code></pre><p>As you can see, the start-up time is significantly lower when you use snapshots.</p><p>The default snapshot format is <a href="https://github.com/dart-lang/sdk/wiki/Kernel-Documentation?ref=blog.tst.sh">kernel</a>, an intermediate representation of Dart code equivalent to the AST.</p><p>When running a Flutter app in debug mode, the flutter tool creates a kernel snapshot and runs it in your android app with the debug runtime + JIT. This gives you the ability to debug your app and modify code live at runtime with hot reload.</p><p>Unfortunately for us, using your own JIT compiler is frowned upon in the mobile industry due to increased concerns of RCEs. iOS actually prevents you from executing dynamically generated code like this entirely.</p><p>There are two more types of snapshots though, <code>app-jit</code> and <code>app-aot</code>, these contain compiled machine code that can be initialized quicker than kernel snapshots but aren&apos;t cross-platform.</p><p>The final type of snapshot, <code>app-aot</code>, contains only machine code and no kernel. These snapshots are generated using the <code>gen_snapshots</code> tool found in <code>flutter/bin/cache/artifacts/engine/&lt;arch&gt;/&lt;target&gt;/</code>, more on that later.</p><p>They are a little more than just a compiled version of Dart code though, in fact they are a full &quot;snapshot&quot; of the VMs heap just before main is called. This is a unique feature of Dart and one of the reasons it initializes so quickly compared to other runtimes.</p><p>Flutter uses these AOT snapshots for release builds, you can see the files that contain them in the file tree for an Android APK built with <code>flutter build apk</code>:</p><pre><code>#lint shell
ping@debian:~/Desktop/app/lib$ tree .
.
&#x251C;&#x2500;&#x2500; arm64-v8a
&#x2502;   &#x251C;&#x2500;&#x2500; libapp.so
&#x2502;   &#x2514;&#x2500;&#x2500; libflutter.so
&#x2514;&#x2500;&#x2500; armeabi-v7a
    &#x251C;&#x2500;&#x2500; libapp.so
    &#x2514;&#x2500;&#x2500; libflutter.so</code></pre><p>Here you can see the two libapp.so files which are a64 and a32 snapshots as ELF binaries.</p><p>The fact that <code>gen_snapshots</code> outputs an ELF / shared object here might be a bit misleading, it does not expose dart methods as symbols that can be called externally. Instead, these files are containers for the &quot;clustered snapshot&quot; format but with compiled code in the separate executable section, here is how they are structured:</p><pre><code>#lint shell
ping@debian:~/Desktop/app/lib/arm64-v8a$ aarch64-linux-gnu-objdump -T libapp.so

libapp.so:     file format elf64-littleaarch64

DYNAMIC SYMBOL TABLE:
0000000000001000 g    DF .text  0000000000004ba0 _kDartVmSnapshotInstructions
0000000000006000 g    DF .text  00000000002d0de0 _kDartIsolateSnapshotInstructions
00000000002d7000 g    DO .rodata        0000000000007f10 _kDartVmSnapshotData
00000000002df000 g    DO .rodata        000000000021ad10 _kDartIsolateSnapshotData</code></pre><p>The reason why AOT snapshots are in shared object form instead of a regular snapshot file is because machine code generated by <code>gen_snapshot</code> needs to be loaded into executable memory when the app starts and the nicest way to do that is through an ELF file.</p><p>With this shared object, everything in the <code>.text</code> section will be loaded into executable memory by the linker allowing the Dart runtime to call into it at any time.</p><p>You may have noticed there are two snapshots: the VM snapshot and the Isolate snapshot.</p><p>DartVM has a second isolate that does background tasks called the vm isolate, it is required for <code>app-aot</code> snapshots since the runtime can&apos;t dynamically load it in as the <code>dart</code> executable would.</p><hr><h3 id="the-dart-sdk">The Dart SDK</h3><p></p><p>Thankfully Dart is completely open source so we don&apos;t have to fly blind when reverse engineering the snapshot format.</p><p>Before creating a testbed for generating and disassembling snapshots you have to set up the Dart SDK, there is documentation on how to build it here: <a href="https://github.com/dart-lang/sdk/wiki/Building?ref=blog.tst.sh">https://github.com/dart-lang/sdk/wiki/Building</a>.</p><p>You want to generate libapp.so files typically orchestrated by the flutter tool, but there doesn&apos;t seem to be any documentation on how to do that yourself.</p><p>The flutter sdk ships binaries for <code>gen_snapshot</code> which is not part of the standard <code>create_sdk</code> build target you usually use when building dart.</p><p>It does exist as a separate target in the SDK though, you can build the <code>gen_snapshot</code> tool for arm with this command:</p><pre><code class="language-sh">./tools/build.py -m product -a simarm gen_snapshot</code></pre><p>Normally you can only generate snapshots for the architecture you are running on, to work around that they have created sim targets which simulate snapshot generation for the target platform. This has some limitations such as not being able to make aarch64 or x86_64 snapshots on a 32 bit system.</p><p>Before making a shared object you have to compile a dill file using the front-end:</p><pre><code>~/flutter/bin/cache/dart-sdk/bin/dart ~/flutter/bin/cache/artifacts/engine/linux-x64/frontend_server.dart.snapshot --sdk-root ~/flutter/bin/cache/artifacts/engine/common/flutter_patched_sdk_product/ --strong --target=flutter --aot --tfa -Ddart.vm.product=true --packages .packages --output-dill app.dill package:foo/main.dart</code></pre><p>Dill files are actually the same format as kernel snapshots, their format is specified here: <a href="https://github.com/dart-lang/sdk/blob/master/pkg/kernel/binary.md?ref=blog.tst.sh">https://github.com/dart-lang/sdk/blob/master/pkg/kernel/binary.md</a></p><p>This is the format used as a common representation of dart code between tools, including <code>gen_snapshot</code> and <code>analyzer</code>. </p><p>With the app.dill we can finally generate a libapp.so using this command:</p><pre><code class="language-sh">gen_snapshot --causal_async_stacks --deterministic --snapshot_kind=app-aot-elf --elf=libapp.so --strip app.dill</code></pre><p>Once you are able to manually generate the libapp.so, it is easy to modify the SDK to print out all of the debug information needed to reverse engineer the AOT snapshot format.</p><p>As a side note, Dart was actually designed by some of the people who created JavaScript&apos;s V8 which is arguably the most advanced interpreter ever made. DartVM is incredibly well engineered and I don&apos;t think people give its creators enough credit.</p><hr><h3 id="anatomy-of-a-snapshot">Anatomy of a snapshot</h3><p></p><p>The AOT snapshot itself is quite complex, it is a custom binary format with no documentation. You may be forced to step through the serialization process manually in a debugger to implement a tool that can read the format.</p><p>The source files relevant to snapshot generation can be found here:</p><ul><li>Cluster serialization / deserialization<br><code><a href="https://github.com/dart-lang/sdk/blob/7340a569caac6431d8698dc3788579b57ffcf0c6/runtime/vm/clustered_snapshot.h?ref=blog.tst.sh">vm/clustered_snapshot.h</a></code><br><code><a href="https://github.com/dart-lang/sdk/blob/7340a569caac6431d8698dc3788579b57ffcf0c6/runtime/vm/clustered_snapshot.cc?ref=blog.tst.sh">vm/clustered_snapshot.cc</a></code></li><li>ROData serialization<br><code><a href="https://github.com/dart-lang/sdk/blob/7340a569caac6431d8698dc3788579b57ffcf0c6/runtime/vm/image_snapshot.h?ref=blog.tst.sh">vm/image_snapshot.h</a></code><br><code><a href="https://github.com/dart-lang/sdk/blob/7340a569caac6431d8698dc3788579b57ffcf0c6/runtime/vm/image_snapshot.cc?ref=blog.tst.sh">vm/image_snapshot.cc</a></code></li><li>ReadStream / WriteStream<br><code><a href="https://github.com/dart-lang/sdk/blob/7340a569caac6431d8698dc3788579b57ffcf0c6/runtime/vm/datastream.h?ref=blog.tst.sh">vm/datastream.h</a></code></li><li>Object definitions<br><code><a href="https://github.com/dart-lang/sdk/blob/7340a569caac6431d8698dc3788579b57ffcf0c6/runtime/vm/object.h?ref=blog.tst.sh">vm/object.h</a></code></li><li>ClassId enum<br><code><a href="https://github.com/dart-lang/sdk/blob/7340a569caac6431d8698dc3788579b57ffcf0c6/runtime/vm/class_id.h?ref=blog.tst.sh">vm/class_id.h</a></code></li></ul><p>It took me about two weeks to implement a command line utility that is capable of parsing a snapshot, giving us complete access to the heap of a compiled app.</p><p>As an overview, here is the layout of clustered snapshot data:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/02/snapshot_data-1.png" class="kg-image" alt="Reverse engineering Flutter apps (Part 1)" loading="lazy"></figure><p>Every <code>RawObject*</code> in the Isolate gets serialized by a corresponding <code>SerializationCluster</code> instance depending on its class id. These objects can contain anything from code, instances, types, primitives, closures, constants, etc. More on that later.</p><p>After deserializing the VM isolate snapshot, every object in its heap gets added to the Isolate snapshot object pool allowing them to be referenced in the same context.</p><p>Clusters are serialized in three stages: Trace, Alloc, and Fill.</p><p>In the trace stage, root objects are added to a queue along with the objects they reference in a breadth first search. At the same time a <code>SerializationCluster</code> instance is created corresponding to each class type.</p><p>Root objects are a static set of objects used by the vm in the isolate&apos;s <code>ObjectStore</code> which we will use later to locate libraries and classes. The VM snapshot includes <code>StubCode</code> base objects which are shared between all isolates.</p><p>Stubs are basically hand written sections of assembly that dart code calls into, allowing it to communicate safely with the runtime.</p><p>After tracing, cluster info is written containing basic information about the clusters, most importantly the number of objects to allocate.</p><p>In the alloc stage, each clusters <code>WriteAlloc</code> method is called which writes any information needed to allocate raw objects. Most of the time all this method does is write the class id and number of objects that are part of this cluster.</p><p>The objects that are part of each cluster are also assigned an incrementing object id in the order they are allocated, this is used later during the fill stage when resolving object references.</p><p>You may have noticed the lack of any indexing and cluster size information, the entire snapshot has to be read fully in order to get any meaningful data out of it. So to actually do any reverse engineering you must either implement deserialization routines for 31+ cluster types (which I have done) or extract information by loading it into a modified runtime (which is difficult to do cross-architecture).</p><p>Here is a simplified example of what the structure of the clusters would be for an array <code>[123, 42]</code>:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/02/cluster_alloc-3.png" class="kg-image" alt="Reverse engineering Flutter apps (Part 1)" loading="lazy"></figure><p>If an object references another object like an array element, the serializer writes the object id initially assigned during the alloc phase as shown above.</p><p>In the case of simple objects like Mints and Smis, they are constructed entirely in the alloc stage because they don&apos;t reference any other objects.</p><p>After that the ~107 root refs are written including object ids for core types, libraries, classes, caches, static exceptions and several other miscellaneous objects.</p><p>Finally, ROData objects are written which are directly mapped to <code>RawObject*</code>s in-memory to avoid an extra deserialization step.</p><p>The most important type of ROData is <code>RawOneByteString</code> which is used for library / class / function names. ROData is also referenced by offset being the only place in the snapshot data where decoding is optional.</p><p>Similar to ROData, <code>RawInstruction</code> objects are direct pointers to snapshot data but are stored in the executable instruction symbol rather than main snapshot data.</p><p>Here is a dump of serialization clusters that are typically written when compiling an app:</p><pre><code>#lint cluster-tbl
idx | cid | ClassId enum        | Cluster name
----|-----|---------------------|----------------------------------------
  0 |   5 | Class               | ClassSerializationCluster
  1 |   6 | PatchClass          | PatchClassSerializationCluster
  2 |   7 | Function            | FunctionSerializationCluster
  3 |   8 | ClosureData         | ClosureDataSerializationCluster
  4 |   9 | SignatureData       | SignatureDataSerializationCluster
  5 |  12 | Field               | FieldSerializationCluster
  6 |  13 | Script              | ScriptSerializationCluster
  7 |  14 | Library             | LibrarySerializationCluster
  8 |  17 | Code                | CodeSerializationCluster
  9 |  20 | ObjectPool          | ObjectPoolSerializationCluster
 10 |  21 | PcDescriptors       | RODataSerializationCluster
 11 |  22 | CodeSourceMap       | RODataSerializationCluster
 12 |  23 | StackMap            | RODataSerializationCluster
 13 |  25 | ExceptionHandlers   | ExceptionHandlersSerializationCluster
 14 |  29 | UnlinkedCall        | UnlinkedCallSerializationCluster
 15 |  31 | MegamorphicCache    | MegamorphicCacheSerializationCluster
 16 |  32 | SubtypeTestCache    | SubtypeTestCacheSerializationCluster
 17 |  36 | UnhandledException  | UnhandledExceptionSerializationCluster
 18 |  40 | TypeArguments       | TypeArgumentsSerializationCluster
 19 |  42 | Type                | TypeSerializationCluster
 20 |  43 | TypeRef             | TypeRefSerializationCluster
 21 |  44 | TypeParameter       | TypeParameterSerializationCluster
 22 |  45 | Closure             | ClosureSerializationCluster
 23 |  49 | Mint                | MintSerializationCluster
 24 |  50 | Double              | DoubleSerializationCluster
 25 |  52 | GrowableObjectArray | GrowableObjectArraySerializationCluster
 26 |  65 | StackTrace          | StackTraceSerializationCluster
 27 |  72 | Array               | ArraySerializationCluster
 28 |  73 | ImmutableArray      | ArraySerializationCluster
 29 |  75 | OneByteString       | RODataSerializationCluster
 30 |  95 | TypedDataInt8Array  | TypedDataSerializationCluster
 31 | 143 | &lt;instance&gt;          | InstanceSerializationCluster
...
 54 | 463 | &lt;instance&gt;          | InstanceSerializationCluster</code></pre><p>There are a few more clusters that could potentially be in a snapshot, but these are the only ones I have seen in a Flutter app so far.</p><p>In DartVM there are a static set of predefined class IDs defined in the <code>ClassId</code> enum, 142 IDs as of Dart 2.4.0 to be exact. IDs outside of that (or do not have an associated cluster) are written with separate <code>InstanceSerializationCluster</code>s.</p><p>Finally bringing the parser together I can view the structure of the snapshot from the ground up, starting with the libraries list in the root object table.</p><p>Using the object tree here&apos;s how you can locate a top level function, in this case <code>package:ftest/main.dart</code>s <code>main</code>:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.tst.sh/content/images/2020/01/dartdec-graph-1.png" class="kg-image" alt="Reverse engineering Flutter apps (Part 1)" loading="lazy"></figure><p>As you can see above the names of libraries, classes, and functions are included in release snapshots.</p><p>Dart can&apos;t really remove them without also obfuscating stack traces, see: <a href="https://github.com/flutter/flutter/wiki/Obfuscating-Dart-Code?ref=blog.tst.sh">https://github.com/flutter/flutter/wiki/Obfuscating-Dart-Code</a></p><p>Obfuscation is probably not worth the effort but this will most likely change in the future and become more streamlined similar to proguard on Android or sourcemaps on the web.</p><p>The actual machine code is stored in <code>Instructions</code> objects pointed to by <code>Code</code> objects from an offset to the start of the instruction data.</p><hr><h3 id="rawobject">RawObject</h3><p></p><p>Under the hood all managed objects in DartVM are called <code>RawObject</code>s, in true DartVM fashion these classes are all defined in a single 3,000 line file found at <code>vm/raw_object.h</code>.</p><p>In generated code you can access and move around <code>RawObject*</code>s however you want as long as you yield according to an incremental write barrier mask, the GC appears to be able to track references through passive scanning alone.</p><p>Here is the class tree:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/02/classTree-1.png" class="kg-image" alt="Reverse engineering Flutter apps (Part 1)" loading="lazy"></figure><p><code>RawInstance</code>s are the traditional <code>Object</code>s you pass around Dart code and invoke methods on, all of them have an equivalent type in dart land. Non-instance objects however are internal and only exist to leverage reference tracking and garbage collection, they do not have equivalent dart types.</p><p>Each object starts with a uint32_t containing the following tags:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2020/02/objtags-1.png" class="kg-image" alt="Reverse engineering Flutter apps (Part 1)" loading="lazy"></figure><p>Class IDs here are the same as before with cluster serialization, they are defined in <code><a href="https://github.com/dart-lang/sdk/blob/7340a569caac6431d8698dc3788579b57ffcf0c6/runtime/vm/class_id.h?ref=blog.tst.sh">vm/class_id.h</a></code> but also include user-defined starting at <code>kNumPredefinedCids</code>.</p><p>Size and GC data tags are used for garbage collection, most of the time they can be ignored.</p><p>If the canonical bit is set that means that this object is unique and no other object is equal to it, like with <code>Symbol</code>s and <code>Type</code>s.</p><p>Objects are very light and the size of <code>RawInstance</code> is usually only 4 bytes, they surprisingly do not use virtual methods at all either.</p><p>All of this means allocating an object and filling in its fields can be done virtually for free, something we do quite lot in Flutter.</p><hr><h3 id="hello-world-">Hello, World!</h3><p></p><p>Cool, we can locate functions by name but how do we figure out what they actually do?</p><p>As expected reverse engineering from here on is a bit more difficult because we are digging through the assembly code contained in <code>Instructions</code> objects.</p><p>Instead of using a modern compiler backend like clang, Dart actually uses its JIT compiler for code generation but with a couple AOT specific optimizations.</p><p>If you have never worked with JIT code, it is a bit bloated in some places compared to what the equivalent C code would produce. Not that Dart is doing a bad job though, it&apos;s designed to be generated quickly at runtime and the hand-written assembly for common instructions often beats clang/gcc in terms of performance.</p><p>Generated code being less micro-optimized actually works heavily to our advantage since it closer resembles the higher level IR used to generate it.</p><p>Most of the relevant code generation can be found in:</p><ul><li><code>vm/compiler/backend/il_&lt;arch&gt;.cc</code></li><li><code>vm/compiler/assembler/assembler_&lt;arch&gt;.cc</code></li><li><code>vm/compiler/asm_intrinsifier_&lt;arch&gt;.cc</code></li><li><code>vm/compiler/graph_intrinsifier_&lt;arch&gt;.cc</code></li></ul><p>Here is the register layout and calling conventions for dart&apos;s A64 assembler:</p><pre><code>#lint reg-tbl
       r0 |     | Returns
r0  -  r7 |     | Arguments
r0  - r14 |     | General purpose
      r15 | sp  | Dart stack pointer
      r16 | ip0 | Scratch register
      r17 | ip1 | Scratch register
      r18 |     | Platform register
r19 - r25 |     | General purpose
r19 - r28 |     | Callee saved registers
      r26 | thr | Current thread
      r27 | pp  | Object pool
      r28 | brm | Barrier mask
      r29 | fp  | Frame pointer
      r30 | lr  | Link register
      r31 | zr  | Zero / CSP</code></pre><p>This ABI follows the standard AArch64 calling conventions <a href="https://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf?ref=blog.tst.sh">here</a> but with a few global registers:</p><ul><li>R26 / THR: Pointer to the running vm <code>Thread</code>, see <a href="https://github.com/dart-lang/sdk/blob/7340a569caac6431d8698dc3788579b57ffcf0c6/runtime/vm/thread.h?ref=blog.tst.sh">vm/thread.h</a></li><li>R27 / PP: Pointer to the <code>ObjectPool</code> of the current context, see <a href="https://github.com/dart-lang/sdk/blob/7340a569caac6431d8698dc3788579b57ffcf0c6/runtime/vm/object.h?ref=blog.tst.sh#L4275">vm/object.h</a></li><li>R28 / BRM: The barrier mask, used for incremental garbage collection</li></ul><p> Similarly, this is the register layout for A32:</p><pre><code>#lint reg-tbl
r0 -  r1 |     | Returns
r0 -  r9 |     | General purpose
r4 - r10 |     | Callee saved registers
      r5 | pp  | Object pool
     r10 | thr | Current thread
     r11 | fp  | Frame pointer
     r12 | ip  | Scratch register
     r13 | sp  | Stack pointer
     r14 | lr  | Link register
     r15 | pc  | Program counter</code></pre><p>While A64 is a more common target I&apos;ll mostly be covering A32 since its is simpler to read and disassemble.</p><p>You can view the IR along with the disassembly by passing <code>--disassemble-optimized</code> to <code>gen_snapshot</code>, but note this only works on the debug/release targets and not product.</p><p>As an example, when compiling hello world:</p><pre><code class="language-dart">void hello() {
  print(&quot;Hello, World!&quot;);
}</code></pre><p>Scrolling down a bit in the disassembly you will find:</p><pre><code>#lint dartvm-dasm
Code for optimized function &apos;package:dectest/hello_world.dart_::_hello&apos; {
        ;; B0
        ;; B1
        ;; Enter frame
0xf69ace60    e92d4800               stmdb sp!, {fp, lr}
0xf69ace64    e28db000               add fp, sp, #0
        ;; CheckStackOverflow:8(stack=0, loop=0)
0xf69ace68    e59ac024               ldr ip, [thr, #+36]
0xf69ace6c    e15d000c               cmp sp, ip
0xf69ace70    9bfffffe               blls +0 ; 0xf69ace70
        ;; PushArgument(v3)
0xf69ace74    e285ca01               add ip, pp, #4096
0xf69ace78    e59ccfa7               ldr ip, [ip, #+4007]
0xf69ace7c    e52dc004               str ip, [sp, #-4]!
        ;; StaticCall:12( print&lt;0&gt; v3)
0xf69ace80    ebfffffe               bl +0 ; 0xf69ace80
0xf69ace84    e28dd004               add sp, sp, #4
        ;; ParallelMove r0 &lt;- C
0xf69ace88    e59a0060               ldr r0, [thr, #+96]
        ;; Return:16(v0)
0xf69ace8c    e24bd000               sub sp, fp, #0
0xf69ace90    e8bd8800               ldmia sp!, {fp, pc}
0xf69ace94    e1200070               bkpt #0x0
}</code></pre><p>What is printed here is slightly different from a snapshot built in product but the important part is that we can see the IR instructions alongside assembly.</p><p>Breaking it down:</p><pre><code>#lint dartvm-dasm
        ;; Enter frame
0xf6a6ce60    e92d4800               stmdb sp!, {fp, lr}
0xf6a6ce64    e28db000               add fp, sp, #0</code></pre><p>This is a standard function prologue, the frame pointer of the caller and link register are pushed to the stack after which the frame pointer is set to the bottom of the function stack frame.</p><p>As with the standard ARM ABI, this uses a full-descending stack meaning it grows backwards in memory.<br></p><pre><code>#lint dartvm-dasm
        ;; CheckStackOverflow:8(stack=0, loop=0)
0xf6a6ce68    e59ac024               ldr ip, [thr, #+36]
0xf6a6ce6c    e15d000c               cmp sp, ip
0xf6a6ce70    9bfffffe               blls +0 ; 0xf6a6ce70</code></pre><p>This is a simple routine which does what you probably guessed, checks if the stack overflowed.</p><p>Sadly their disassembler does not annotate either thread fields or branch targets so you have to do some digging.</p><p>A list of field offsets can be found in <code>vm/compiler/runtime_offsets_extracted.h</code>, which defines <code>Thread_stack_limit_offset = 36</code> telling us that the field accessed is the threads stack limit.</p><p>After the stack pointer is compared, it calls the <code>stackOverflowStubWithoutFpuRegsStub</code> stub if it has overflowed. The branch target in the disassembly appears to be un-patched but we can still inspect the binary afterwards to confirm.<br></p><pre><code>#lint dartvm-dasm
        ;; PushArgument(v3)
0xf6a6ce74    e285ca01               add ip, pp, #4096
0xf6a6ce78    e59ccfa7               ldr ip, [ip, #+4007]
0xf6a6ce7c    e52dc004               str ip, [sp, #-4]!</code></pre><p>Here an object from the object pool is pushed onto the stack. Since the offset is too big to fit in an ldr offset encoding it uses an extra add instruction.</p><p>This object is in fact our &quot;Hello, World!&quot; string as a <code>RawOneByteString*</code> stored in the <code>globalObjectPool</code> of our isolate at offset 8103.</p><p>You may have noticed that offsets are misaligned, this is because object pointers are tagged with <code>kHeapObjectTag</code> from <code>vm/pointer_tagging.h</code>, in this case all of the pointers to <code>RawObject</code>s in compiled code are offset by 1.<br></p><pre><code>#lint dartvm-dasm
        ;; StaticCall:12( print&lt;0&gt; v3)
0xf6a6ce80    ebfffffe               bl +0 ; 0xf6a6ce80
0xf6a6ce84    e28dd004               add sp, sp, #4</code></pre><p>Here print is called followed by the string argument being popped from the stack.</p><p>Like before the branch hasn&apos;t been resolved, it is a relative branch to the entry point for <code>print</code> in dart:core.<br></p><pre><code>#lint dartvm-dasm
        ;; ParallelMove r0 &lt;- C
0xf69ace88    e59a0060               ldr r0, [thr, #+96]</code></pre><p>Null is loaded into the return register, 96 being the offset to the null object field in a <code>Thread</code>.<br></p><pre><code>#lint dartvm-dasm
        ;; Return:16(v0)
0xf69ace8c    e24bd000               sub sp, fp, #0
0xf69ace90    e8bd8800               ldmia sp!, {fp, pc}
0xf69ace94    e1200070               bkpt #0x0</code></pre><p>And finally the function epilogue, the stack frame is restored along with any callee-saved registers. Since lr was pushed last, popping it into pc will cause the function to return.</p><p>From now on I&apos;ll be using snippets from my own disassembler which has less problems than the builtin one.</p><hr><h2 id="continued-in-part-2-">Continued in <a href="https://blog.tst.sh/reverse-engineering-flutter-apps-part-2/">Part 2</a>!</h2>]]></content:encoded></item><item><title><![CDATA[Cute little space ship]]></title><description><![CDATA[<p>Here are some pictures of my first project with Substance Painter!</p><p>First I modeled a space ship in Autodesk Inventor, this is the modelling software I have a lot of experience in so it&apos;s easy peasy.</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/Zf4y.png" class="kg-image" alt loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/I2Qt.png" class="kg-image" alt loading="lazy"></figure><p>Why does a space ship need wings? who knows.</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/OPcu_2.png" class="kg-image" alt loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/Lm1I.png" class="kg-image" alt loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/Jtp8.png" class="kg-image" alt loading="lazy"></figure><p>And to finish</p>]]></description><link>https://blog.tst.sh/cute-little-rocket-ship/</link><guid isPermaLink="false">5fa9b817c9aac25a07117258</guid><category><![CDATA[CAD]]></category><category><![CDATA[Game Design]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Sun, 14 Jul 2019 19:04:56 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2019/07/Untitled_3.PNG" medium="image"/><content:encoded><![CDATA[<img src="https://blog.tst.sh/content/images/2019/07/Untitled_3.PNG" alt="Cute little space ship"><p>Here are some pictures of my first project with Substance Painter!</p><p>First I modeled a space ship in Autodesk Inventor, this is the modelling software I have a lot of experience in so it&apos;s easy peasy.</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/Zf4y.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/I2Qt.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><p>Why does a space ship need wings? who knows.</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/OPcu_2.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/Lm1I.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/Jtp8.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><p>And to finish it off let&apos;s give it guns.</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/VmEJ.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><p>Now for the texturing part, I started with an outline:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/Hc5u.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/29li.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><p>Textured the window and added some decals:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/eqcA.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/NUmw.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/a1SX.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><p>And that&apos;s version 1, Imported it into Unreal Engine in all it&apos;s PBR glory:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/ljRP_2.jpg" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><p>I didn&apos;t really like the color scheme so I re-did everything now that I got the hang of texturing in substance.</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/1gCn.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><p>Started again, this time I put much less emphasis on the edges and made the black metal pop out more, along with breaking it up into panels.</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/sXCy.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><p>Imported into unreal engine again:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/thw9.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/wC58.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/gjdQ.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><p>Went back to add some more detail to the texture, screwing around with metal wear.</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/eTI4.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/hOUy.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/LZ0i.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/07/2DGS.png" class="kg-image" alt="Cute little space ship" loading="lazy"></figure><p>That&apos;s all folks, thanks for tuning in.</p>]]></content:encoded></item><item><title><![CDATA[Raytracing in the browser]]></title><description><![CDATA[<p>As a small side project I built a raytracer in Dart:</p><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/CmU41mE5OB4?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><p>It took an enormous amount of time to render this video, around 12 hours for 3600 frames at 3840x2160.</p><p>Source code: <a href="https://gist.github.com/PixelToast/84377d383c20d056664e80849c5b79e9?ref=blog.tst.sh">https://gist.github.com/PixelToast/84377d383c20d056664e80849c5b79e9</a></p>]]></description><link>https://blog.tst.sh/web-raytracer/</link><guid isPermaLink="false">5fa9b817c9aac25a07117257</guid><category><![CDATA[Web Dev]]></category><category><![CDATA[Dart]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Sun, 16 Jun 2019 07:23:04 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2019/06/download--3-.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.tst.sh/content/images/2019/06/download--3-.png" alt="Raytracing in the browser"><p>As a small side project I built a raytracer in Dart:</p><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/CmU41mE5OB4?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><p>It took an enormous amount of time to render this video, around 12 hours for 3600 frames at 3840x2160.</p><p>Source code: <a href="https://gist.github.com/PixelToast/84377d383c20d056664e80849c5b79e9?ref=blog.tst.sh">https://gist.github.com/PixelToast/84377d383c20d056664e80849c5b79e9</a></p>]]></content:encoded></item><item><title><![CDATA[Tangent - A discord bot with full access to a Linux VM]]></title><description><![CDATA[<p>Tangent is a bot I&apos;ve been working on and testing for about a week now and I think its ready for public use.</p><p>There are a lot of reasons why allowing anyone to execute arbitrary code is a terrible idea so I had to do a lot of</p>]]></description><link>https://blog.tst.sh/tangent/</link><guid isPermaLink="false">5fa9b817c9aac25a07117256</guid><category><![CDATA[Back End]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Low Level]]></category><category><![CDATA[Servers and Networks]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Wed, 05 Jun 2019 10:50:24 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2020/01/tan-flat.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.tst.sh/content/images/2020/01/tan-flat.png" alt="Tangent - A discord bot with full access to a Linux VM"><p>Tangent is a bot I&apos;ve been working on and testing for about a week now and I think its ready for public use.</p><p>There are a lot of reasons why allowing anyone to execute arbitrary code is a terrible idea so I had to do a lot of planning beforehand.</p><p>Think you can break it? Try it out here: <a href="https://discord.gg/F2F2EdE?ref=blog.tst.sh">https://discord.gg/F2F2EdE</a><br>Github: <a href="https://github.com/PixelToast/tangent?ref=blog.tst.sh">https://github.com/PixelToast/tangent</a></p><p>My design constraints were the following:</p><ol><li>Arbitrary native code execution, not just a sandboxed interpreted language like the <a href="https://ocdoc.cil.li/?ref=blog.tst.sh">OpenComputers mod</a> for Minecraft.</li><li>Limited internet access, people should not be able to use my internet connection for nefarious purposes.</li><li>Limited CPU, memory, and disk usage.</li><li>Automatic recovery, nothing a user can do should put the bot into an unrecoverable state whether it be a fork bomb, filling the filesystem, killing all processes.</li><li>The discord bot should limit buffers on anything the VM sends, you should not be able to spam files / data / process events to the bot and cause it to run out of memory.</li><li>Discord should not be trusted, if discord server or my account were compromised it should not allow attackers to gain access to my server.</li></ol><p>The most common method for sandboxing is restricting methods at the language level for example <a href="https://github.com/xpcall/-v4/blob/master/sbox.lua?ref=blog.tst.sh">my old Lua sandbox</a>, this is incredibly language-specific and some languages like C simply cannot be sandboxed like this.</p><p>Another common method is <a href="https://en.wikipedia.org/wiki/Chroot?ref=blog.tst.sh">chroot</a>, a Linux command which changes the root directory of the running program but can be <a href="http://pentestmonkey.net/blog/chroot-breakout-perl?ref=blog.tst.sh">easily broken out of</a> if you aren&apos;t careful.</p><p>Docker does everything I need in terms of sandboxing with little effort including limiting resources, custom network routing, etc but it is based on chroot meaning sandboxed applications share a kernel with the host system.</p><p>Sharing a kernel with the host system makes it more vulnerable to <a href="https://assured-cloud-computing.illinois.edu/files/2016/01/04132016-Ahmad.pdf?ref=blog.tst.sh">side channel attacks</a> and <a href="https://seclists.org/oss-sec/2019/q1/119?ref=blog.tst.sh">nasty privilege escalation</a> than a hypervisor based virtual machine which is not something I would feel safe leaving running publicly for a long period time especially with the amount of hardware exploits being discovered on x86 CPUs lately.</p><p>Docker also doesn&apos;t have support for qcow2 snapshots meaning resetting a containers state is much slower than a qemu / libvirt based machine, something that is very important for handling people continuously bricking the vm as a denial of service attack.</p><p>It seems pretty clear now that a full Linux VM in a hypervisor would be much better than a container for what I&apos;m trying to do.</p><h3 id="my-solution">My solution</h3><p>Right now, Tangent uses <a href="https://libvirt.org/?ref=blog.tst.sh">libvirt</a> to manage a qemu powered Debian 9 virtual machine and communicates to it through a custom json rpc like protocol.</p><p><code>tangent-server</code> contains the server that runs as an unprivileged user on the VM, allowing the bot on the host machine to start processes, read stdin/stdout, and access files safely.</p><p>In its current configuration the VM is on a closed virtual network with only the host machine being routable (<code>192.168.69.1</code>), iptables are set up so requests from the VM to the host are blocked to prevent it from attempting to connect to SSH or other services it should not have access to.</p><p>The only way for information to go in and out of the VM is through connections initiated by the host.</p><p>System resources are also heavily limited with 1 thread, 256MB of memory, and a 16GB virtual disk.</p><p>If you manage to put the VM in an unusable state like by killing the server process continuously, Tangent will automatically use virsh to reboot the VM which only takes around 4 seconds.</p><p>As a last resort if someone obtains root and bricks the system a qcow2 snapshot can restore the system state to brand new using the <code>qclean</code> command, this is actually much faster than rebooting the VM.</p><p>The bot itself is a Dart application and is designed to be as fault tolerant as possible, all buffers that the VM send to are capped, any malformed packets will instantly terminate the connection, and all of the async wrappers for files and processes are destroyed properly when closed.</p><p>Dart is especially good for this job because of its powerful and safe <a href="https://api.dartlang.org/stable/2.3.1/dart-async/dart-async-library.html?ref=blog.tst.sh">async library</a>, it eliminates a lot of corner cases and concurrency problems you usually get when designing asynchronous code.</p><h3 id="where-the-fun-starts">Where the fun starts</h3><p>So far I&apos;ve installed the SDKs of over 50 languages to the VM, including:<br>sh, bash, ARM assembly, x86 assembly, C, C++, Lua 5.3/5.2/5.1, LuaJIT, Python 2/3, JavaScript, Perl, Java, Lisp, Brainfuck, C#, F#, Haskell, PHP, COBOL, Golang, Ruby, APL, Prolog, OCaml, SML, Crystal, Ada, D, Groovy, Dart, Erlang, FORTH, Pascal, Fortran, Hack, Julia, Kotlin, Scala, Swift, TypeScript, Verilog, WebAssembly, Scheme, AWK, Clojure, TI-BASIC, Batch, Racket, Rust. Over 12GB of packages!<br>All with bot commands that compile and run them for a single file. </p><p>Here are some examples:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/06/Selection_617.png" class="kg-image" alt="Tangent - A discord bot with full access to a Linux VM" loading="lazy"></figure><h3 id="but-wait-there-s-more">But wait there&apos;s more</h3><p>You can upload and download files to it!</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/06/birb.png" class="kg-image" alt="Tangent - A discord bot with full access to a Linux VM" loading="lazy"></figure><h3 id="in-conclusion">In conclusion</h3><p>It was a fun project to work on, I hope somebody finds a good use for it.<br>Back to working on my new game.</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/06/432649674848206858.gif" class="kg-image" alt="Tangent - A discord bot with full access to a Linux VM" loading="lazy"></figure><p>Discord: <a href="https://discord.gg/F2F2EdE?ref=blog.tst.sh">https://discord.gg/F2F2EdE</a></p>]]></content:encoded></item><item><title><![CDATA[KOHCTPYKTOP 2: Electric Boogaloo]]></title><description><![CDATA[<p>I&apos;ve made a zachtronics inspired digital circuit simulator in AngularDart.</p><p>It was inspired by is one of my favorite zachtronics games, KOHCTPYKTOP. It is a game where you design digital circuits on a grid, closely resembling the <a href="https://en.wikipedia.org/wiki/CMOS?ref=blog.tst.sh">CMOS</a> process which is how ICs are made in real life.</p>]]></description><link>https://blog.tst.sh/kohctpyktop-2-electric-bogaloo/</link><guid isPermaLink="false">5fa9b817c9aac25a07117253</guid><category><![CDATA[Dart]]></category><category><![CDATA[Game Design]]></category><category><![CDATA[Hardware Design]]></category><category><![CDATA[Low Level]]></category><category><![CDATA[Web Dev]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Fri, 10 May 2019 07:08:03 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2019/05/HR5lw-1.gif" medium="image"/><content:encoded><![CDATA[<img src="https://blog.tst.sh/content/images/2019/05/HR5lw-1.gif" alt="KOHCTPYKTOP 2: Electric Boogaloo"><p>I&apos;ve made a zachtronics inspired digital circuit simulator in AngularDart.</p><p>It was inspired by is one of my favorite zachtronics games, KOHCTPYKTOP. It is a game where you design digital circuits on a grid, closely resembling the <a href="https://en.wikipedia.org/wiki/CMOS?ref=blog.tst.sh">CMOS</a> process which is how ICs are made in real life.</p><p>If you haven&apos;t played it already I highly recommend checking it out: <a href="http://www.zachtronics.com/kohctpyktop-engineer-of-the-people/?ref=blog.tst.sh"><a href="http://www.zachtronics.com/kohctpyktop-engineer-of-the-people/?ref=blog.tst.sh">http://www.zachtronics.com/kohctpyktop-engineer-of-the-people/</a></a></p><p>My recreation features a configurable PCB size, panning, live simulation, and more!</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/05/sNcOm.gif" class="kg-image" alt="KOHCTPYKTOP 2: Electric Boogaloo" loading="lazy"></figure><p>At the bottom there is a toolbox containing the 4 primary elements:</p><ol><li>Metal</li><li>N-Type silicon</li><li>P-Type silicon</li><li>Via</li></ol><p>These elements can be placed by click-dragging to form traces:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/05/Z6Gui.gif" class="kg-image" alt="KOHCTPYKTOP 2: Electric Boogaloo" loading="lazy"></figure><p>Metal and silicon conduct electricity provided by inputs to your IC, and the two layers can be connected with a via:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/05/GZLkG.gif" class="kg-image" alt="KOHCTPYKTOP 2: Electric Boogaloo" loading="lazy"></figure><p>Metal is placed on a layer above Silicon where it conducts electricity separately, the two types of silicon can&apos;t be overlapped but can however be used to create gates.</p><p>Gates can be created by placing different typed silicon over one another, here is a simple circuit acting as an AND gate:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/05/w2Emu.gif" class="kg-image" alt="KOHCTPYKTOP 2: Electric Boogaloo" loading="lazy"></figure><p>You may have noticed a little blip on the output when A turned off at the same time B turned on, this was because it takes time for gates to activate and deactivate:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/05/a2vWo.gif" class="kg-image" alt="KOHCTPYKTOP 2: Electric Boogaloo" loading="lazy"></figure><p>Here it takes time for each gate to turn but the output turns off as soon as the input does because gates are just bridges, the delay is only in it&apos;s activation and not the electricity that flows through.</p><p>To get a proper delay you would do this:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/05/h8fxy.gif" class="kg-image" alt="KOHCTPYKTOP 2: Electric Boogaloo" loading="lazy"></figure><p>Cool! What about an OR gate:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/05/tQmXE.gif" class="kg-image" alt="KOHCTPYKTOP 2: Electric Boogaloo" loading="lazy"></figure><p>Since there are no diodes the simplest way to make an OR gate is to AND both inputs with VCC and then combine the result.</p><p>If PNP transistors are used instead of NPN what you get is a simple NAND gate:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/05/tLgY7.gif" class="kg-image" alt="KOHCTPYKTOP 2: Electric Boogaloo" loading="lazy"></figure><p>XOR gates are a bit more complicated, here is a version that uses 1 PNP and 3 NPN transistors:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/05/6ZpKF-1.gif" class="kg-image" alt="KOHCTPYKTOP 2: Electric Boogaloo" loading="lazy"></figure><p>Now for something more interesting, a 2 bit full adder:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/05/u5br5.gif" class="kg-image" alt="KOHCTPYKTOP 2: Electric Boogaloo" loading="lazy"></figure><p>There is a test build up at <a href="https://c.tst.sh/?ref=blog.tst.sh">https://c.tst.sh/</a> let me know what you think &#x2764;&#xFE0F;</p>]]></content:encoded></item><item><title><![CDATA[Emulating the VEX Cortex]]></title><description><![CDATA[<p>If you have ever competed in VEX you know how painful programming the cortex is.</p><p>In order to develop your robot code you have to plug into the robot or controller, upload, wait several seconds, reset the field, test your code, and repeat. Sometimes you may not even have physical</p>]]></description><link>https://blog.tst.sh/emulating-the-vex-cortex/</link><guid isPermaLink="false">5fa9b817c9aac25a07117252</guid><category><![CDATA[Low Level]]></category><category><![CDATA[C/C++]]></category><category><![CDATA[Assembly]]></category><category><![CDATA[ARM]]></category><category><![CDATA[Robotics]]></category><category><![CDATA[Hardware Design]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Thu, 10 Jan 2019 17:55:58 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2019/01/Selection_590.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.tst.sh/content/images/2019/01/Selection_590.png" alt="Emulating the VEX Cortex"><p>If you have ever competed in VEX you know how painful programming the cortex is.</p><p>In order to develop your robot code you have to plug into the robot or controller, upload, wait several seconds, reset the field, test your code, and repeat. Sometimes you may not even have physical access to a robot which makes the entire process even slower.</p><p>So far has been only one way to simulate your robot which is RVW (Robot Virtual Worlds). Unfortunately RVW only works for RobotC code and you only have a handful of pre-built virtual robots to choose from making it useless for most teams.</p><p>What compounds issues even more is that the Cortex does not have any officially supported debugging functionality, we do not have a JTAG port to attach a debugger when things go wrong.</p><p>I aimed to solve this issue by implementing a way of simulating your robot hardware seamlessly but there a few technical challenges to overcome before that is possible, to explain these challenges first lets look at a block diagram of the cortex:</p><figure class="kg-card kg-image-card"><img src="https://blog.tst.sh/content/images/2019/05/cortex--2-.png" class="kg-image" alt="Emulating the VEX Cortex" loading="lazy"></figure><p>Ideally we do full emulation including both the supervisor and user SoCs so that we can execute the exact binaries that are used on a real robot, I quickly realized how difficult that would be as the supervisor&apos;s pinout is not documented and the protocol it uses to communicate with VEXNet keys is not documented.</p><!--kg-card-begin: markdown--><p>The user SoC is a STM32F103VD with 384K flash, 64k ram, and a Cortex-M3 armv7-m processor. QEMU itself supports Cortex-M3 but only a very limited number of SoCs and development boards not including anything stm32.</p>
<p>Support for stm32 specifically is extremely important because timers, interrupt control, dma, and most other mmio functionality is vendor specific.</p>
<p>Turns out there is a qemu fork that adds stm32 support for the stm32f103xx SoCs <a href="https://github.com/beckus/qemu_stm32/?ref=blog.tst.sh">here</a>.<br>
This fork includes enough to get PROS running out of the box and even print messages to UART without any modifications to the kernel.</p>
<p>This is great but unfortunately I2C and SPI aren&apos;t implemented and the ADC is incomplete (lacks continuous conversion mode). I need SPI for the supervisor or user code won&apos;t even run, I2C is needed to simulate IMEs, and the ADC is needed to simulate analog sensors.</p>
<p>To work around these issues instead of rewriting everything I opted to modify the pros kernel to do all I/O through custom hardware.</p>
<p>First step was to make a custom qemu device for the vex cortex implemented <a href="https://github.com/PixelToast/qemu_stm32/blob/vex_cortex/hw/arm/vex_cortex.c?ref=blog.tst.sh">here</a>.</p>
<p>Pretty straight forward, just a stripped down version of the stm32f103c8 device.</p>
<p>Next I needed to write qemu hardware <a href="https://github.com/PixelToast/qemu_stm32/blob/vex_cortex/hw/arm/vex_mgr.c?ref=blog.tst.sh">here</a> to communicate with the kernel, to do this i needed the following:</p>
<ol>
<li>A timer to update supervisor every couple ms</li>
<li>The irq list for the cortex so I can poke them from qemu</li>
<li>A memory region that I can control read and writes from on the qemu side</li>
</ol>
<p>Sadly the qemu internals aren&apos;t too well documented so I had to take reference from some other hardware implementaitons (dma, adc, etc.)</p>
<p>Timers are simple:</p>
<pre><code class="language-c">s-&gt;circular_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *)vex_mgr_stream_circular_timer, s)
</code></pre>
<p>This creates a nanosecond timer with <code>vex_mgr_stream_circular_timer</code> as a callback when it gets triggered.</p>
<pre><code class="language-c">uint64_t curr_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
timer_mod(s-&gt;circular_timer, curr_time + 10000000);
</code></pre>
<p>This schedules the timer 10ms in the future</p>
<p>Next we need the irq list, which is returned by <code>stm32_init</code> and can just be passed to <code>vex_mgr_create</code> in <code>vex_cortex_init</code></p>
<p>Once you have the list (named <code>pic</code>) you can just trigger them with <code>qemu_irq_pulse</code>. The stm32 header conveniently provides definitions for irqs so the following example triggers the SPI1 interrupt as the SPI controller would do when there is data in the SPI buffer sent from the supervisor:</p>
<pre><code class="language-c">qemu_irq_pulse(s-&gt;pic[STM32_SPI1_IRQ]);
</code></pre>
<p>Finally the memory region, I picked some reserved space in the stm32 memory map that&apos;s at 0x40021400 and 0xC00 (3072) bytes wide. The following code maps it to functions that handle reading and writing:</p>
<pre><code class="language-c">static const MemoryRegionOps vex_mgr_ops = {
	.read = vex_mgr_read,
	.write = vex_mgr_write,
	.endianness = DEVICE_NATIVE_ENDIAN,
	.impl = {
		.min_access_size = 1,
		.max_access_size = 4,
	}
};

sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40021400);
// s-&gt;iomem is a MemoryRegion struct
memory_region_init_io(&amp;s-&gt;iomem, OBJECT(s), &amp;vex_mgr_ops, s, &quot;vex_mgr&quot;, 0xC00);
sysbus_init_mmio(dev, &amp;s-&gt;iomem);
</code></pre>
<p>Now if the cortex ever tries to read or write from that memory region qemu will immediately call the handlers allowing two way communication.</p>
<p>As with most MMIO on the cortex, in the kernel you define the device as a convenient struct with volatile fields:</p>
<pre><code class="language-c">typedef struct {
  // Control register
  __IO uint32_t CR;
  // Args
  __IO uint32_t A1;
  __IO uint32_t A2;
  __IO uint32_t A3;
  // Results
  __IO uint32_t R1;
  __IO uint32_t R2;
  __IO uint32_t R3;
  __IO uint32_t R4;
} EMULATOR_TypeDef;

#define EMULATOR ((EMULATOR_TypeDef*)0x40021400)
</code></pre>
<p>With the struct and <code>EMULATOR</code> macro we can access the hardware registers by name instead of directly reading and writing from memory.</p>
<p>The <code>emuCall</code> helper function i wrote loads arguments and signals the control register to call a specific function on the qemu side provided a 16 bit module ID and 16 bit function ID. It is this this way instead of passing a function name as a string i.e. &quot;Motor_set&quot; for simplicity as there are only a few dozen functions.</p>
<pre><code class="language-c">inline int emuCall(uint16_t mod, uint16_t func, uint32_t a1, uint32_t a2, uint32_t a3) {
  _enterCritical();
  EMULATOR-&gt;A1 = a1;
  EMULATOR-&gt;A2 = a2;
  EMULATOR-&gt;A3 = a3;
  EMULATOR-&gt;CR = func | (mod &lt;&lt; 16);
  int out = EMULATOR-&gt;R1;
  _exitCritical();
  return out;
}
</code></pre>
<p>The enter and exit critical ensure interrupts are disabled while registers are being accessed, as an interrupt inbetween any of these reads and writes will most likely clobber the argument and return registers.</p>
<p>After that I refactored a good chunk of the kernel to do all I/O through emuCall, you can see the huge commit <a href="https://github.com/PixelToast/pros/commit/115ac7159bc0106c46653501654e63fa44d9cea0?ref=blog.tst.sh">here</a>.</p>
<p>Here are all the functions that needed to be implemented to remove dependency on standard cortex gpio:</p>
<pre><code class="language-c">int EmuSerial_init(int port, int baud, int flags);
int EmuSerial_shutdown(int port);
int EmuSerial_putc(int port, int c);

int EmuFS_programOn();
int EmuFS_programOff();
int EmuFS_erasePage(int page);

int EmuI2C_startRead(int addr, void* data, int count);
int EmuI2C_startWrite(int addr, void* data, int count);
int EmuI2C_setAddr(int addr);

int EmuGPIO_ADCInit(uint32_t data);
int EmuGPIO_setDir(int port, int mode);
int EmuGPIO_getInput(int port);
int EmuGPIO_getOutput(int port);
int EmuGPIO_setOutput(int port, int value);
int EmuGPIO_setInterrupt(int port, int edges);

int EmuMotor_get(int channel);
int EmuMotor_set(int channel, int value);
int EmuMotor_stop();

int EmuComp_init();
int EmuComp_enableStandalone();
int EmuComp_setName(char* name);
int EmuComp_getStatus(void* buff);

int EmuSystem_exit();
int EmuSystem_break();
</code></pre>
<p><a href="https://github.com/PixelToast/pros/blob/emulator/include/emulator.h?ref=blog.tst.sh">Header where these are defined</a></p>
<p>On the qemu side most of this will remain stubbed until I have an actual robot simulation to send and recieve all the data from.</p>
<p>For now only UART and the supervisor is fully implemented but that&apos;s enough to get the PROS kernel and user code fully running in an emulator:</p>
<p><img src="https://blog.tst.sh/content/images/2019/01/Selection_589.png" alt="Emulating the VEX Cortex" loading="lazy"></p>
<p>And that&apos;s what I have so far! In the future I plan on hooking this up to an Unreal Engine powered robot simulation, tune in for more updates.</p>
<p>Saucy: <a href="https://github.com/PixelToast/qemu_stm32/tree/vex_cortex/?ref=blog.tst.sh">QEMU Fork</a>, <a href="https://github.com/PixelToast/pros/tree/emulator?ref=blog.tst.sh">PROS Fork</a></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[MC6000 in hardware - Part 2: XBus]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>So, we have the instruction set the next step is to design the schematics for the MC6000 in an HDL. HDLs are programming languages which compile to circuit schematics with transistors or program FGPAS which can simulate logic gates many times faster than what a CPU could.</p>
<p>In SHENZHEN I/</p>]]></description><link>https://blog.tst.sh/shenzhen-io-in-hardware-2/</link><guid isPermaLink="false">5fa9b817c9aac25a07117250</guid><category><![CDATA[Low Level]]></category><category><![CDATA[Hardware Design]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Sat, 21 Jul 2018 15:47:02 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2018/07/2018-05-22_17_56_19-Window_2.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.tst.sh/content/images/2018/07/2018-05-22_17_56_19-Window_2.png" alt="MC6000 in hardware - Part 2: XBus"><p>So, we have the instruction set the next step is to design the schematics for the MC6000 in an HDL. HDLs are programming languages which compile to circuit schematics with transistors or program FGPAS which can simulate logic gates many times faster than what a CPU could.</p>
<p>In SHENZHEN I/O components can send each other data over a protocol called XBus:</p>
<p><img src="https://blog.tst.sh/content/images/2018/06/2018-06-01-04_05_17-Window.png" alt="MC6000 in hardware - Part 2: XBus" loading="lazy"></p>
<p>In this screenshot the microcontroller on the left is sending 69 on the x1 pin and the microcontroller on the right is saving it to acc from the x0 pin.</p>
<p>The difference between this and analog signals is XBus will guarantee the data was received by another component and you can use the <code>SLX</code> instruction to sleep until a specific bus has data.</p>
<p>Traditionally buses have a single master and multiple slaves where the master generates the clock signal and slaves can only talk to the master and vise versa, XBus however appears to be a multi-master bus like <a href="https://en.wikipedia.org/wiki/CAN_bus?ref=blog.tst.sh">CAN</a> which allows any component on the bus to talk to any other.<br>
You can probably point out an issue with doing this, if two components want to talk at the same time they will talk over each other and the data will be corrupted.</p>
<p>To solve this issue we can use uid based delegation, before sending any data you transmit the uid and the highest wins. How it works is you transmit your uid MSB first and then compare it to what you are receiving at the same time and if what you are receiving is different from what you are sending then you lose the delegation.</p>
<p>Here is an example:</p>
<p><img src="https://blog.tst.sh/content/images/2018/06/busexample-2.png" alt="MC6000 in hardware - Part 2: XBus" loading="lazy"></p>
<p>This shows what the MCs are transmitting vs what is seen on the bus where the bus is pull-down and the MCs are floating when low and pull up when high. The red means the MC failed arbitration and outputs floating for the remaining bits, you can see this for MC1 and MC2 after they write a 0 but read a 1 meaning there was someone trying to transmit using a higher id.</p>
<p>I used this method to write a multi-master XBus in Verilog that can handle an<br>
arbitrary number of devices to attempt to send data in the same clock cycle, here it is in action:</p>
<p><img src="https://blog.tst.sh/content/images/2018/07/2018-05-22_17_56_19-Window--1-.png" alt="MC6000 in hardware - Part 2: XBus" loading="lazy"></p>
<p>This is a screenshot of <a href="https://www.mentor.com/company/higher_ed/modelsim-student-edition?ref=blog.tst.sh">ModelSim PE</a> running a test on my xbus circuit where xb1 is trying to write 0x69 in the same clock as xb2 tries to write 0x7f0, xb1 loses arbitration because it has a lower id and when arbitration is over xb2 goes into the write state</p>
<p>To make it a little easier to understand, here is the state machine diagram of the xbus controller:</p>
<p><img src="https://blog.tst.sh/content/images/2018/07/2018-07-21-11_25_25-Window.png" alt="MC6000 in hardware - Part 2: XBus" loading="lazy"></p>
<p>When arbitration happens the d1 line goes high for 1ns where the controllers that are trying to write go into the arbitration state and the rest go into the read state.</p>
<p>Note that all controllers but the one sending are required to actively read the data and you might fail arbitration indefinitely if a controller with a higher id is constantly sending data and that might cause a deadlock.</p>
<p>Verilog code: <a href="https://gist.github.com/PixelToast/64f05b06537d3043f47ca20d065759c4?ref=blog.tst.sh">https://gist.github.com/PixelToast/64f05b06537d3043f47ca20d065759c4</a></p>
<p>Now i&apos;ll work on desinging the MC6000 CPU itself!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[MC6000 in hardware - Part 1: The assembler]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><a href="http://www.zachtronics.com/shenzhen-io/?ref=blog.tst.sh">SHENZHEN-IO</a> is an interactive circuit building and programming puzzle game with a programmable microcontroller called the MC6000, it has an extremely simple instruction set and no memory besides 2 registers that can only store numbers from -999 to 999.</p>
<p>Each instruction consists of a label, condition, instruction, and comment:</p>
<pre><code>foo:</code></pre>]]></description><link>https://blog.tst.sh/shenzhen-io-in-hardware/</link><guid isPermaLink="false">5fa9b817c9aac25a0711724f</guid><category><![CDATA[Low Level]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Hardware Design]]></category><category><![CDATA[Assembly]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Tue, 15 May 2018 05:25:17 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2018/05/2018-05-14-04_43_03-SHENZHEN-I_O.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.tst.sh/content/images/2018/05/2018-05-14-04_43_03-SHENZHEN-I_O.png" alt="MC6000 in hardware - Part 1: The assembler"><p><a href="http://www.zachtronics.com/shenzhen-io/?ref=blog.tst.sh">SHENZHEN-IO</a> is an interactive circuit building and programming puzzle game with a programmable microcontroller called the MC6000, it has an extremely simple instruction set and no memory besides 2 registers that can only store numbers from -999 to 999.</p>
<p>Each instruction consists of a label, condition, instruction, and comment:</p>
<pre><code>foo: +mov 50 x2 # puts 50 to XBus 2
</code></pre>
<p>Conditions can either be <code>+</code>, <code>-</code>, or blank and control if the instruction should execute after a comparison. Labels are optional and used to tell where the jmp instruction should jump to, this is just sugar to make things easier to keep track of. Registers consist of acc, bak, and 6 virtual registers coresponding to the 6 I/O ports on the MC6000. The game comes with a more in-depth manual with a language specification here: <a href="https://u.pxtst.com/QAgvo8UJR6fah.pdf?ref=blog.tst.sh">https://u.pxtst.com/QAgvo8UJR6fah.pdf</a></p>
<p>The first step to implementing this in actual hardware is to lay out the machine code:</p>
<center><h3>Registers</h3></center>
<style>
    .reglist tr td:nth-child(2n+0) {
        border-right-color: rgb(127, 136, 143) !important;
    }
</style>
<table class="reglist">
    <tr>
        <td>000</td><td>acc</td>
        <td>001</td><td>dat</td>
        <td>010</td><td>p0</td>
        <td>011</td><td>p1</td>
    </tr>
    <tr>
        <td>100</td><td>x0</td>
        <td>101</td><td>x1</td>
        <td>110</td><td>x2</td>
        <td>111</td><td>x3</td>
    </tr>
</table>
<p>You may notice the lack of the register <code>null</code> which does nothing when you write to it and returns 0 when you read from it. I did not include it because it can simply be replaced with the literal 0 except when writing to it with <code>MOV</code>, because of this I added the flag <code>E</code> to the instruction <code>SLX</code> which when 1 will eat a value from the bus and do nothing with it.</p>
<center><h3>Condition codes</h3></center>
<table>
    <tr>
        <td>00</td><td>always execute</td>
    </tr>
    <tr>
        <td>01</td><td>only execute on - flag</td>
    </tr>
    <tr>
        <td>10</td><td>only execute on + flag</td>
    </tr>
    <tr>
        <td>11</td><td>only execute once</td>
    </tr>
</table>
<p>Internally there are two execution flags, <code>+</code> and <code>-</code> which are set by the test instructions <code>TEQ</code>, <code>TGT</code>, <code>TLT</code>, <code>TCP</code>. Every instruction but <code>TCP</code> sets the flags in a differential as in only either + or - can be true at the same time but <code>TCP</code> will disable both flags when both operands are equal.</p>
<p>Because I want to reduce instruction size I&apos;ve made it so only the first operand of test instructions can be immediate values, because of this things have to be shifted around when assembling:</p>
<pre><code>TGT acc 69 -&gt; TLT 69 acc
TEQ 69 69 -&gt; TST 1 0
TCP acc 42 -&gt; TPC 42 acc
</code></pre>
<p>You may notice I&apos;ve added 2 extra test instructions: <code>TST</code> and <code>TPC</code>.<br>
<code>TST</code> takes 2 operands, <code>+</code> and <code>-</code> and sets the coresponding flags directly, this is always emitted when both operands of a test instruction are immediates.<br>
<code>TPC</code> is the same as <code>TCP</code> but with the operands reversed, this happens when the second operand is an immediate.</p>
<style>
    .instruction td {
        text-align: center;
    }
    .numrow td {border: none !important;}
    .instruction td:last-child {
        border: none !important;
        font-weight: bold;
        text-align: left;
    }
    .instruction .dead {
        background-color: rgba(0,0,0,0.15);
    }
</style>
<center><h3>Register/Immediate values</h3></center>
<table class="instruction">
<tr class="numrow">
    <td>10</td><td>9</td><td>8</td><td>7</td><td>6</td><td>5</td><td>4</td><td>3</td><td>2</td><td>1</td><td>0</td><td></td>
</tr>
<tr>
    <td>1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td>
    <td colspan="3">reg</td>
    <td>Regsiter</td>
</tr>
<tr>
    <td colspan="11">immediate</td>
    <td>Immediate</td>
</tr>
</table>
<p>Immediate values can range from -999 to 999 and are encoded with <a href="https://en.wikipedia.org/wiki/Two&apos;s_complement?ref=blog.tst.sh">two&apos;s complement</a> and because it doesn&apos;t completely fill the 11 bits (0 - 2048) we can store a register number without adding an extra bit to signal whether or not it&apos;s a register. To easily tell the difference between the two you simply check if the first 8 bits are 10000000 which means it&apos;s &lt;= -1017.</p>
<center><h3>Register/Select values</h3></center>
<table class="instruction">
<tr class="numrow">
    <td>3</td><td>2</td><td>1</td><td>0</td><td></td>
</tr>
<tr>
    <td>1</td>
    <td colspan="3">reg</td>
    <td>Regsiter</td>
</tr>
<tr>
    <td>0</td><td class="dead"></td>
    <td colspan="2">imm</td>
    <td>Selection</td>
</tr>
</table>
<center><h3>Register/Digit values</h3></center>
<table class="instruction">
<tr class="numrow">
    <td>4</td><td>3</td><td>2</td><td>1</td><td>0</td><td></td>
</tr>
<tr>
    <td>1</td><td class="dead"></td>
    <td colspan="3">reg</td>
    <td>Regsiter</td>
</tr>
<tr>
    <td>0</td>
    <td colspan="4">immediate</td>
    <td>Digit</td>
</tr>
</table>
<p>Register/Digit values work similarly to Register/Immediate values but store single digits from 0 to 9 but requires a flag to differentiate registers from immediates. If the digit is out of bounds it should be encoded as 0b1111 which will be interpreted as a no-op.</p>
<center><h3>Instructions</h3></center>
<table class="instruction">
<tr class="numrow">
    <td>18</td><td>17</td><td>16</td><td>15</td><td>14</td><td>13</td><td>12</td><td>11</td><td>10</td><td>9</td><td>8</td><td>7</td><td>6</td><td>5</td><td>4</td><td>3</td><td>2</td><td>1</td><td>0</td><td></td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>0</td><td>0</td><td>0</td>
    <td colspan="11">R/I arg</td>
    <td colspan="3">reg</td>
    <td>MOV</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>0</td><td>1</td><td>0</td>
    <td>0</td><td>0</td>
    <td colspan="8" class="dead"></td>
    <td colspan="4">line</td>
    <td>JMP</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>0</td><td>1</td><td>0</td>
    <td>0</td><td>1</td>
    <td colspan="1" class="dead"></td>
    <td colspan="11">R/I sleep amount</td>
    <td>SLP</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>0</td><td>1</td><td>0</td>
    <td>1</td><td>0</td><td>E</td>
    <td colspan="9" class="dead"></td>
    <td colspan="2">xpin</td>
    <td>SLX</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>0</td><td>1</td><td>0</td>
    <td>1</td><td>1</td>
    <td colspan="1" class="dead"></td>
    <td colspan="11">R/I value</td>
    <td>ADD</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>0</td><td>1</td><td>1</td>
    <td>0</td><td>0</td>
    <td colspan="9" class="dead"></td>
    <td colspan="3">reg</td>
    <td>SUB</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>0</td><td>1</td><td>1</td>
    <td>0</td><td>1</td>
    <td colspan="1" class="dead"></td>
    <td colspan="11">R/I value</td>
    <td>MUL</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>0</td><td>1</td><td>1</td>
    <td>1</td><td>1</td><td>0</td>
    <td colspan="11" class="dead"></td>
    <td>NOT</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>0</td><td>1</td><td>1</td>
    <td>1</td><td>0</td><td>0</td>
    <td colspan="7" class="dead"></td>
    <td colspan="4">R/S selection</td>
    <td>DGT</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>0</td><td>1</td><td>1</td>
    <td>1</td><td>0</td><td>1</td>
    <td colspan="2" class="dead"></td>
    <td colspan="5">R/D value</td>
    <td colspan="4">R/S selection</td>
    <td>DST</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>1</td><td>0</td><td>0</td>
    <td colspan="11">R/I arg</td>
    <td colspan="3">reg</td>
    <td>TEQ</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>1</td><td>0</td><td>1</td>
    <td colspan="11">R/I arg</td>
    <td colspan="3">reg</td>
    <td>TGT</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>1</td><td>1</td><td>0</td>
    <td colspan="11">R/I arg</td>
    <td colspan="3">reg</td>
    <td>TLT</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>1</td><td>1</td><td>1</td>
    <td colspan="11">R/I arg</td>
    <td colspan="3">reg</td>
    <td>TCP</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>0</td><td>0</td><td>1</td>
    <td colspan="11">R/I arg</td>
    <td colspan="3">reg</td>
    <td>TPC</td>
</tr>
<tr>
    <td colspan="2">cond</td>
    <td>0</td><td>1</td><td>1</td>
    <td>1</td><td>1</td><td>1</td>
    <td colspan="9" class="dead"></td>
    <td>+</td><td>-</td>
    <td>TST</td>
</tr>
</table>
<p>Using this layout I made an assembler and disassembler in Dart:</p>
<p><a href="https://dartpad.dartlang.org/1398b0d59ce1f7292c5d5d1064b591b5?ref=blog.tst.sh">https://dartpad.dartlang.org/1398b0d59ce1f7292c5d5d1064b591b5</a></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[My own file uploading service]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>The motivation behind this project was that I needed a very simple way for me and my friends to securely put files on my server for the various projects we use it for and the insane gigabit download speeds. Previously we just used rsync, a Linux command line utility that</p>]]></description><link>https://blog.tst.sh/simple-file-uploader/</link><guid isPermaLink="false">5fa9b817c9aac25a0711724e</guid><category><![CDATA[Web Dev]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Back End]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Sun, 13 May 2018 08:10:03 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2018/05/2018-05-13-02_53_18-Window-1.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.tst.sh/content/images/2018/05/2018-05-13-02_53_18-Window-1.png" alt="My own file uploading service"><p>The motivation behind this project was that I needed a very simple way for me and my friends to securely put files on my server for the various projects we use it for and the insane gigabit download speeds. Previously we just used rsync, a Linux command line utility that uses ssh to transfer files to and from another machine. This has many problems, mainly the fact that you need ssh and rsync installed which is a pain to do on Windows and doing it from a mobile device is out of the question. Secondly you need to add your ssh public key to the target server, this has some security issues because they have an actual linux user to run arbitrary code and after the infamous meltdown exploit i&apos;m not willing to take that risk.</p>
<p>Of course we could just go back to using Google Drive but the free storage space is limited. We could use one of the many basic PHP uploaders but no, let&apos;s do it my way.</p>
<p>Over the span of 2 days I threw together some server-side Dart and some front-end html/css based off of my home website to make a unique way of uploading files:</p>
<p><img src="https://blog.tst.sh/content/images/2018/05/2018-05-13-03_31_38-Window.png" alt="My own file uploading service" loading="lazy"></p>
<p>Basically how it works is, I generate a token which grants a certain amount of storage space and when you upload a file it uses up that token&apos;s storage space.<br>
You can add multiple tokens and choose which ones to use first, the file list is based off what files use it and when you delete a file it will give you back the storage space the file took.</p>
<p><img src="https://blog.tst.sh/content/images/2018/05/2018-05-13-03_39_28-Window.png" alt="My own file uploading service" loading="lazy"></p>
<p>In the settings menu you can delete and re-order tokens, it will fill your storage space from the top down and supports sharing files between multiple tokens so that if you had two 100MB tokens you could upload a 200MB file.</p>
<p><img src="https://blog.tst.sh/content/images/2018/05/2018-05-13-03_47_10-Window.png" alt="My own file uploading service" loading="lazy"></p>
<p>Drag-dropping files works as a bonus!</p>
<p>Setting it up is really simple, the backend is written in Dart (as usual) and can either be used self-contained as it&apos;s own webserver or through another webserver that is better at serving static files and supports like what I did with NGINX.</p>
<p>NGINX config:</p>
<pre><code>server {
    listen 80;
    server_name u.pxtst.com;
    location / {
        rewrite ^(.*)$ https://u.pxtst.com$1 permanent;
    }
}


server {
        listen 443;
        listen [::]:443;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_stapling on;
        ssl_stapling_verify on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        ssl on;
        server_name u.pxtst.com;
        ssl_certificate /etc/letsencrypt/live/u.pxtst.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/u.pxtst.com/privkey.pem;

        root /home/upload/;

        index index.html index.htm index.nginx-debian.html;

        location /api {
                proxy_pass http://localhost:17132;
                proxy_set_header    Host            $host;
                proxy_set_header    X-Real-IP       $remote_addr;
                proxy_set_header    X-Forwarded-for $remote_addr;
                port_in_redirect off;
                proxy_redirect   http://IP:17132/  /;
                proxy_connect_timeout 300;
                client_max_body_size 1G;
        }
        
        location /upload {
                alias /home/upload/www;
                index index.html;
                try_files $uri $uri/ =404;
        }
        
        location / {
                gzip            on;
                gzip_min_length 1000;
                gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;
                expires 7d;
                alias /home/upload/uploads/;
                try_files $uri =403;
        }
}
</code></pre>
<p>Sauce: <a href="https://lab.pxtst.com/PixelToast/file-uploader?ref=blog.tst.sh">https://lab.pxtst.com/PixelToast/file-uploader</a></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[A small ARM assembler]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I made a small ARM assembler in Dart!</p>
<p>It just takes a string, like <code>mov r0, #69</code> and emits the equivalent machine code: <code>e3a00045</code>. Initial tests with dissasemblers show it works pretty well though this isn&apos;t a full assembler, you can&apos;t do linking, macros, labels or</p>]]></description><link>https://blog.tst.sh/a-small-arm-assembler/</link><guid isPermaLink="false">5fa9b817c9aac25a0711724d</guid><category><![CDATA[Low Level]]></category><category><![CDATA[Dart]]></category><category><![CDATA[ARM]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Thu, 10 May 2018 23:09:40 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2018/05/2018-05-10-21_12_43-ARM7-TDMI-manual-pt2.pdf-1.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.tst.sh/content/images/2018/05/2018-05-10-21_12_43-ARM7-TDMI-manual-pt2.pdf-1.png" alt="A small ARM assembler"><p>I made a small ARM assembler in Dart!</p>
<p>It just takes a string, like <code>mov r0, #69</code> and emits the equivalent machine code: <code>e3a00045</code>. Initial tests with dissasemblers show it works pretty well though this isn&apos;t a full assembler, you can&apos;t do linking, macros, labels or even psudo-ops like push. It also has very little safeguards for incorrect flags on some instructions for example a pre-indexed <code>ldrt</code>.</p>
<p>Example output:</p>
<pre><code>e3a00045 mov r0, #69
e12fff1e bx lr
09bc3ffc ldmedeq ip!, {a3-sp}
87e5d1ce strbhi r13, [v2, lr, asr #3]!
96992a4d ldrls a3, [sb], sp, asr #0x14
</code></pre>
<p>Live demo: <a href="https://dartpad.dartlang.org/9a4bb914d3d0d640061564517cf2e1ac?ref=blog.tst.sh">https://dartpad.dartlang.org/9a4bb914d3d0d640061564517cf2e1ac</a></p>
<p>The following instructions are fully supported:</p>
<pre><code>Data processing:
    AND{S}, EOR{S}, SUB{S}, RSB{S}
    ADD{S}, ADC{S}, SBC{S}, SRC{S}
    TST{S}, TEQ{S}, CMP{S}, CMN{S}
    ORR{S}, MOV{S}, BIC{S}, MVN{S}
Multiply:
    MUL{S}, MLA{S}
    UMULL{S}, UMLAL{S}, SMULL{S}, SMLAL{S}
Branching:
    BX, BL, B
Load/Store:
    STR{B}{T}, LDR{B}{T}
    LDM{FD|ED|FA|EA|IA|IB|DA|DB}
    STM{FD|ED|FA|EA|IA|IB|DA|DB}
    SWP{B}
Syscall:
    SWI, SVC
Condition codes:
    EQ, NE, CS, CC
    MI, PL, VS, VC,
    HI, LS, GE, LT
    GT, LE, AL
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[RIP Dedi]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>After 4 years of constant use in the worst conditions imaginable, my first home server codenamed &quot;Dedi&quot; has finally died.</p>
<p>You will be missed.</p>
<img src="https://i.imgur.com/uOh716h.png">
<!--kg-card-end: markdown-->]]></description><link>https://blog.tst.sh/rip-dedi/</link><guid isPermaLink="false">5fa9b817c9aac25a0711724c</guid><category><![CDATA[Servers and Networks]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Thu, 10 May 2018 21:48:06 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2018/05/20161210_044945.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.tst.sh/content/images/2018/05/20161210_044945.jpg" alt="RIP Dedi"><p>After 4 years of constant use in the worst conditions imaginable, my first home server codenamed &quot;Dedi&quot; has finally died.</p>
<p>You will be missed.</p>
<img src="https://i.imgur.com/uOh716h.png" alt="RIP Dedi">
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[What 28 hours of app development looks like]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Over the past 2 weeks I have been recording myself making <a href="https://flutter.io/?ref=blog.tst.sh">Flutter</a> apps, and here they are!</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/m49bP5alwPU" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
<iframe width="560" height="315" src="https://www.youtube.com/embed/6MjsK7lpaR8" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
<!--
<iframe width="560" height="315" src="https://www.youtube.com/embed/Yy-lB6TAR9Q" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe> --><!--kg-card-end: markdown-->]]></description><link>https://blog.tst.sh/what-28-hours-of-app-development-looks-like/</link><guid isPermaLink="false">5fa9b817c9aac25a07117249</guid><category><![CDATA[Mobile Dev]]></category><dc:creator><![CDATA[ping]]></dc:creator><pubDate>Wed, 21 Feb 2018 21:15:00 GMT</pubDate><media:content url="https://blog.tst.sh/content/images/2018/05/maxresdefault.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.tst.sh/content/images/2018/05/maxresdefault.jpg" alt="What 28 hours of app development looks like"><p>Over the past 2 weeks I have been recording myself making <a href="https://flutter.io/?ref=blog.tst.sh">Flutter</a> apps, and here they are!</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/m49bP5alwPU" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
<iframe width="560" height="315" src="https://www.youtube.com/embed/6MjsK7lpaR8" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
<!--
<iframe width="560" height="315" src="https://www.youtube.com/embed/Yy-lB6TAR9Q" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe> --><!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>