<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://kevindavid.org/feed.xml" rel="self" type="application/atom+xml" /><link href="https://kevindavid.org/" rel="alternate" type="text/html" /><updated>2026-03-31T18:39:28+00:00</updated><id>https://kevindavid.org/feed.xml</id><title type="html">Kevin David</title><subtitle></subtitle><entry><title type="html">Creating a Proxmox or QEMU ChromeOS Flex VM</title><link href="https://kevindavid.org/code/2024/03/20/chrome-os-flex-proxmox.html" rel="alternate" type="text/html" title="Creating a Proxmox or QEMU ChromeOS Flex VM" /><published>2024-03-20T02:30:00+00:00</published><updated>2024-03-20T02:30:00+00:00</updated><id>https://kevindavid.org/code/2024/03/20/chrome-os-flex-proxmox</id><content type="html" xml:base="https://kevindavid.org/code/2024/03/20/chrome-os-flex-proxmox.html"><![CDATA[<p>I recently wanted to experiment with <a href="https://chromeos.google/products/chromeos-flex/">ChromeOS Flex</a> but didn’t want to install it on a physical machine. I also was doing this without a USB key, which complicated things slightly.</p>

<p>After some trial and error, there are two non-obvious steps to get the installer running:</p>
<ul>
  <li>Properly mounting the <code class="language-plaintext highlighter-rouge">.bin</code> live USB image in a way that’s accessible to the VM</li>
  <li>Setting graphics drivers to something ChromeOS Flex supports, which enables the VM to successfully boot the live USB image</li>
</ul>

<h2 id="prepare">Prepare</h2>

<p>You’ll need the ChromeOS Flex USB image, which you can get from <a href="https://support.google.com/chromeosflex/answer/11541904?hl=en&amp;ref_topic=11551271&amp;sjid=13345019591806376618-NA">here</a> if you don’t want to enter your contact info on the main page.</p>

<p>Download the ZIP and extract it to your host device somewhere.</p>

<h3 id="while-you-wait">While you wait…</h3>

<p>To use the <code class="language-plaintext highlighter-rouge">VirGL GPU</code> driver, which we’ll need later:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt-get <span class="nb">install </span>libgl1 libegl1
</code></pre></div></div>

<h2 id="create-the-vm">Create the VM</h2>

<p>I used mostly default settings, with a few exceptions.</p>

<ul>
  <li>Linux OS, with the <code class="language-plaintext highlighter-rouge">Do not use any media</code> option initially selected</li>
  <li><code class="language-plaintext highlighter-rouge">q35</code> machine type</li>
  <li><code class="language-plaintext highlighter-rouge">host</code> CPU type</li>
  <li><code class="language-plaintext highlighter-rouge">OVMF (UEFI)</code> BIOS type</li>
</ul>

<p>If you’re using <code class="language-plaintext highlighter-rouge">OVMF (UEFI)</code> make sure you uncheck the <code class="language-plaintext highlighter-rouge">Pre-Enroll keys</code> option on the EFI disk, otherwise it won’t boot - I got an “Access Denied” error in the virtual BIOS.</p>

<p>After creation, go change the <code class="language-plaintext highlighter-rouge">Graphic card</code> setting under <code class="language-plaintext highlighter-rouge">Display</code> (inside <code class="language-plaintext highlighter-rouge">Hardware</code>) to  <code class="language-plaintext highlighter-rouge">VirGL GPU</code>.</p>

<p><code class="language-plaintext highlighter-rouge">VirtIO-GPU</code> may also work, but in my experience it was a bit laggier - <a href="https://forum.proxmox.com/threads/difference-between-virtio-gpu-and-virgl-gpu.113619/">more info</a> on differences, or search yourself.</p>

<p>Don’t bother starting the VM yet.</p>

<h2 id="map-in-the-live-usb-binary-image-as-a-disk">Map in the live USB binary image as a disk</h2>

<p>Set up a loop device with the extracted binary image from the earlier-downloaded from the ZIP file.</p>

<p>For example:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>losetup <span class="nt">--partscan</span> /dev/loop1 chromeos_15393.58.0_reven_recovery_stable-channel_mp-v2.bin
</code></pre></div></div>

<p>Then, map it to your VM like you would any other block device (<a href="https://pve.proxmox.com/wiki/Passthrough_Physical_Disk_to_Virtual_Machine_(VM)?ref=raptorswithhats.com#Update_Configuration">reference</a>):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qm <span class="nb">set</span> &lt;vm_id_here&gt; <span class="nt">-scsi2</span> /dev/loop&lt;num&gt;
</code></pre></div></div>

<p>Mine was <code class="language-plaintext highlighter-rouge">/dev/loop1</code>, yours may be different.</p>

<h2 id="start-it-up">Start it up</h2>

<p>Go back to the Proxmox VM settings (<code class="language-plaintext highlighter-rouge">Options</code>, not <code class="language-plaintext highlighter-rouge">Hardware</code> this time) and change the boot order, putting the <code class="language-plaintext highlighter-rouge">scsi2</code> device first. It should be obvious which one it is, because it’s mapped to your loop device.</p>

<p>Finally, start the VM. If all goes well, you should be greeted with the ChromeOS Flex install screen via the console.</p>]]></content><author><name></name></author><category term="code" /><summary type="html"><![CDATA[I recently wanted to experiment with ChromeOS Flex but didn’t want to install it on a physical machine. I also was doing this without a USB key, which complicated things slightly.]]></summary></entry><entry><title type="html">Shrinking VHDs on Azure, live</title><link href="https://kevindavid.org/code/2023/05/02/shrink-azure-vhd-live.html" rel="alternate" type="text/html" title="Shrinking VHDs on Azure, live" /><published>2023-05-02T16:00:00+00:00</published><updated>2023-05-02T16:00:00+00:00</updated><id>https://kevindavid.org/code/2023/05/02/shrink-azure-vhd-live</id><content type="html" xml:base="https://kevindavid.org/code/2023/05/02/shrink-azure-vhd-live.html"><![CDATA[<p>Azure decided to <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/unmanaged-disks-deprecation">deprecate unmanaged disks</a>, which really cramped my style for a silly reason: my <a href="/code/2020/12/20/azure-vm-from-existing-disks.html">unmanaged disks</a> were <em>just</em> a bit too large to fit into the “round” managed disk size of 256GiB. So that 4GiB was going to double my storage costs. Not cool!</p>

<p>Complicating the matter…</p>

<ul>
  <li>My 257GiB disks were mostly full, so I <em>really</em> didn’t want to download them, copy the data around, then reupload.</li>
  <li>I was using the max number of data disks for the VM size I had (four)</li>
  <li>I was using <code class="language-plaintext highlighter-rouge">btrfs</code> and striping the data across the data disks. So mounting a single disk and copying some data over to a larger disk couldn’t be done - all four had to be mounted at once.</li>
</ul>

<p>Happily, the disks are in the standard VHD format. I decided to take this as a bit of a challenge and modify them using the <a href="https://learn.microsoft.com/en-us/rest/api/storageservices/operations-on-page-blobs">Azure Page Blob API</a>, but still keep them usable enough to migrate.</p>

<h3 id="what-how">What? How?</h3>

<blockquote>
⚠️

This was, and is, probably a bad idea, so follow these steps at your own risk. I didn't care much about the data but I preferred not to lose it.

Standard advice of back up your data, and disclaimer that it's not my fault if you lose it.

There's always the easier and safer option of just mounting more disks on a larger VM temporarily, and copying the data over!
</blockquote>

<p>The general idea I came up with was - as long as the disks had unallocated space at the end, the only thing I’d have to do was  truncate the blobs and rewrite the <a href="https://github.com/qemu/qemu/blob/7c18f2d663521f1b31b821a13358ce38075eaf7d/block/vpc.c#L64">VHD footer</a>.</p>

<p>At first, I thought I was going to have to write my own VHD footer reading/writing code myself - but then I found <a href="https://jrudlin.github.io/2017/10/31/resize-azure-vm-vhd-blob-to-smaller-disk-size-downsize/">someone with a similar idea</a> which inspired my C# version.</p>

<p>With that in mind, I started with a few manual steps:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">btrfs balance</code> and <code class="language-plaintext highlighter-rouge">btrfs filesystem resize &lt;devId&gt;:-1G /home</code> more than once for each “physical” drive and verified there was free space at the end</li>
  <li>Create a dummy disk with the desired size so the trailer is correct
    <ul>
      <li>I did this individually, for <em>each</em> data disk, because there’s a UUID in the footer. Unclear if this was actually necessary.</li>
    </ul>
  </li>
  <li>Set the dummy disk to export mode to get a SAS URI</li>
  <li>Get the exact size of the dummy disk and save off its footer</li>
  <li>One dash of caution: take a snapshot of each pre-modification VHD so I could roll back if something went wrong.</li>
</ul>

<p>Then, some code to:</p>
<ul>
  <li>Resize each target disk to the <em>exact</em> same size as the dummy disk</li>
  <li>Copy over the footer from the dummy disk to the target disk so Hyper-V (Azure) is happy</li>
</ul>

<p>And then finally finish the migration to managed disks.</p>

<h3 id="so-did-it-work">So did it work?</h3>

<p>Yep! Everything went fine. The only issue was a post-migration <code class="language-plaintext highlighter-rouge">fsck</code> which showed some errors like</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>btrfs: csum mismatch on free space cache
failed to load free space cache for block group
</code></pre></div></div>

<p>These were easy to fix via <code class="language-plaintext highlighter-rouge">btrfs check --progress --clear-space-cache v1</code></p>

<h3 id="give-me-the-code">Give me the code</h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.Text</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Azure.Storage</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Azure.Storage.Blobs.Specialized</span><span class="p">;</span>

<span class="kt">var</span> <span class="n">accountName</span> <span class="p">=</span> <span class="s">"myStorageAccount"</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">containerName</span> <span class="p">=</span> <span class="s">"myContainer"</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">blobName</span> <span class="p">=</span> <span class="s">"myBlobName.vhd"</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">creds</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">StorageSharedKeyCredential</span><span class="p">(</span><span class="n">accountName</span><span class="p">,</span> <span class="s">"myStorageAccountKey"</span><span class="p">);</span>

<span class="kt">var</span> <span class="n">dummyStorageName</span> <span class="p">=</span> <span class="s">"dummy4-footer.bin"</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">existingBackupFooterName</span> <span class="p">=</span> <span class="s">"existing4-footer.bin"</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">dummySasUri</span> <span class="p">=</span> <span class="s">"&lt;dummy_export_URL&gt;"</span><span class="p">;</span>

<span class="kt">var</span> <span class="n">tempSourceBlob</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">PageBlobClient</span><span class="p">(</span><span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="n">dummySasUri</span><span class="p">));</span>
<span class="k">await</span> <span class="nf">ReadFooterAsync</span><span class="p">(</span><span class="n">tempSourceBlob</span><span class="p">,</span> <span class="n">dummyStorageName</span><span class="p">);</span>
<span class="kt">var</span> <span class="n">targetPageBlob</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">PageBlobClient</span><span class="p">(</span><span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">$"https://</span><span class="p">{</span><span class="n">accountName</span><span class="p">}</span><span class="s">.blob.core.windows.net/</span><span class="p">{</span><span class="n">containerName</span><span class="p">}</span><span class="s">/</span><span class="p">{</span><span class="n">blobName</span><span class="p">}</span><span class="s">"</span><span class="p">),</span> <span class="n">creds</span><span class="p">);</span>
<span class="k">await</span> <span class="nf">ReadFooterAsync</span><span class="p">(</span><span class="n">targetPageBlob</span><span class="p">,</span> <span class="n">existingBackupFooterName</span><span class="p">);</span>
<span class="k">await</span> <span class="nf">ResizeAndWriteFooterAsync</span><span class="p">(</span><span class="n">targetPageBlob</span><span class="p">,</span> <span class="n">dummyStorageName</span><span class="p">);</span>

<span class="k">static</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">ReadFooterAsync</span><span class="p">(</span><span class="n">PageBlobClient</span> <span class="n">sourceBlob</span><span class="p">,</span> <span class="kt">string</span> <span class="n">targetBinaryFooterFileName</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">footerSize</span> <span class="p">=</span> <span class="m">512</span><span class="p">;</span>
    <span class="kt">var</span> <span class="n">length</span> <span class="p">=</span> <span class="p">(</span><span class="k">await</span> <span class="n">sourceBlob</span><span class="p">.</span><span class="nf">GetPropertiesAsync</span><span class="p">()).</span><span class="n">Value</span><span class="p">.</span><span class="n">ContentLength</span><span class="p">;</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Dummy length: </span><span class="p">{</span><span class="n">length</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
    <span class="k">using</span> <span class="nn">var</span> <span class="n">blobStream</span> <span class="p">=</span> <span class="n">sourceBlob</span><span class="p">.</span><span class="nf">OpenRead</span><span class="p">(</span><span class="n">length</span> <span class="p">-</span> <span class="n">footerSize</span><span class="p">,</span> <span class="n">footerSize</span><span class="p">);</span>

    <span class="kt">var</span> <span class="n">buf</span> <span class="p">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="p">[</span><span class="n">footerSize</span><span class="p">];</span>
    <span class="kt">int</span> <span class="n">read</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">read</span> <span class="p">&lt;</span> <span class="n">footerSize</span> <span class="p">&amp;&amp;</span> <span class="n">read</span> <span class="p">&gt;=</span> <span class="m">0</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">read</span> <span class="p">+=</span> <span class="n">blobStream</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">read</span><span class="p">,</span> <span class="n">footerSize</span> <span class="p">-</span> <span class="n">read</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Read </span><span class="p">{</span><span class="n">read</span><span class="p">}</span><span class="s"> bytes"</span><span class="p">);</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Footer: </span><span class="p">{</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetString</span><span class="p">(</span><span class="n">buf</span><span class="p">)}</span><span class="s">"</span><span class="p">);</span>
    <span class="k">using</span> <span class="nn">var</span> <span class="n">fs</span> <span class="p">=</span> <span class="n">File</span><span class="p">.</span><span class="nf">OpenWrite</span><span class="p">(</span><span class="n">targetBinaryFooterFileName</span><span class="p">);</span>
    <span class="k">await</span> <span class="n">fs</span><span class="p">.</span><span class="nf">WriteAsync</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">ResizeAndWriteFooterAsync</span><span class="p">(</span><span class="n">PageBlobClient</span> <span class="n">targetBlob</span><span class="p">,</span> <span class="kt">string</span> <span class="n">sourceFooterBinaryFileName</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">footerSize</span> <span class="p">=</span> <span class="m">512</span><span class="p">;</span>
    <span class="kt">var</span> <span class="n">finalSize</span> <span class="p">=</span> <span class="m">273804165632</span><span class="p">;</span> <span class="c1">// 255GB</span>

    <span class="kt">var</span> <span class="n">length</span> <span class="p">=</span> <span class="p">(</span><span class="k">await</span> <span class="n">targetBlob</span><span class="p">.</span><span class="nf">GetPropertiesAsync</span><span class="p">()).</span><span class="n">Value</span><span class="p">.</span><span class="n">ContentLength</span><span class="p">;</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Before length: </span><span class="p">{</span><span class="n">length</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
    <span class="k">await</span> <span class="n">targetBlob</span><span class="p">.</span><span class="nf">ResizeAsync</span><span class="p">(</span><span class="n">finalSize</span><span class="p">);</span>

    <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">blobStream</span> <span class="p">=</span> <span class="k">await</span> <span class="n">targetBlob</span><span class="p">.</span><span class="nf">OpenWriteAsync</span><span class="p">(</span><span class="n">overwrite</span><span class="p">:</span> <span class="k">false</span><span class="p">,</span> <span class="n">finalSize</span> <span class="p">-</span> <span class="n">footerSize</span><span class="p">))</span>
    <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">fs</span> <span class="p">=</span> <span class="n">File</span><span class="p">.</span><span class="nf">OpenRead</span><span class="p">(</span><span class="n">sourceFooterBinaryFileName</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="k">await</span> <span class="n">fs</span><span class="p">.</span><span class="nf">CopyToAsync</span><span class="p">(</span><span class="n">blobStream</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">length</span> <span class="p">=</span> <span class="p">(</span><span class="k">await</span> <span class="n">targetBlob</span><span class="p">.</span><span class="nf">GetPropertiesAsync</span><span class="p">()).</span><span class="n">Value</span><span class="p">.</span><span class="n">ContentLength</span><span class="p">;</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"After length: </span><span class="p">{</span><span class="n">length</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="code" /><summary type="html"><![CDATA[Azure decided to deprecate unmanaged disks, which really cramped my style for a silly reason: my unmanaged disks were just a bit too large to fit into the “round” managed disk size of 256GiB. So that 4GiB was going to double my storage costs. Not cool!]]></summary></entry><entry><title type="html">MITMing your own remote Android emulator traffic</title><link href="https://kevindavid.org/code/2022/11/27/mitm-remote-android.html" rel="alternate" type="text/html" title="MITMing your own remote Android emulator traffic" /><published>2022-11-27T17:30:59+00:00</published><updated>2022-11-27T17:30:59+00:00</updated><id>https://kevindavid.org/code/2022/11/27/mitm-remote-android</id><content type="html" xml:base="https://kevindavid.org/code/2022/11/27/mitm-remote-android.html"><![CDATA[<p>Setting up an Android emulator on a remote machine (to avoid destroying my laptop battery) in a state to capture traffic from an app was a bit annoying, and I wanted to store the shortened set instructions somewhere so I can reference it later.</p>

<p>If you’re not me, replace <code class="language-plaintext highlighter-rouge">192.168.1.223</code> with whatever your host machine is on your LAN.</p>

<h2 id="setup">Setup</h2>

<h3 id="host">Host</h3>

<p>Just in case the machine I really don’t care to backup bites the dust…</p>

<ul>
  <li>Assuming a Debian-like system, <code class="language-plaintext highlighter-rouge">apt-get install default-jdk</code>
    <ul>
      <li>I initially tried to do this in a Linux Container (LXC) - but nested virtualization via <code class="language-plaintext highlighter-rouge">/dev/kvm</code> doesn’t pass through. Oh well.</li>
    </ul>
  </li>
  <li>Create or reuse a user. In my case, I did:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">useradd -m android</code></li>
      <li><code class="language-plaintext highlighter-rouge">usermod -a -G kvm android</code> (otherwise the emulator + nested virtualization won’t work)</li>
      <li><code class="language-plaintext highlighter-rouge">passwd android</code> to something I could remember (needed later)</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">su android</code>, install Android Platform tools, emulator, etc. One of the links below should help.
    <ul>
      <li>If it’s been a while, <code class="language-plaintext highlighter-rouge">sdkmanager --update</code> to pull the latest SDK</li>
    </ul>
  </li>
  <li>Add android tools to path. e.g. create <code class="language-plaintext highlighter-rouge">.localrc</code> and set contents to <code class="language-plaintext highlighter-rouge">PATH=$HOME/cmdline-tools/latest/bin:$HOME/platform-tools:$HOME/emulator:$PATH</code></li>
  <li>Get the image: <code class="language-plaintext highlighter-rouge">sdkmanager --install "system-images;android-35;google_apis;x86_64"</code></li>
  <li>Create AVD: <code class="language-plaintext highlighter-rouge">avdmanager create avd --name pixel-android-api-35 --package "system-images;android-35;google_apis;x86_64" --tag "google_apis" --abi "x86_64" --device "pixel_6"</code></li>
  <li>Install the mitmproxy cert into the emulator after making the system partiton writable. I won’t copy <a href="https://gist.github.com/approovm/e550374428065ff1ecafca6a0488d384#start-the-emulator-with-a-writable-file-system">those steps, follow them here.</a></li>
</ul>

<h3 id="client">Client</h3>

<p><code class="language-plaintext highlighter-rouge">brew install scrcpy</code></p>

<h2 id="execute">Execute</h2>

<h3 id="host-1">Host</h3>

<ul>
  <li>Start <code class="language-plaintext highlighter-rouge">screen</code> or similar</li>
  <li>In one terminal of your main user with <code class="language-plaintext highlighter-rouge">sudo</code>: <code class="language-plaintext highlighter-rouge">sudo docker run --rm -it -v ~/.mitmproxy:/home/kevin/.mitmproxy -p 0.0.0.0:8080:8080 -p 0.0.0.0:8081:8081 mitmproxy/mitmproxy:latest mitmweb --showhost --web-host 0.0.0.0</code></li>
  <li>(<code class="language-plaintext highlighter-rouge">ADB_MDNS=0</code> can be removed if https://issuetracker.google.com/issues/382999951 is fixed)</li>
  <li>In another terminal: <code class="language-plaintext highlighter-rouge">ADB_MDNS=0 emulator -avd pixel-android-api-35 -writable-system -no-window -http-proxy http://192.168.1.223:8080</code></li>
  <li>In another terminal:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">export ADB_MDNS=0</code></li>
      <li><code class="language-plaintext highlighter-rouge">adb forward tcp:1234 tcp:5555</code></li>
      <li><code class="language-plaintext highlighter-rouge">adb tcpip 5555</code></li>
      <li><code class="language-plaintext highlighter-rouge">ssh -Nfg -L 1234:localhost:5555 localhost</code></li>
    </ul>
  </li>
</ul>

<h3 id="remote">Remote</h3>

<ul>
  <li><code class="language-plaintext highlighter-rouge">adb connect 192.168.1.223:1234</code></li>
  <li><code class="language-plaintext highlighter-rouge">scrcpy -m1024</code></li>
  <li>Open <code class="language-plaintext highlighter-rouge">http://192.168.1.223:8081</code></li>
  <li>Run your scenario and hopefully it’s captured. If not, you might have a problem with cert pinning or an app bypassing the proxy.</li>
</ul>

<p>Unclear if this is necessary with the <code class="language-plaintext highlighter-rouge">-http-proxy</code> argument with the emulator (I discovered that later), but found it useful enough to document. Run on either host or remote.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adb shell settings put global http_proxy 192.168.1.223:8080
adb shell settings put global global_http_proxy_host 192.168.1.223
adb shell settings put global global_http_proxy_port 8080
</code></pre></div></div>

<h2 id="future">Future</h2>

<p>At some point, I think I’m going to need a packet capture for my next step in the <a href="https://github.com/thirtythreeforty/neolink/blob/master/dissector/baichuan.lua">reverse engineering journey</a> I started down…</p>

<h2 id="resources">Resources</h2>

<p>This terse set of instructions was compiled from:</p>
<ul>
  <li><a href="https://gist.github.com/mrk-han/66ac1a724456cadf1c93f4218c6060ae">https://gist.github.com/mrk-han/66ac1a724456cadf1c93f4218c6060ae</a></li>
  <li><a href="https://gist.github.com/approovm/e550374428065ff1ecafca6a0488d384">https://gist.github.com/approovm/e550374428065ff1ecafca6a0488d384</a></li>
  <li><a href="https://dev.to/andreisfedotov/how-to-install-android-emulator-without-installing-android-studio-3lce">https://dev.to/andreisfedotov/how-to-install-android-emulator-without-installing-android-studio-3lce</a></li>
  <li><a href="https://www.reddit.com/r/Crostini/comments/hqyjir/comment/fy1l0ar/?context=3">https://www.reddit.com/r/Crostini/comments/hqyjir/comment/fy1l0ar/?context=3</a></li>
</ul>

<p>I never did get Frida working, it just hung on my emulator device and the client timed out connecting. But for my case, that wasn’t important.</p>

<h2 id="interesting-things-from-2025-01-05">Interesting things from 2025-01-05</h2>

<ul>
  <li>Using this Frida “codeshare” was really effective: <code class="language-plaintext highlighter-rouge">frida --codeshare akabe1/frida-multiple-unpinning -U -f &lt;app_name&gt;</code></li>
  <li><a href="https://github.com/REAndroid/APKEditor">https://github.com/REAndroid/APKEditor</a> - merge APKs like <code class="language-plaintext highlighter-rouge">java -jar APKEditor.jar m -i &lt;directory&gt;</code></li>
  <li>Sign a recompiled APK: <code class="language-plaintext highlighter-rouge">java -jar uber-apk-signer-1.3.0.jar -a &lt;merged_apk&gt;.apk</code></li>
  <li><a href="https://apktool.org/">https://apktool.org/</a></li>
  <li><a href="https://medium.com/@vaishalinagori112/9-different-ways-to-bypass-ssl-pinning-in-android-2d8c7f81b837">https://medium.com/@vaishalinagori112/9-different-ways-to-bypass-ssl-pinning-in-android-2d8c7f81b837</a></li>
  <li><a href="https://approov.io/blog/how-to-bypass-certificate-pinning-with-frida-on-an-android-app">https://approov.io/blog/how-to-bypass-certificate-pinning-with-frida-on-an-android-app</a></li>
  <li>https://gist.github.com/approovm/e550374428065ff1ecafca6a0488d384#start-the-emulator-with-a-writable-file-system</li>
</ul>]]></content><author><name></name></author><category term="code" /><summary type="html"><![CDATA[Setting up an Android emulator on a remote machine (to avoid destroying my laptop battery) in a state to capture traffic from an app was a bit annoying, and I wanted to store the shortened set instructions somewhere so I can reference it later.]]></summary></entry><entry><title type="html">Creating a VM from existing VHDs in Azure</title><link href="https://kevindavid.org/code/2020/12/20/azure-vm-from-existing-disks.html" rel="alternate" type="text/html" title="Creating a VM from existing VHDs in Azure" /><published>2020-12-20T17:30:59+00:00</published><updated>2020-12-20T17:30:59+00:00</updated><id>https://kevindavid.org/code/2020/12/20/azure-vm-from-existing-disks</id><content type="html" xml:base="https://kevindavid.org/code/2020/12/20/azure-vm-from-existing-disks.html"><![CDATA[<p>A few years ago, for some masochistic reason, I decided I wanted to create an Arch Linux VM in Azure. I really don’t remember why, or how, I got started down that path - but switching now would be too high-cost to be worth it.</p>

<p>Sometimes I like to screw around with my Azure resources like the VM itself, networking, sizes, etc. which necessitates deleting the VM. But I don’t want to actually lose the OS - I want my data to say safe.</p>

<p>My handy <code class="language-plaintext highlighter-rouge">.txt</code> file this is based off of told me I originally used <a href="http://sc.scomurr.com/arm-recreating-vm-off-existing-vhds/">this post</a>, so thanks to whoever wrote that. Without further ado:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Connect-AzAccount</span><span class="w">

</span><span class="c"># RG info</span><span class="w">
</span><span class="nv">$rgName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"default"</span><span class="w">
</span><span class="nv">$locName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"East US 2"</span><span class="w">
</span><span class="nv">$vmName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"{YOUR_VM_NAME}"</span><span class="w">
</span><span class="nv">$diskBaseName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"{YOUR_DISK_BASE_NAME_MAYBE_SAME_AS_VM}"</span><span class="w">

</span><span class="c"># Networking</span><span class="w">
</span><span class="nv">$vnet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-AzVirtualNetwork</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"public-vnet"</span><span class="w"> </span><span class="nt">-ResourceGroupName</span><span class="w"> </span><span class="nv">$rgName</span><span class="w">
</span><span class="nv">$nic</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-AzNetworkInterface</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"</span><span class="nv">$vmName</span><span class="s2">-if"</span><span class="w"> </span><span class="nt">-ResourceGroupName</span><span class="w"> </span><span class="nv">$rgName</span><span class="w">

</span><span class="c"># VM</span><span class="w">
</span><span class="nv">$vm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-AzVMConfig</span><span class="w"> </span><span class="nt">-VmName</span><span class="w"> </span><span class="nv">$vmName</span><span class="w"> </span><span class="nt">-VmSize</span><span class="w"> </span><span class="s2">"Standard_B2ms"</span><span class="w">
</span><span class="nv">$vm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Add-AzVMNetworkInterface</span><span class="w"> </span><span class="nt">-VM</span><span class="w"> </span><span class="nv">$vm</span><span class="w"> </span><span class="nt">-Id</span><span class="w"> </span><span class="nv">$nic</span><span class="o">.</span><span class="nf">Id</span><span class="w">

</span><span class="c"># Attach disks</span><span class="w">
</span><span class="nv">$vm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Set-AzVMOSDisk</span><span class="w"> </span><span class="nt">-VM</span><span class="w"> </span><span class="nv">$vm</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"arch_azure"</span><span class="w"> </span><span class="nt">-VhdUri</span><span class="w"> </span><span class="s2">"https://</span><span class="nv">$vmName</span><span class="s2">.blob.core.windows.net/os-vhd/arch_azure.vhd"</span><span class="w"> </span><span class="nt">-CreateOption</span><span class="w"> </span><span class="nx">Attach</span><span class="w"> </span><span class="nt">-Linux</span><span class="w"> </span><span class="nt">-Caching</span><span class="w"> </span><span class="nx">ReadWrite</span><span class="w">
</span><span class="nv">$vm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Add-AzVMDataDisk</span><span class="w"> </span><span class="nt">-VM</span><span class="w"> </span><span class="nv">$vm</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"</span><span class="nv">$diskBaseName</span><span class="s2">-data1"</span><span class="w"> </span><span class="nt">-VhdUri</span><span class="w"> </span><span class="s2">"https://</span><span class="nv">$vmName</span><span class="s2">.blob.core.windows.net/os-vhd/</span><span class="nv">$diskBaseName</span><span class="s2">-data1.vhd"</span><span class="w"> </span><span class="nt">-LUN</span><span class="w"> </span><span class="nx">0</span><span class="w"> </span><span class="nt">-Caching</span><span class="w"> </span><span class="nx">None</span><span class="w"> </span><span class="nt">-CreateOption</span><span class="w"> </span><span class="nx">Attach</span><span class="w">
</span><span class="nv">$vm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Add-AzVMDataDisk</span><span class="w"> </span><span class="nt">-VM</span><span class="w"> </span><span class="nv">$vm</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"</span><span class="nv">$diskBaseName</span><span class="s2">-data2"</span><span class="w"> </span><span class="nt">-VhdUri</span><span class="w"> </span><span class="s2">"https://</span><span class="nv">$vmName</span><span class="s2">.blob.core.windows.net/os-vhd/</span><span class="nv">$diskBaseName</span><span class="s2">-data2.vhd"</span><span class="w"> </span><span class="nt">-LUN</span><span class="w"> </span><span class="nx">1</span><span class="w"> </span><span class="nt">-Caching</span><span class="w"> </span><span class="nx">None</span><span class="w"> </span><span class="nt">-CreateOption</span><span class="w"> </span><span class="nx">Attach</span><span class="w">
</span><span class="nv">$vm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Add-AzVMDataDisk</span><span class="w"> </span><span class="nt">-VM</span><span class="w"> </span><span class="nv">$vm</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"</span><span class="nv">$diskBaseName</span><span class="s2">-data3"</span><span class="w"> </span><span class="nt">-VhdUri</span><span class="w"> </span><span class="s2">"https://</span><span class="nv">$vmName</span><span class="s2">.blob.core.windows.net/os-vhd/</span><span class="nv">$diskBaseName</span><span class="s2">-data3.vhd"</span><span class="w"> </span><span class="nt">-LUN</span><span class="w"> </span><span class="nx">2</span><span class="w"> </span><span class="nt">-Caching</span><span class="w"> </span><span class="nx">None</span><span class="w"> </span><span class="nt">-CreateOption</span><span class="w"> </span><span class="nx">Attach</span><span class="w">
</span><span class="nv">$vm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Add-AzVMDataDisk</span><span class="w"> </span><span class="nt">-VM</span><span class="w"> </span><span class="nv">$vm</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"</span><span class="nv">$diskBaseName</span><span class="s2">-data4"</span><span class="w"> </span><span class="nt">-VhdUri</span><span class="w"> </span><span class="s2">"https://</span><span class="nv">$vmName</span><span class="s2">.blob.core.windows.net/os-vhd/</span><span class="nv">$diskBaseName</span><span class="s2">-data4.vhd"</span><span class="w"> </span><span class="nt">-LUN</span><span class="w"> </span><span class="nx">3</span><span class="w"> </span><span class="nt">-Caching</span><span class="w"> </span><span class="nx">None</span><span class="w"> </span><span class="nt">-CreateOption</span><span class="w"> </span><span class="nx">Attach</span><span class="w">

</span><span class="c"># Create the VM</span><span class="w">
</span><span class="n">New-AzVM</span><span class="w"> </span><span class="nt">-ResourceGroupName</span><span class="w"> </span><span class="nv">$rgName</span><span class="w"> </span><span class="nt">-Location</span><span class="w"> </span><span class="nv">$locName</span><span class="w"> </span><span class="nt">-VM</span><span class="w"> </span><span class="nv">$vm</span><span class="w"> </span><span class="nt">-Verbose</span><span class="w">
</span></code></pre></div></div>

<p>In case it’s not obvious, this is using Powershell with the <a href="https://docs.microsoft.com/en-us/powershell/azure/new-azureps-module-az">Azure Az module</a>. Yes, it could probably use the fancy az CLI that exists too. No, I don’t care - it works for me 🙂</p>

<p>Preempting a few questions:</p>
<ul>
  <li>This is using unmanaged disks because they are WAY cheaper per GB, and I can stripe 4 of them in RAID 0 using <code class="language-plaintext highlighter-rouge">btrfs</code>. While it wouldn’t be ideal, I don’t <em>really really</em> care about losing the data on there, plus I trust Azure to keep my data (relatively) safe.
    <ul>
      <li>Remember, I did this masochistically.</li>
    </ul>
  </li>
  <li>The NIC name, as well as the storage account paths and disk names, are by convention - no real association necessary.</li>
  <li>I don’t remember why exactly I disabled disk caching for the “data disks” - I think some article told me to, but recommended that I keep it enabled for the OS disk. I might have perf tested it too.</li>
  <li>I’m using Azure because I get it for free as a work benefit. Not sure what I’d do otherwise, but dedicated servers are pretty cheap these days versus the public cloud!</li>
</ul>]]></content><author><name></name></author><category term="code" /><summary type="html"><![CDATA[A few years ago, for some masochistic reason, I decided I wanted to create an Arch Linux VM in Azure. I really don’t remember why, or how, I got started down that path - but switching now would be too high-cost to be worth it.]]></summary></entry><entry><title type="html">Why this exists</title><link href="https://kevindavid.org/meta/2020/12/19/why.html" rel="alternate" type="text/html" title="Why this exists" /><published>2020-12-19T12:30:59+00:00</published><updated>2020-12-19T12:30:59+00:00</updated><id>https://kevindavid.org/meta/2020/12/19/why</id><content type="html" xml:base="https://kevindavid.org/meta/2020/12/19/why.html"><![CDATA[<p>Hey! I’m Kevin. If you found this site, you probably know that though 🙂</p>

<p>For a long time, I put off setting up a blog because I felt like I had nothing unique to write about or share. Blogging seemed pretty silly and weird – still does, honestly.</p>

<p>But, about a year ago, I started a “new job” where asynchronous work became much more important. COVID-19 happened and put the transition from my former-office-based-self (still want to go back, BTW – but it’ll never be the same) into overdrive. So, I found myself writing a LOT more as a part of my daily job. Why not do it for fun too? (only half-kidding…)</p>

<p>Over the past year, I realized a few things:</p>
<ul>
  <li>I actually enjoy writing, most of the time. Maybe there’s a <a href="https://medium.com/coaching-notes/youre-not-managing-a-team-of-software-engineers-you-re-managing-a-team-of-writers-b263d3a10cc7">pattern there…</a></li>
  <li>I’m a much better written storyteller than a verbal one. And I’m not a great writer.</li>
  <li>I was setting the bar WAY too high for myself, and was afraid to “put myself out there.” Don’t be me. Try the things you want to try, life is short.</li>
</ul>

<p><a href="https://en.wikipedia.org/wiki/A_journey_of_a_thousand_miles_begins_with_a_single_step">So here we are</a>! I don’t expect to put a lot on here, and I don’t expect it to be high quality<sup>1</sup>. Mostly just a place to put the outcomes of my random quests to learn things, primarily sourced from the internet. During my relatively short time on earth, I think I’ve accumulated a fair bit of knowledge and experience (obviously paling in comparison to what I don’t know), so I might try to share some of that too.</p>

<p>Then again, maybe this will be the only post ever, who knows? One thing I do know - without setting this up it was going to be impossible to try.</p>

<hr />

<p><sup>1</sup>A good friend of mine once told me – the key to measuring success is low expectations 😊</p>]]></content><author><name></name></author><category term="meta" /><summary type="html"><![CDATA[Hey! I’m Kevin. If you found this site, you probably know that though 🙂]]></summary></entry></feed>