# JavascriptCore Math.random predictor

Lately, I decided to take a look at how `Math.random`

work and how we could try to predict the value.

So just like anyone else, I tried to search for already existing `Math.random`

predictors and found:

- v8 Math.random predictor: https://github.com/PwnFunction/v8-randomness-predictor
- Firefox & Chrome Math.random predictor: https://github.com/TACIXAT/XorShift128Plus

Then, I saw that the author `TACIXAT`

said in the README:

Set the browser in main() to Chrome or Firefox. (Safari hasn’t updated yet.)

Thus, it seems like a good excercise for me to try looking into `Math.random`

of Safari.

# Webkit

Safari is browser built into the Apple’s OS, and its engine is Webkit, and the underline JS engine of Webkit is JavascriptCore (JSC).

Thankfully, LiveOverflow has published a video on how to debug Webkit / JSC on video so it’s easy for me to start from there.

# Frustration attempts

Showing the result PoC is cool, but for me it’s cooler if readers can see how much frustration I had within this journey.

Readers: just shut up and show me the PoC !!! Okay here ðŸ˜¢

## 1st attempt: setting breakpoints

First, I tried tracing in the source code on how JSC call `Math.random()`

. By reading source code I found that the function is triggered at:

- https://github.com/WebKit/WebKit/blob/08e4dc175b88c43782fd9d8f1f3f73166a090ff8/Source/JavaScriptCore/runtime/MathObject.cpp#L278-L281
- Then, the
`globalObject->weakRandomNumber()`

would call to https://github.com/WebKit/WebKit/blob/08e4dc175b88c43782fd9d8f1f3f73166a090ff8/Source/JavaScriptCore/runtime/JSGlobalObject.h#L1322 - Which results at
`WeakRandom.h`

file: https://github.com/WebKit/WebKit/blob/08e4dc175b88c43782fd9d8f1f3f73166a090ff8/Source/WTF/wtf/WeakRandom.h#L64-L68

Thus, I hapily setting breakpoint in `gdb`

and wait for magic to happen:

WTF? Even though I’ve setup the breakpoint but no matter how I do, the breakpoint couldn’t be hit. You can see I even change the Webkit source code and try to do some `printf`

in the `double get`

function but I got no luck, there’s no output being printed.

## 2nd attempt: copy-paste JSC `Math.random`

At this point, I tried copy-paste the `WeakRandom.h`

to another file to check if `Math.random()`

behave exactly like how it is implemented (or I’m looking at the wrong code?):

```
#include <stdint.h>
#include <cstdio>
#include <cmath>
#include <cstring>
unsigned m_seed;
uint64_t m_low;
uint64_t m_high;
uint64_t nextState(uint64_t x, uint64_t y)
{
x ^= x << 23;
x ^= x >> 17;
x ^= y ^ (y >> 26);
return x;
}
uint64_t advance()
{
uint64_t x = m_low;
uint64_t y = m_high;
m_low = y;
m_high = nextState(x, y);
return m_high + m_low;
}
double get()
{
uint64_t value = advance() & ((1ULL << 53) - 1);
return value * (1.0 / (1ULL << 53));
}
void setSeed(unsigned seed)
{
m_seed = seed;
// A zero seed would cause an infinite series of zeroes.
if (!seed)
seed = 1;
m_low = seed;
m_high = seed;
advance();
}
int main(int argc, char **argv)
{
setSeed(1337);
for (int i = 0; i < 10; ++i)
{
double k = get();
printf("%.12lf\n", k);
}
return 0;
}
```

The above program code first call `setSeed(1337)`

and then output 10 first random values. This is its output:

```
$ c++ jsc.cpp -o main
$ ./main
0.000001245188
0.445313745787
0.890626246274
0.895725289653
0.451399313092
0.137931930495
0.837726360333
0.470502978679
0.532515356442
0.079062741176
```

Then, I get back to the real JSC and try to `setSeed(1337)`

before calling `Math.random()`

. Luckily in JSC there’s a function to set the seed which called `setRandomSeed(<seed>)`

defined at: https://github.com/WebKit/WebKit/blob/08e4dc175b88c43782fd9d8f1f3f73166a090ff8/Source/JavaScriptCore/jsc.cpp#L1837-L1846

This is the output when I run the real JSC:

```
./jsc
>>> setRandomSeed(1337)
undefined
>>> Math.random()
0.0000012451879418673428
>>> Math.random()
0.44531374578659877
>>> Math.random()
0.8906262462736768
>>> Math.random()
0.8957252896526161
>>> Math.random()
0.4513993130918518
>>> Math.random()
0.8723069304951169
>>> Math.random()
0.18149526315413556
>>> Math.random()
0.5404527925639941
>>> Math.random()
0.467484617730592
>>> Math.random()
0.5534826395406868
```

WTF? Are you seeing what I’m seeing ðŸ‘€ ? In the first 5 attempts, the copy-pasted code returns exactly the same with the real JSC, whereas from the 6th attempt to 10th attempt its value is different.

## 3rd attempt: implement my own function into JSC

On this 3rd attempt, I was hopeless because I’m quite new to debugging low-level programming language like C/C++, prior to this I only did high-level exploiting (Java/Javascript/Python etc).

Thus, I tried putting breakpoints wherever I find it suitable, and tried calling `Math.random`

to see if it hits my breakpoint. Heck! I even tried implementing my own function which call the function `double get()`

, it looks something like this:

Then, I go to `jsc`

binary and tried running my function, and what is this ?

```
$ ./jsc
>>> getMyRandom()
duma: 1814795562 1814795562 15223638817340558
0.6901636153078781
>>>
```

It is running the `printf("duma: %i %ld %ld\n" ...);`

that I put into before ðŸ˜±. Thus, this indicates that the file `WeakRandom.h`

is a honeypot and `Math.random`

won’t hit into the `double get`

function ðŸ˜¡.

Just kidding, I think there’re some optimization, and based on some architecture it will use `WeakRandom.h`

, while on my machine it would use another place for optimizing. So now the obstacle is clear, I’ve been looking at the wrong code so I’d need to look for the real random implementation ðŸ˜³

# Found real random function

Because now I know some pattern of the random function:

- It’s using XorShift128+
- It’s implementation should look-alike with
`WeakRandom.h`

So by `Ctrl+Shift+F`

on multiple keywords, and setting breakpoints, I’ve found the `Math.random()`

is actually defined in: https://github.com/WebKit/WebKit/blob/08e4dc175b88c43782fd9d8f1f3f73166a090ff8/Source/JavaScriptCore/jit/AssemblyHelpers.cpp#L497-L547

Now I’d only need to find out why the first 5 attempts my code returns correct but from the 6th attempt it returns wrong. Thus after some error-prone, I found out that: **The shift right function jit.rshift64 is arithmetic shift right, not a logical shift right** like v8.

Ah ! So that’s why when the `m_low`

and `m_high`

is large enough (after 5 attempts), the arithmetic shift right of `m_low`

and `m_high`

will cause my code to return different with JSC.

Thus, now I only need to change my code from using `uint64_t`

to `int64_t`

:

```
#include <stdint.h>
#include <cstdio>
#include <cmath>
#include <cstring>
int64_t m_low;
int64_t m_high;
unsigned m_seed;
int64_t advance()
{
int64_t x = m_low;
int64_t y = m_high;
m_low = y;
x ^= x << 23;
x ^= x >> 17;
x ^= y ^ (y >> 26);
m_high = x;
return m_high + m_low;
}
double get()
{
int64_t value = advance() & ((1ULL << 53) - 1);
return value * (1.0 / (1ULL << 53));
}
void setSeed(unsigned seed)
{
m_seed = seed;
// A zero seed would cause an infinite series of zeroes.
if (!seed)
seed = 1;
m_low = seed;
m_high = seed;
advance();
}
// solve with input m_low and m_high
int main(int argc, char ** argv) {
setSeed(1337);
for (int i = 0; i < 10; ++i) {
double k = get();
printf("%.12lf\n", k);
}
return 0;
}
```

and run:

```
$ c++ jsc.cpp -o main
$ ./main
0.000001245188
0.445313745787
0.890626246274
0.895725289653
0.451399313092
0.872306930495
0.181495263154
0.540452792564
0.467484617731
0.553482639541
```

it would be exactly the same with the value of `Math.random`

in JSC.

# Writing z3 solver

This part is boring so please just look at my github repo

Thanks for reading !!!