Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
pl-semiotics committed Jan 2, 2021
0 parents commit 387a4f3
Show file tree
Hide file tree
Showing 15 changed files with 1,840 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*~
\#*\#
.\#*
result
build
340 changes: 340 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
CC ?= arm-remarkable-linux-gnueabi-gcc
LD ?= arm-remarkable-linux-gnueabi-ld
AR ?= arm-remarkable-linux-gnueabi-ar
OBJCOPY ?= arm-remarkable-linux-gnueabi-objcopy

.PHONY: all clean

all: build/libqsgepaper_extract_info
all: build/payload.bin
all: build/libqsgepaper-snoop.so build/libqsgepaper-snoop-standalone.a
clean:
rm -rf build

build:
mkdir -p build
build/%.o: %.c | build
$(CC) $(CFLAGS) -c $< -o $@ $(LDFLAGS)
build/%.o: %.s | build
$(CC) $(ASFLAGS) -c $< -o $@ $(LDFLAGS)
build/%.a: | build
$(AR) cr $@ $^
build/%.so: | build
$(CC) -shared -z defs -o $@ $^ $(LDFLAGS)
build/%.xz: build/% | build
xz -f -k $<

build/extract_info.o: cached_info.h
build/libqsgepaper_extract_info: build/extract_info.o | build
$(CC) $< -o $@ -Wl,--start-group -lunicorn -larm-softmmu -Wl,--end-group -lpthread
build/libqsgepaper_extract_info.bin: build/libqsgepaper_extract_info.xz
$(OBJCOPY) -I binary -O elf32-littlearm -B arm $< $@

build/payload-a.o: private ASFLAGS=-ffreestanding
build/payload-c.o: private CFLAGS=-ffreestanding -fno-stack-protector -fPIE -fno-plt -mpic-data-is-text-relative
build/payload.o: build/payload-a.o build/payload-c.o payload.ld | build
$(LD) -Tpayload.ld -s build/payload-c.o build/payload-a.o -o $@
build/payload.bin: build/payload.o
$(OBJCOPY) -O binary -j .binary $< $@

build/inject.o: cached_info.h
build/inject-standalone.o: build/inject.o inject-standalone.ld build/libqsgepaper_extract_info.bin build/payload.o | build
$(LD) -Tinject-standalone.ld -i -o $@ build/inject.o

build/libqsgepaper-snoop-standalone.a: build/inject-standalone.o

build/libqsgepaper-snoop.so: build/inject.o
build/libqsgepaper-snoop.so: private override LDFLAGS += -lcrypto -llzma
75 changes: 75 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Introduction

This library provides damage tracking information version 2 of the
[reMarkable tablet](https://remarkable.com). Unlike the reMarkable 1,
the reMarkable 2 does not use the integrated e-paper display
controller on the SoC used for the system, but instead drives the
display directly from software. This makes it impossible to extract
damage information from the (nonexistent) hardware driver, which was
the approach taken on the rM1.

It is therefore necessary to inject code into the process that _is_
controlling the framebuffer in order to provide notification of damage
update. In all known production software, the display is controlled by
executables using the proprietary static library `libqsgepaper.a`. The
purpose of this project, therefore, is to inject code into a
`libqsgepaper.a`-based binary that exports the software framebuffer to
which content is drawn, as well as damage notifications whenever an
update is sent to the display.

In order to work on as wide a range of driving binaries as possible,
this library avoids using hardcoded addresses or similar techniques.
Instead, it uses data taken from Qt metaobjects that are part of the
interface of `libqsgepaper.a` in order to locate relevant functions
and data.

It is relatively easy to find the Qt metaobject in the data section of
the binary@riving the display. Unfortunately, this does not give
direct access to the virtual framebuffer, or to the function used to
notify the display that an update is present. In order to find these
things, the [Unicorn Engine](https://www.unicorn-engine.org/) emulator
is used to emulate the Qt static meta-call function (when given
appropriate parameters). This uses the fact that `libqsgepaper.a`
passes the framebuffer address to `QImage::fill` in the (inlined)
`clearScreen` function, the fact that `sendUpdate` is not inlined into
`QObject::metacall`, and little else.

Once the appropriate addresses have been found, a writable and
executable page is `mmap`d via ptrace()ing the target process, and a
payload built from [payload-c.c](./payload-c.c) and
[payload-a.s](./payload-a.s) is injected. The preamble of the
`sendUpdate` function in the original process is overwritten to
redirect to a portion of the payload which saves the information
damaged, runs the original `sendUpdate`,and then exports the damage
information over a Unix domain socket. The payload also includes
initialization code that replaces the private anonymous mapping of the
framebuffer with a shared mapping backed by an in-memory file from
`memfd_create` and connects to a Unix domain socket opened by the
process requesting the information.

The necessary addresses appear to in practice remain stable over
multiple executions of the same binary, as the `EPFramebuffer` class
which is being hooked into is a singleton and address-space layout
randomization is not used on the reMarkable. Therefore, in order to
improve startup time and minimize resource usage, the Unicorn-based
emulation analysis is not run every time, but rather stored in a
cache.

# Building

The supported way to build this is via the
[Nix](https://nixos.org/nix) package manager, through the
[nix-remarkable](https://github.com/peter-sa/nix-remarkable)
expressions. To build just this project via `nix build` from this
repo, download it into the `pkgs/` directory of `nix-remarkable`.

For other systems, the [Makefile](./Makefile) provides the necessary
commands. A suitable build of the Unicorn engine (static for the
standalone static library) is required.

Prebuilt binaries are available in the [Releases
tab](https://github.com/pl-semiotics/libqsgepaper-snoop/releases).

# Usage

See [libqsgepaper-snoop.h](./libqsgepaper-snoop.h).
31 changes: 31 additions & 0 deletions cached_info.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef CACHED_INFO_H_
#define CACHED_INFO_H_

#include <sys/types.h>

#include "private.h"

#define HEADER_MAGIC "libqsgepaper-snoop cached info v1\n"
#define SIZE_BYTE sizeof(HEADER_MAGIC)
#define STATE_BEGIN sizeof(HEADER_MAGIC) + sizeof(uint);

struct check_bit {
uint addr;
uint eval;
};
struct cached_state {
uint qimage_bits_addr_addr;
uint mmap_addr_addr;
uint fb_addr;
uint sendUpdate_addr;
/* the user code could use this directly, but really it's used for
* checking that nothing's changed. however, since we might have
* injected already, it doesn't go in cbits below.
*/
uint su_preamble[N_PREAMBLE_INSTRS];
/* for verification that this is the correct executable only */
uint ncbits;
struct check_bit cbits[];
};

#endif /* CACHED_INFO_H_ */
1 change: 1 addition & 0 deletions default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(import ../.. {}).rmPkgs.libqsgepaper-snoop
19 changes: 19 additions & 0 deletions derivation.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{ stdenv, lib, unicorn }:

stdenv.mkDerivation {
pname = "libqsgepaper-snoop";
version = "0.0.1";
src = lib.cleanSource ./.;
buildInputs = [ unicorn ];
installPhase = ''
mkdir -p $out/lib
cp build/libqsgepaper-snoop.so $out/lib
cp build/libqsgepaper-snoop-standalone.a $out/lib
mkdir -p $out/share/libqsgepaper-snoop
cp build/payload.bin $out/share/libqsgepaper-snoop
mkdir -p $out/libexec/libqsgepaper-snoop
cp build/libqsgepaper_extract_info $out/libexec/libqsgepaper-snoop
mkdir -p $out/include
cp libqsgepaper-snoop.h $out/include
'';
}
Loading

0 comments on commit 387a4f3

Please sign in to comment.