Scary PDF Experiments: Launching Arkanoid in a Document

You can learn more about this hack and how it works in the talk at !! con 2020 “Playing Breakout … inside a PDF !!”

If you haven’t watched it, then try to open breakout.pdf file in Chrome.

Like many of you, I have always considered PDF to be a fairly safe format: the author creates text and graphics, and then it opens in the PDF viewer without doing anything else. A few years ago, I heard in passing about Adobe Reader vulnerabilities, but didn’t really think about how they might arise.

Initially Adobe made PDF for exactly this, but we have already found out that this is not the case at all today. IN 1,310-page PDF Specification (actually pretty clear and interesting reading) describes an insane amount of possibilities, including:

but the most interesting thing for us …

Of course, most PDF readers (except Adobe Reader) do not provide most of these capabilities. However, Chrome implements JavaScript! If you open a similar PDF file in Chrome, it will run the script. I found out by repeating the steps from See this post about creating PDF with JS

However, there is a trick here. Chrome only implements tiny a subset of the huge surface of the Acrobat JavaScript API. The API implementation in PDFium Chrome browser mainly consists of similar plugs:

FX_BOOL Document::addAnnot(IJS_Context* cc,
                           const CJS_Parameters& params,
                           CJS_Value& vRet,
                           CFX_WideString& sError) {
  // Not supported.
  return TRUE;
FX_BOOL Document::addField(IJS_Context* cc,
                           const CJS_Parameters& params,
                           CJS_Value& vRet,
                           CFX_WideString& sError) {
  // Not supported.
  return TRUE;
FX_BOOL Document::exportAsText(IJS_Context* cc,
                               const CJS_Parameters& params,
                               CJS_Value& vRet,
                               CFX_WideString& sError) {
  // Unsafe, not supported.
  return TRUE;

And I understand the concerns of developers – this Adobe JavaScript API has absolutely huge surface area… Presumably scripts can do things like connection to arbitrary databases, recognition of connected monitors, import of external resources and manipulating 3D objects

Therefore, in Chrome, such a strange situation turned out: we can perform arbitrary calculations, but we have this strange, limited API surface, in which I / O and data transfer between the program and the user are very inconvenient to implement.

It is probably possible to embed a C compiler into PDF by compiling it to JS, for example with Emscripten, but then the C compiler would have to take input from the plain text form and execute the output again in the form field.

In fact, I became interested in PDF a couple of weeks ago due to PostScript; I read Don Hopkins’ posts about NeWS – reminiscent of AJAX system, but implemented in the 80s in PostScript.

It’s funny that PDF was a reaction to PostScript, which was too expressive (being a fully functional programming language), too difficult to analyze and understand. Probably, PDF is still a step forward in this regard, but it’s still funny that it has grown with all these possibilities.

Another curious point: like any long-lived digital format (I personally have a fondness for the FAT file system), PDF itself is a kind of historical document. We can track how generations of engineers added the functions they needed at one time, while trying not to break existing ones.

I don’t quite understand why Chrome developers bother with JS support at all. They took Foxit PDF reader code; maybe Foxit had some kind of client that used JavaScript form validation?

In addition, Chrome uses the same runtime as the browser, although it does not expose browser APIs. As I understand it, this means you can use ES6 features such as arrow functions and proxies.


So what can we do with the API surface provided to us by Chrome?

By the way, I must apologize for the imperfect collision detection and inconsistent game speed. I ripped most of the game off tutorial

The first user-accessible I / O points I could find in the Chrome browser PDF API implementation were in Field.cpp

We can not change fill color text box at runtime, but we can change the rectangle of its borders and style borders… We can not read the exact position of the mouse, however, we can bind the mouse-enter and mouse-leave scripts to the fields when creating a PDF. Also, you cannot add fields at runtime: you have to limit yourself to what we put in the PDF at the time of creation. Curious why the developers chose these particular methods? It looks like some kind of stereotype about programming in old-school FORTRAN: you must declare all the variables in advance so that the compiler can statically allocate memory for them.

So the PDF file is generated script, pre-creating a set of text fields, including game elements:

  • Racket
  • Bricks
  • Ball
  • Glasses
  • Of life

But for the game to work properly, we will also add some hacks.

First, we create a thin, long “strip” of text box for each column in the bottom half of the screen. The bars receive a mouse-enter event when the player moves the mouse along the X axis, so the paddle can move as the mouse moves.

Secondly, we create a field called “whole” that covers the entire upper half of the screen. Chrome doesn’t expect the PDF display to change, so moving the fields in JS will result in some pretty strong artifacts. This “whole” field solves this problem – we enable / disable it during frame rendering. This trick makes Chrome clean up the artifacts.

Also, when you move the field, it seems to reset it external display stream… The beautiful appearance you have chosen from arbitrary PDF-graphics “flies” and is replaced by a simple filled rectangle with a border. Therefore, my game uses simplified dictionary of appearance characteristics… In the most extreme case, the fill color specified there remains unchanged when the widget is moved.

Useful Resources


Order a server and start working right away! Creature VDS of any configuration within a minute, including servers for storing large amounts of data up to 4000 GB. Epic 🙂

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *