<?xml version='1.0' encoding='utf-8'?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jakub Szturomski — Writing</title><id>https://jakub.app/</id><updated>2020-12-16T05:35:07.322Z</updated><link href="https://jakub.app/feed.xml" rel="self" /><link href="https://jakub.app/" /><author><name>Jakub Szturomski</name></author><entry><title>Reverse-engineering Instagram’s device ID</title><id>https://jakub.app/blog/instagram-uuid/</id><published>2020-12-16T05:35:07.322Z</published><updated>2020-12-16T05:35:07.322Z</updated><summary>Following an HTTP header into an Android APK, learning Smali, and finding a small twist on UUID generation.</summary><link href="https://jakub.app/blog/instagram-uuid/" /><content type="html">&lt;p&gt;While experimenting with Instagram’s private API, I ran into a strange problem: requests using a UUID generated by Python’s &lt;code&gt;uuid&lt;/code&gt; library were being rejected. An ID taken from the Android app worked.&lt;/p&gt;
&lt;p&gt;The two values looked almost identical. That was enough to make me curious.&lt;/p&gt;
&lt;h2 id="a-uuid-that-wasnt-quite-a-uuid"&gt;A UUID that wasn’t quite a UUID&lt;/h2&gt;
&lt;p&gt;The app sent a device identifier in the &lt;code&gt;X-IG-Device-ID&lt;/code&gt; header. One value I observed looked like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;9595474d-e663-4183-b1b1-2f42dc794059
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Compare that with a regular UUID generated in Python:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;383320ef-b7d2-4c38-9baa-f4e3bb65c0ec
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At first glance, there is no obvious difference. Both have the familiar groups of hexadecimal characters separated by hyphens. But replacing the app’s identifier with my own resulted in bad requests on the endpoints I was testing.&lt;/p&gt;
&lt;p&gt;That observation did not prove the identifier was the only thing Instagram checked. It did give me a useful question: &lt;strong&gt;how was the Android app generating it?&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="learning-enough-android-to-follow-the-trail"&gt;Learning enough Android to follow the trail&lt;/h2&gt;
&lt;p&gt;I've used Android development tools before, and I know ADB from flashing custom ROMs. For this investigation, I needed to get more familiar with DEX bytecode and Smali.&lt;/p&gt;
&lt;p&gt;Two ideas helped me get started:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DEX&lt;/strong&gt; is the bytecode format used by Android applications. An APK contains one or more &lt;code&gt;.dex&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smali&lt;/strong&gt; is a readable assembly-like representation of that bytecode. When a Java decompiler struggles, the underlying instructions can still tell you what the app does.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unlike the Java Virtual Machine’s stack-based bytecode, Dalvik bytecode uses registers. For example, loading the integer &lt;code&gt;42&lt;/code&gt; into a register can look like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;const/16&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I did not need to understand every instruction immediately. I needed enough to trace where a string came from and how it changed.&lt;/p&gt;
&lt;h2 id="finding-the-header-in-the-apk"&gt;Finding the header in the APK&lt;/h2&gt;
&lt;p&gt;First, I used &lt;a href="https://developer.android.com/tools/adb"&gt;ADB&lt;/a&gt; to locate the installed package and copy its APK to my computer:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;adb&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;pm&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;packages
adb&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;pm&lt;span class="w"&gt; &lt;/span&gt;path&lt;span class="w"&gt; &lt;/span&gt;com.instagram.android
&lt;span class="c1"&gt;# Use the APK path returned by the previous command:&lt;/span&gt;
adb&lt;span class="w"&gt; &lt;/span&gt;pull&lt;span class="w"&gt; &lt;/span&gt;/path/returned/by/pm/base.apk&lt;span class="w"&gt; &lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then I opened the APK in &lt;a href="https://github.com/skylot/jadx"&gt;JADX&lt;/a&gt;, which lets you browse decompiled Java and the underlying Smali.&lt;/p&gt;
&lt;figure&gt;
&lt;img src="https://jakub.app/assets/blog/instagram-uuid/image-20201203161905570.png" alt="The Instagram APK opened in JADX, showing its packages and decompiled source." loading="lazy" width="1024"&gt;
&lt;figcaption&gt;Exploring the Android app in JADX.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Searching for &lt;code&gt;X-IG-Device-ID&lt;/code&gt; led to a call resembling this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;r7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;A01&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-IG-Device-ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AnonymousClass0P0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;A02&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;A05&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;figure&gt;
&lt;img src="https://jakub.app/assets/blog/instagram-uuid/image-20201203162305351.png" alt="JADX search results showing references to the X-IG-Device-ID header." loading="lazy" width="1024"&gt;
&lt;figcaption&gt;The header name was the starting point for tracing the value.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The first method just added a key-value pair to a list:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;A01&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;A05&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;C17920uL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The interesting part was the second argument: the result of &lt;code&gt;A05(context)&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="when-the-decompiler-gives-up"&gt;When the decompiler gives up&lt;/h2&gt;
&lt;p&gt;JADX could not reconstruct that method cleanly as Java. Fortunately, its Smali view was still available.&lt;/p&gt;
&lt;figure&gt;
&lt;img src="https://jakub.app/assets/blog/instagram-uuid/image-20201203162913234.png" alt="JADX displaying the device-ID method that it could not fully decompile into Java." loading="lazy" width="1024"&gt;
&lt;figcaption&gt;A failed Java decompilation does not mean the bytecode is unreadable.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I switched to Smali, copied the method into a file, and started annotating it instruction by instruction. My notes are mostly in Polish, with plenty of question marks. I'm figuring this out as I go.&lt;/p&gt;
&lt;figure&gt;
&lt;img src="https://jakub.app/assets/blog/instagram-uuid/image-20201203163857608.png" alt="Smali instructions for the method responsible for obtaining or generating the device identifier." loading="lazy" width="1024"&gt;
&lt;figcaption&gt;Following the registers through the Smali version of the method.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Before generating anything, the method tried to reuse an identifier. The code referred to &lt;code&gt;/mnt/sdcard/.profig.os&lt;/code&gt; and an &lt;code&gt;INSTALLATION&lt;/code&gt; file in the app’s files directory. It read stored values, checked them, and copied a value between the files in some branches.&lt;/p&gt;
&lt;p&gt;If no acceptable identifier was available, it generated a new one. This was the branch I wanted to understand.&lt;/p&gt;
&lt;h2 id="the-small-modification"&gt;The small modification&lt;/h2&gt;
&lt;p&gt;The generation code began with an ordinary Java UUID:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It split the string on hyphens, then replaced the second segment with a value assembled from two pieces:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A random integer from &lt;code&gt;0&lt;/code&gt; through &lt;code&gt;14&lt;/code&gt;, converted to hexadecimal.&lt;/li&gt;
&lt;li&gt;A time-derived number, also converted to hexadecimal.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first piece came from &lt;code&gt;new Random().nextInt(15)&lt;/code&gt;. The upper bound is exclusive, so this produces the characters &lt;code&gt;0&lt;/code&gt; through &lt;code&gt;e&lt;/code&gt;, not &lt;code&gt;f&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For the second piece, the code took the current time in milliseconds, subtracted a static field named &lt;code&gt;A01&lt;/code&gt;, divided the difference by &lt;code&gt;1000&lt;/code&gt;, and added &lt;code&gt;1635&lt;/code&gt; (&lt;code&gt;0x663&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Here is the important part of the Smali, with the operations called out:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;invoke-static&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;L&lt;/span&gt;java/lang/&lt;span class="nc"&gt;System&lt;/span&gt;;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="kt"&gt;J&lt;/span&gt;
move-result-wide&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v2&lt;/span&gt;

sget-wide&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;L&lt;/span&gt;X/&lt;span class="nc"&gt;0P0&lt;/span&gt;;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;A01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;J&lt;/span&gt;
sub-long/2addr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v0&lt;/span&gt;

const-wide/16&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x3e8&lt;/span&gt;
&lt;span class="c"&gt;# Divide the elapsed milliseconds by 1000.&lt;/span&gt;
div-long/2addr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v0&lt;/span&gt;

const-wide/16&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x663&lt;/span&gt;
&lt;span class="c"&gt;# Add 1635.&lt;/span&gt;
add-long/2addr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v0&lt;/span&gt;

invoke-static&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;v3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;L&lt;/span&gt;java/lang/&lt;span class="nc"&gt;Long&lt;/span&gt;;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toHexString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;J&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="kt"&gt;L&lt;/span&gt;java/lang/&lt;span class="nc"&gt;String&lt;/span&gt;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The method combined the random hexadecimal digit and the time-derived hexadecimal string, then used &lt;code&gt;replaceFirst&lt;/code&gt; to replace the UUID’s second segment.&lt;/p&gt;
&lt;p&gt;For the example I started with, that segment was &lt;strong&gt;&lt;code&gt;e663&lt;/code&gt;&lt;/strong&gt;: a random &lt;code&gt;e&lt;/code&gt; followed by &lt;code&gt;663&lt;/code&gt;, the hexadecimal representation of &lt;code&gt;1635&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="reconstructing-it-in-java"&gt;Reconstructing it in Java&lt;/h2&gt;
&lt;p&gt;I rewrote the operations in Java to make them easier to follow. Here’s the result:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.Random&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.UUID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GenerateDeviceId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// For this experiment, initialize the reference time at class load.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// The app reads its reference time from a separate static field.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;REFERENCE_TIME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;secondSegment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;randomHex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toHexString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;nextInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;elapsedSeconds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;REFERENCE_TIME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;timeHex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toHexString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsedSeconds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1635&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceFirst&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secondSegment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;randomHex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;timeHex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One detail I still need to investigate is how the app initializes its static &lt;code&gt;A01&lt;/code&gt; field. In this experiment, I use &lt;code&gt;System.currentTimeMillis()&lt;/code&gt; at class load. That lets me reproduce the operations, but it doesn’t establish the app’s reference time.&lt;/p&gt;
&lt;p&gt;It also explains why the result looked so familiar. Most of the string still came directly from &lt;code&gt;UUID.randomUUID()&lt;/code&gt;; only one segment had changed.&lt;/p&gt;
&lt;h2 id="what-i-took-away"&gt;What I took away&lt;/h2&gt;
&lt;p&gt;The most useful part of this exercise was learning to keep going when a decompiler stopped being helpful. Searching for a header, tracing its value, and reading a small amount of Smali was enough to uncover behavior that was not obvious from the request itself.&lt;/p&gt;
&lt;p&gt;I started out trying to understand one rejected request and ended up learning about DEX, registers, and Android’s storage of device identifiers. It was a fun investigation, and a good reason to get more comfortable reading Smali.&lt;/p&gt;</content></entry></feed>